feat(flows): frontend for the memory node — palette, config panel, i18n ×14 (#5228)

This commit is contained in:
Cyrus Gray
2026-07-29 05:58:24 +03:00
committed by GitHub
parent 6c665f4f6f
commit 39fe7dbb33
22 changed files with 919 additions and 8 deletions
@@ -0,0 +1,118 @@
/**
* Behavior tests for the `memory` node config form (issue #5226). Covers the
* per-operation progressive disclosure (`recall`/`search` vs `flavour` vs
* `remember`/`forget`) and the hard UI rule mirroring the engine invariant:
* a `remember`/`forget` node's `scope` control must never offer `user`.
* `useT()` falls back to the bundled English map with no provider mounted
* (same convention as the sibling `nodeConfigForms.test.tsx`).
*/
import { fireEvent, render, screen, within } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { MemoryForm } from '../memoryFields';
function renderMemoryForm(config: Record<string, unknown> = {}) {
const onChange = vi.fn();
render(<MemoryForm config={config} onChange={onChange} />);
return { onChange };
}
describe('MemoryForm', () => {
it('defaults to recall: shows scope (including user) and query, no key/value/flavour', () => {
renderMemoryForm();
const scope = screen.getByTestId('node-config-memory-scope');
const options = within(scope).getAllByRole('option');
expect(options.map(o => o.getAttribute('value'))).toEqual(['user', 'flow', 'flows']);
expect(screen.getByTestId('node-config-memory-query')).toBeInTheDocument();
expect(screen.queryByTestId('node-config-memory-flavour')).not.toBeInTheDocument();
expect(screen.queryByTestId('node-config-memory-key')).not.toBeInTheDocument();
expect(screen.queryByTestId('node-config-memory-value')).not.toBeInTheDocument();
});
it('search behaves like recall: scope includes user, and query is shown', () => {
renderMemoryForm({ operation: 'search' });
const scope = screen.getByTestId('node-config-memory-scope');
const options = within(scope).getAllByRole('option');
expect(options.map(o => o.getAttribute('value'))).toContain('user');
expect(screen.getByTestId('node-config-memory-query')).toBeInTheDocument();
});
it('flavour shows only the flavour slug field, no scope or query', () => {
renderMemoryForm({ operation: 'flavour' });
expect(screen.getByTestId('node-config-memory-flavour')).toBeInTheDocument();
expect(screen.queryByTestId('node-config-memory-scope')).not.toBeInTheDocument();
expect(screen.queryByTestId('node-config-memory-query')).not.toBeInTheDocument();
});
it('remember hides the user scope option (only flow) and shows key + value', () => {
renderMemoryForm({ operation: 'remember', scope: 'flow' });
const scope = screen.getByTestId('node-config-memory-scope');
const options = within(scope).getAllByRole('option');
expect(options.map(o => o.getAttribute('value'))).toEqual(['flow']);
expect(within(scope).queryByRole('option', { name: /read-only/i })).not.toBeInTheDocument();
expect(screen.getByTestId('node-config-memory-key')).toBeInTheDocument();
expect(screen.getByTestId('node-config-memory-value')).toBeInTheDocument();
// Not a read/search operation, so no query field.
expect(screen.queryByTestId('node-config-memory-query')).not.toBeInTheDocument();
});
it('forget hides the user scope option and shows key but not value', () => {
renderMemoryForm({ operation: 'forget', scope: 'flow' });
const scope = screen.getByTestId('node-config-memory-scope');
const options = within(scope).getAllByRole('option');
expect(options.map(o => o.getAttribute('value'))).toEqual(['flow']);
expect(screen.getByTestId('node-config-memory-key')).toBeInTheDocument();
expect(screen.queryByTestId('node-config-memory-value')).not.toBeInTheDocument();
});
it('clamps scope to flow when switching from recall(user) to remember', () => {
const { onChange } = renderMemoryForm({ operation: 'recall', scope: 'user' });
fireEvent.change(screen.getByTestId('node-config-memory-operation'), {
target: { value: 'remember' },
});
expect(onChange).toHaveBeenLastCalledWith({ operation: 'remember', scope: 'flow' });
});
it('self-heals a pre-existing invalid remember+user config on mount', () => {
const { onChange } = renderMemoryForm({ operation: 'remember', scope: 'user' });
expect(onChange).toHaveBeenCalledWith({ scope: 'flow' });
});
it('emits a query patch as the query is typed for recall', () => {
const { onChange } = renderMemoryForm({ operation: 'recall' });
fireEvent.change(screen.getByTestId('node-config-memory-query'), {
target: { value: '=item.title' },
});
expect(onChange).toHaveBeenLastCalledWith({ query: '=item.title' });
});
it('emits limit as a number as it is typed', () => {
const { onChange } = renderMemoryForm({ operation: 'recall' });
fireEvent.change(screen.getByTestId('node-config-memory-limit'), { target: { value: '5' } });
expect(onChange).toHaveBeenLastCalledWith({ limit: 5 });
});
it('emits undefined for limit when the field is cleared back to empty', () => {
// Start from an already-populated `limit` so clearing it is a genuine
// DOM value change (a fresh controlled input already renders blank).
const { onChange } = renderMemoryForm({ operation: 'recall', limit: 5 });
fireEvent.change(screen.getByTestId('node-config-memory-limit'), { target: { value: '' } });
expect(onChange).toHaveBeenLastCalledWith({ limit: undefined });
});
it('emits min_score as a number as it is typed', () => {
const { onChange } = renderMemoryForm({ operation: 'recall' });
fireEvent.change(screen.getByTestId('node-config-memory-min-score'), {
target: { value: '0.5' },
});
expect(onChange).toHaveBeenLastCalledWith({ min_score: 0.5 });
});
it('emits undefined for min_score when the field is cleared back to empty', () => {
// Start from an already-populated `min_score` so clearing it is a genuine
// DOM value change (a fresh controlled input already renders blank).
const { onChange } = renderMemoryForm({ operation: 'recall', min_score: 0.5 });
fireEvent.change(screen.getByTestId('node-config-memory-min-score'), { target: { value: '' } });
expect(onChange).toHaveBeenLastCalledWith({ min_score: undefined });
});
});
@@ -0,0 +1,235 @@
/**
* `memory` node config form (issue #5226 — 13th tinyflows `NodeKind`).
* Declarative, in-graph memory access: `recall`/`search`/`flavour`/`people`
* read; `remember`/`forget` write. Spec: `my_docs/memory_access_in_workflows/08-memory-node.md`.
*
* Field keys mirror what the engine actually reads at runtime (per the
* design doc's `MemoryProvider` trait + node config table):
* - `operation` — always. `recall` · `search` · `flavour` · `people` ·
* `remember` · `forget`.
* - `scope` — for `recall`/`search`/`remember`/`forget`. `user` and
* `flows` are read-only scopes (only offered for the read operations);
* `remember`/`forget` may only ever target `flow`.
* - `query` — `=`-bindable, for `recall`/`search` (and optionally
* `people`, whose `people(query: Option<&str>)` signature takes one too).
* - `flavour` — a plain slug (not an expression), for `flavour`.
* - `key` — `=`-bindable, for `remember`/`forget`.
* - `value` — `=`-bindable, for `remember` only.
* - `limit` / `min_score` — optional numeric caps on the read operations.
*
* Progressive disclosure mirrors the other multi-field forms in
* `nodeConfigForms.tsx` (e.g. `TriggerForm`'s `trigger_kind` switch): only
* the fields relevant to the selected `operation` render.
*/
import createDebug from 'debug';
import { useEffect } from 'react';
import { useT } from '../../../../lib/i18n/I18nContext';
import {
configNumber,
configString,
ExpressionField,
NumberField,
SelectField,
} from './nodeConfigFields';
import type { UpstreamExpressionOption } from './upstreamOptions';
const log = createDebug('app:flows:nodeConfig:memory');
const MEMORY_OPERATIONS = ['recall', 'search', 'flavour', 'people', 'remember', 'forget'] as const;
type MemoryOperation = (typeof MEMORY_OPERATIONS)[number];
/**
* The seven persona facets the `flavour` operation's `memory_flavour` engine
* reader (`src/openhuman/memory/tools/flavour.rs`) accepts — any other slug
* returns "Unknown flavour". Rendered as a dropdown rather than free text so
* an author can't type an invalid slug (e.g. `email-tone`) in the first place.
*/
const MEMORY_FLAVOURS = [
'communication',
'coding_style',
'stack',
'workflow',
'environment',
'directives',
'anti_preferences',
] as const;
/** Operations that read/write at a `scope`. `flavour` and `people` don't take one. */
const SCOPED_OPERATIONS = new Set<MemoryOperation>(['recall', 'search', 'remember', 'forget']);
/**
* `remember`/`forget` are flow-only writes — the engine rejects a
* `user`-scoped write at `validate_all` time (the hard security invariant
* from `05-security.md`/`08-memory-node.md`). The UI must never let an
* author construct that pair, so these operations get a `scope` control
* offering only `flow`, never `user` (or the other read-only scope, `flows`).
*/
const WRITE_OPERATIONS = new Set<MemoryOperation>(['remember', 'forget']);
/** Operations that take a free-text `query`. */
const QUERY_OPERATIONS = new Set<MemoryOperation>(['recall', 'search', 'people']);
/** Operations whose result set can be capped/thresholded. */
const RESULT_OPERATIONS = new Set<MemoryOperation>(['recall', 'search', 'people']);
/**
* Deliberately narrower than `NodeConfigFormProps` (`nodeConfigForms.tsx`) —
* this form needs no `connections` (memory nodes don't use the credential
* picker), so it isn't imported here just to keep the shape identical. A
* component typed against this subset is still assignable into
* `NODE_CONFIG_FORMS`'s `NodeConfigForm` slot (the fuller prop type has
* every property this one requires).
*/
export interface MemoryFormProps {
config: Record<string, unknown>;
onChange: (patch: Record<string, unknown>) => void;
upstreamOptions?: UpstreamExpressionOption[];
}
export function MemoryForm({ config, onChange, upstreamOptions }: MemoryFormProps) {
const { t } = useT();
const operation = (configString(config, 'operation') || 'recall') as MemoryOperation;
const scope = configString(config, 'scope');
const isWrite = WRITE_OPERATIONS.has(operation);
// Self-heal: if this node arrives with an already-invalid `remember`/
// `forget` + non-`flow` scope (a raw-JSON edit, an older draft, a
// workflow-builder proposal), correct it as soon as the form mounts/updates
// rather than only guarding the operation-change handler below — the
// invariant must hold no matter how the bad pair got here.
useEffect(() => {
if (isWrite && scope !== 'flow') {
log(
'self-heal: clamping scope=%s to flow for write operation=%s',
scope || '(empty)',
operation
);
onChange({ scope: 'flow' });
}
}, [isWrite, scope, operation, onChange]);
const handleOperationChange = (value: string) => {
const next = value as MemoryOperation;
const patch: Record<string, unknown> = { operation: next };
// Hard UI rule mirroring the engine invariant: switching straight into a
// write operation while a read-only scope (`user`/`flows`) is selected
// must clamp to `flow` in the same patch, not rely on the effect above
// (which would otherwise author one extra, momentarily-invalid patch).
if (WRITE_OPERATIONS.has(next) && scope !== 'flow') {
patch.scope = 'flow';
}
log('operation change: %s -> %s (scope patch=%s)', operation, next, patch.scope ?? 'none');
onChange(patch);
};
const scopeOptions = isWrite
? [{ value: 'flow', label: t('flows.nodeConfig.memory.scope_flow') }]
: [
{ value: 'user', label: t('flows.nodeConfig.memory.scope_user') },
{ value: 'flow', label: t('flows.nodeConfig.memory.scope_flow') },
{ value: 'flows', label: t('flows.nodeConfig.memory.scope_flows') },
];
return (
<div className="space-y-3">
<SelectField
label={t('flows.nodeConfig.memory.operationLabel')}
value={operation}
onChange={handleOperationChange}
testId="node-config-memory-operation"
options={MEMORY_OPERATIONS.map(op => ({
value: op,
label: t(`flows.nodeConfig.memory.operation_${op}`),
}))}
/>
{SCOPED_OPERATIONS.has(operation) && (
<SelectField
label={t('flows.nodeConfig.memory.scopeLabel')}
hint={
isWrite
? t('flows.nodeConfig.memory.scopeWriteHint')
: t('flows.nodeConfig.memory.scopeHint')
}
value={isWrite ? 'flow' : scope || 'user'}
onChange={v => onChange({ scope: v })}
testId="node-config-memory-scope"
options={scopeOptions}
/>
)}
{QUERY_OPERATIONS.has(operation) && (
<ExpressionField
label={t('flows.nodeConfig.memory.queryLabel')}
hint={operation === 'people' ? t('flows.nodeConfig.memory.queryOptionalHint') : undefined}
value={configString(config, 'query')}
onChange={v => onChange({ query: v })}
placeholder="=item.title"
upstreamOptions={upstreamOptions}
testId="node-config-memory-query"
/>
)}
{operation === 'flavour' && (
<SelectField
label={t('flows.nodeConfig.memory.flavourLabel')}
hint={t('flows.nodeConfig.memory.flavourHint')}
value={configString(config, 'flavour') || MEMORY_FLAVOURS[0]}
onChange={v => onChange({ flavour: v })}
testId="node-config-memory-flavour"
options={MEMORY_FLAVOURS.map(facet => ({
value: facet,
label: t(`flows.nodeConfig.memory.flavour_${facet}`),
}))}
/>
)}
{(operation === 'remember' || operation === 'forget') && (
<ExpressionField
label={t('flows.nodeConfig.memory.keyLabel')}
value={configString(config, 'key')}
onChange={v => onChange({ key: v })}
placeholder="=item.id"
upstreamOptions={upstreamOptions}
testId="node-config-memory-key"
/>
)}
{operation === 'remember' && (
<ExpressionField
label={t('flows.nodeConfig.memory.valueLabel')}
value={configString(config, 'value')}
onChange={v => onChange({ value: v })}
placeholder="=item"
upstreamOptions={upstreamOptions}
testId="node-config-memory-value"
/>
)}
{RESULT_OPERATIONS.has(operation) && (
<div className="grid grid-cols-2 gap-3">
<NumberField
label={t('flows.nodeConfig.memory.limitLabel')}
hint={t('flows.nodeConfig.memory.limitHint')}
value={configNumber(config, 'limit')}
onChange={v => onChange({ limit: v })}
min={1}
step={1}
testId="node-config-memory-limit"
/>
<NumberField
label={t('flows.nodeConfig.memory.minScoreLabel')}
hint={t('flows.nodeConfig.memory.minScoreHint')}
value={configNumber(config, 'min_score')}
onChange={v => onChange({ min_score: v })}
min={0}
max={1}
step={0.05}
testId="node-config-memory-min-score"
/>
</div>
)}
</div>
);
}
@@ -36,6 +36,12 @@ export function configString(config: Record<string, unknown>, key: string): stri
return typeof value === 'string' ? value : '';
}
/** Read a finite numeric field off a free-form config object, defaulting to `undefined`. */
export function configNumber(config: Record<string, unknown>, key: string): number | undefined {
const value = config[key];
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
}
/** Read a `Record<string,string>` map off config (e.g. HTTP headers / transform set). */
export function configStringMap(
config: Record<string, unknown>,
@@ -167,6 +173,57 @@ export function SelectField({ label, hint, value, onChange, options, testId }: S
);
}
interface NumberFieldProps {
label: string;
hint?: string;
value: number | undefined;
onChange: (value: number | undefined) => void;
placeholder?: string;
min?: number;
max?: number;
step?: number;
testId?: string;
}
/**
* A plain numeric input (e.g. a memory node's `limit` / `min_score`). Unlike
* {@link TextField} the empty string round-trips to `undefined` rather than
* `''`, so an unset optional numeric config key stays genuinely absent
* instead of becoming a stray `""` in the saved graph.
*/
export function NumberField({
label,
hint,
value,
onChange,
placeholder,
min,
max,
step,
testId,
}: NumberFieldProps) {
const id = useId();
return (
<Field label={label} hint={hint} htmlFor={id}>
<input
id={id}
type="number"
className={INPUT_CLASS}
value={value ?? ''}
placeholder={placeholder}
min={min}
max={max}
step={step}
data-testid={testId}
onChange={e => {
const raw = e.target.value;
onChange(raw === '' ? undefined : Number(raw));
}}
/>
</Field>
);
}
/**
* Compact "insert an upstream value" dropdown (Feature: `nodes` scope picker).
* Always renders with the empty placeholder selected; picking an option calls
@@ -14,6 +14,8 @@
* - `switch` → `expression` (=-expr, precedence) / `field` (fallback)
* - `transform` → `set` (key → =-expression map)
* - `trigger` → `trigger_kind` + kind-specific (`schedule`, `toolkit`/`trigger_slug`)
* - `memory` → `operation` + operation-specific (`scope`/`query`/`flavour`/`key`/`value`/
* `limit`/`min_score`), see `memoryFields.tsx`
*/
import createDebug from 'debug';
import { useEffect, useState } from 'react';
@@ -30,6 +32,7 @@ import {
ComposioTriggerField,
fetchActionSchema,
} from './composioFields';
import { MemoryForm } from './memoryFields';
import { NATIVE_TOOL_PREFIX, NativeToolField } from './nativeToolFields';
import {
configString,
@@ -492,4 +495,5 @@ export const NODE_CONFIG_FORMS: Partial<Record<NodeKind, NodeConfigForm>> = {
switch: SwitchForm,
transform: TransformForm,
code: CodeForm,
memory: MemoryForm,
};
+13 -5
View File
@@ -1,5 +1,5 @@
/**
* Per-kind visual metadata for the 12 tinyflows `NodeKind`s, shared by the
* Per-kind visual metadata for the 13 tinyflows `NodeKind`s, shared by the
* canvas node renderer (`FlowNodeComponent`) and the editable canvas's node
* palette (`NodePalette`). Kept dependency-free (no React) so both a rendered
* `<Handle>`-bearing card and a plain palette button can pull the same
@@ -8,7 +8,7 @@
* Colors cycle through the four CSS-variable-backed semantic ramps
* (primary/sage/amber/coral) that support Tailwind's `/opacity` modifiers in
* this codebase (see `tailwind.config.js`) so light/dark theming comes for
* free; with 12 kinds and 4 ramps some kinds share a color family — the emoji
* free; with 13 kinds and 4 ramps some kinds share a color family — the emoji
* + name remain the primary distinguishers.
*/
import type { NodeKind } from './types';
@@ -29,9 +29,12 @@ interface NodeKindMeta {
}
/**
* The 12 `NodeKind`s in the order they should appear in the palette. Trigger
* The 13 `NodeKind`s in the order they should appear in the palette. Trigger
* leads (every graph needs exactly one); the rest follow the logical grouping
* of the `tinyflows::model::NodeKind` enum.
* of the `tinyflows::model::NodeKind` enum. `memory` (issue #5226) is
* appended last — the design doc (`08-memory-node.md`) sequences it as the
* 13th kind deliberately, so it trails `sub_workflow` here too rather than
* being interleaved with the other `actions`-group kinds.
*/
const NODE_KINDS: NodeKind[] = [
'trigger',
@@ -46,6 +49,7 @@ const NODE_KINDS: NodeKind[] = [
'transform',
'output_parser',
'sub_workflow',
'memory',
];
/** Per-kind emoji + border/chip color + palette group. See the module doc. */
@@ -56,6 +60,10 @@ const NODE_KIND_META: Record<NodeKind, NodeKindMeta> = {
http_request: { emoji: '🌐', color: 'coral', group: 'actions' },
code: { emoji: '📝', color: 'sage', group: 'actions' },
sub_workflow: { emoji: '🧩', color: 'coral', group: 'actions' },
// Declarative in-graph memory access (recall/search/flavour/people/
// remember/forget) — an "actions" node like `tool_call`/`http_request`
// (it reads/writes state), not `logic` (it doesn't itself branch/reshape).
memory: { emoji: '🧠', color: 'sage', group: 'actions' },
condition: { emoji: '🔀', color: 'primary', group: 'logic' },
switch: { emoji: '🔁', color: 'amber', group: 'logic' },
merge: { emoji: '🔗', color: 'coral', group: 'logic' },
@@ -128,7 +136,7 @@ export const PALETTE_ENTRIES_BY_GROUP: Record<NodeGroup, PaletteEntry[]> = {
/**
* Fallback for any `kind` outside {@link NODE_KIND_META} — a saved graph is
* `unknown` on the wire (cast in `FlowCanvasPage.tsx`), so a future 13th
* `unknown` on the wire (cast in `FlowCanvasPage.tsx`), so a future 14th
* tinyflows kind, or any other value the backend ever emits, can reach the
* renderer at runtime even though TypeScript can't see it. Lookups fall back
* here so an unrecognized kind renders as a plain neutral node instead of
+10
View File
@@ -62,4 +62,14 @@ describe('describeNode', () => {
it('returns empty string for an unknown kind', () => {
expect(describeNode('time_travel', {})).toBe('');
});
it('gives memory search its own summary, distinct from recall', () => {
expect(describeNode('memory', { operation: 'search', scope: 'user' })).toBe(
'Searches memory (user)'
);
expect(describeNode('memory', { operation: 'recall', scope: 'user' })).toBe(
'Recalls memory (user)'
);
expect(describeNode('memory', { operation: 'search' })).toBe('Searches memory');
});
});
+15
View File
@@ -89,6 +89,21 @@ export function describeNode(
return 'Parses the previous output';
case 'sub_workflow':
return 'Runs a nested workflow';
case 'memory': {
const operation = str(config, 'operation') || 'recall';
const scope = str(config, 'scope');
if (operation === 'flavour') {
const flavour = str(config, 'flavour');
return flavour ? `Reads the "${flavour}" flavour` : 'Reads a memory flavour';
}
if (operation === 'people') return 'Looks up people memory';
if (operation === 'remember') return 'Remembers a value in this workflow';
if (operation === 'forget') return 'Forgets a value from this workflow';
const scoped = scope ? ` (${scope})` : '';
if (operation === 'search') return `Searches memory${scoped}`;
// recall
return `Recalls memory${scoped}`;
}
default:
return '';
}
+13 -2
View File
@@ -36,8 +36,18 @@ export interface Position {
}
/**
* The 12 node kinds `tinyflows` currently defines (`tinyflows::model::NodeKind`).
* The 13 node kinds `tinyflows` currently defines (`tinyflows::model::NodeKind`).
* Wire values are `snake_case` (`#[serde(rename_all = "snake_case")]`).
*
* `memory` (issue #5226) is the 13th kind — declarative, in-graph memory
* access (`recall`/`search`/`flavour`/`people`/`remember`/`forget`, see
* `my_docs/memory_access_in_workflows/08-memory-node.md`). Its config is
* still the same free-form `WorkflowNode.config` bag as every other kind
* (see {@link WorkflowNode}); there is no dedicated TS config interface
* because no other kind has one either — the node-config form
* (`nodeConfig/memoryFields.tsx`) reads/writes the known keys
* (`operation`, `scope`, `query`, `flavour`, `key`, `value`, `limit`,
* `min_score`) directly off that bag, same as `condition`/`switch`/etc.
*/
export type NodeKind =
| 'trigger'
@@ -51,7 +61,8 @@ export type NodeKind =
| 'split_out'
| 'transform'
| 'output_parser'
| 'sub_workflow';
| 'sub_workflow'
| 'memory';
/**
* A named connection point on a node. Mirrors `tinyflows::model::Port`.
+32
View File
@@ -4092,6 +4092,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': 'التحويل',
'flows.nodeKind.output_parser': 'محلل المخرجات',
'flows.nodeKind.sub_workflow': 'سير عمل فرعي',
'flows.nodeKind.memory': 'الذاكرة',
'flows.palette.title': 'العقد',
'flows.palette.addNode': 'إضافة عقدة {kind}',
'flows.editor.save': 'حفظ',
@@ -4226,6 +4227,37 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'الكود المصدري',
'flows.nodeConfig.memory.operationLabel': 'العملية',
'flows.nodeConfig.memory.operation_recall': 'استرجاع',
'flows.nodeConfig.memory.operation_search': 'بحث',
'flows.nodeConfig.memory.operation_flavour': 'النمط',
'flows.nodeConfig.memory.operation_people': 'الأشخاص',
'flows.nodeConfig.memory.operation_remember': 'تذكر',
'flows.nodeConfig.memory.operation_forget': 'نسيان',
'flows.nodeConfig.memory.scopeLabel': 'النطاق',
'flows.nodeConfig.memory.scopeHint': 'مكان البحث عن هذه الذاكرة.',
'flows.nodeConfig.memory.scopeWriteHint':
'يُسمح بالكتابة داخل سير العمل هذا فقط، وليس أبدًا في ذاكرتك الشخصية.',
'flows.nodeConfig.memory.scope_user': 'أنت (للقراءة فقط)',
'flows.nodeConfig.memory.scope_flow': 'سير العمل هذا',
'flows.nodeConfig.memory.scope_flows': 'كل سير العمل (للقراءة فقط)',
'flows.nodeConfig.memory.queryLabel': 'الاستعلام',
'flows.nodeConfig.memory.queryOptionalHint': 'اختياري: يضيّق نطاق البحث عن الأشخاص.',
'flows.nodeConfig.memory.flavourLabel': 'النمط',
'flows.nodeConfig.memory.flavourHint': 'أي جانب من ملف الشخصية يجب قراءته، مثل communication.',
'flows.nodeConfig.memory.flavour_communication': 'التواصل',
'flows.nodeConfig.memory.flavour_coding_style': 'أسلوب البرمجة',
'flows.nodeConfig.memory.flavour_stack': 'التقنيات المستخدمة',
'flows.nodeConfig.memory.flavour_workflow': 'سير العمل',
'flows.nodeConfig.memory.flavour_environment': 'البيئة',
'flows.nodeConfig.memory.flavour_directives': 'التوجيهات',
'flows.nodeConfig.memory.flavour_anti_preferences': 'التفضيلات السلبية',
'flows.nodeConfig.memory.keyLabel': 'المفتاح',
'flows.nodeConfig.memory.valueLabel': 'القيمة',
'flows.nodeConfig.memory.limitLabel': 'الحد الأقصى',
'flows.nodeConfig.memory.limitHint': 'أقصى عدد من النتائج.',
'flows.nodeConfig.memory.minScoreLabel': 'الحد الأدنى للنتيجة',
'flows.nodeConfig.memory.minScoreHint': 'عتبة الصلة من 0 إلى 1.',
'flows.chooser.title': 'إنشاء سير عمل',
'flows.chooser.subtitle': 'اختر كيف تريد أن تبدأ.',
+32
View File
@@ -4194,6 +4194,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': 'রূপান্তর',
'flows.nodeKind.output_parser': 'আউটপুট পার্সার',
'flows.nodeKind.sub_workflow': 'সাব-ওয়ার্কফ্লো',
'flows.nodeKind.memory': 'মেমরি',
'flows.palette.title': 'নোড',
'flows.palette.addNode': '{kind} নোড যোগ করুন',
'flows.editor.save': 'সংরক্ষণ করুন',
@@ -4333,6 +4334,37 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'সোর্স কোড',
'flows.nodeConfig.memory.operationLabel': 'অপারেশন',
'flows.nodeConfig.memory.operation_recall': 'পুনরুদ্ধার',
'flows.nodeConfig.memory.operation_search': 'অনুসন্ধান',
'flows.nodeConfig.memory.operation_flavour': 'ধরন',
'flows.nodeConfig.memory.operation_people': 'ব্যক্তি',
'flows.nodeConfig.memory.operation_remember': 'মনে রাখুন',
'flows.nodeConfig.memory.operation_forget': 'ভুলে যান',
'flows.nodeConfig.memory.scopeLabel': 'পরিধি',
'flows.nodeConfig.memory.scopeHint': 'এই মেমরিটি কোথায় খুঁজতে হবে।',
'flows.nodeConfig.memory.scopeWriteHint':
'শুধু এই ওয়ার্কফ্লোর মধ্যেই লেখার অনুমতি আছে, আপনার ব্যক্তিগত মেমরিতে কখনও নয়।',
'flows.nodeConfig.memory.scope_user': 'আপনি (শুধু পড়ার জন্য)',
'flows.nodeConfig.memory.scope_flow': 'এই ওয়ার্কফ্লো',
'flows.nodeConfig.memory.scope_flows': 'সব ওয়ার্কফ্লো (শুধু পড়ার জন্য)',
'flows.nodeConfig.memory.queryLabel': 'কোয়েরি',
'flows.nodeConfig.memory.queryOptionalHint': 'ঐচ্ছিক: মানুষ খোঁজার পরিধি সংকুচিত করে।',
'flows.nodeConfig.memory.flavourLabel': 'ধরন',
'flows.nodeConfig.memory.flavourHint': 'কোন ব্যক্তিত্ব দিক পড়তে হবে, যেমন communication।',
'flows.nodeConfig.memory.flavour_communication': 'যোগাযোগ',
'flows.nodeConfig.memory.flavour_coding_style': 'কোডিং শৈলী',
'flows.nodeConfig.memory.flavour_stack': 'প্রযুক্তি স্ট্যাক',
'flows.nodeConfig.memory.flavour_workflow': 'কর্মপ্রবাহ',
'flows.nodeConfig.memory.flavour_environment': 'পরিবেশ',
'flows.nodeConfig.memory.flavour_directives': 'নির্দেশনা',
'flows.nodeConfig.memory.flavour_anti_preferences': 'অপছন্দসমূহ',
'flows.nodeConfig.memory.keyLabel': 'কী',
'flows.nodeConfig.memory.valueLabel': 'মান',
'flows.nodeConfig.memory.limitLabel': 'সীমা',
'flows.nodeConfig.memory.limitHint': 'সর্বোচ্চ ফলাফল সংখ্যা।',
'flows.nodeConfig.memory.minScoreLabel': 'সর্বনিম্ন স্কোর',
'flows.nodeConfig.memory.minScoreHint': '০ থেকে ১ পর্যন্ত প্রাসঙ্গিকতার থ্রেশহোল্ড।',
'flows.chooser.title': 'ওয়ার্কফ্লো তৈরি করুন',
'flows.chooser.subtitle': 'আপনি কীভাবে শুরু করতে চান তা বেছে নিন।',
+33
View File
@@ -4312,6 +4312,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': 'Transformation',
'flows.nodeKind.output_parser': 'Ausgabe-Parser',
'flows.nodeKind.sub_workflow': 'Unter-Workflow',
'flows.nodeKind.memory': 'Gedächtnis',
'flows.palette.title': 'Knoten',
'flows.palette.addNode': '{kind}-Knoten hinzufügen',
'flows.editor.save': 'Speichern',
@@ -4455,6 +4456,38 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'Quellcode',
'flows.nodeConfig.memory.operationLabel': 'Vorgang',
'flows.nodeConfig.memory.operation_recall': 'Abrufen',
'flows.nodeConfig.memory.operation_search': 'Suchen',
'flows.nodeConfig.memory.operation_flavour': 'Stilprofil',
'flows.nodeConfig.memory.operation_people': 'Personen',
'flows.nodeConfig.memory.operation_remember': 'Merken',
'flows.nodeConfig.memory.operation_forget': 'Vergessen',
'flows.nodeConfig.memory.scopeLabel': 'Bereich',
'flows.nodeConfig.memory.scopeHint': 'Wo nach diesem Gedächtniseintrag gesucht werden soll.',
'flows.nodeConfig.memory.scopeWriteHint':
'Schreibzugriffe sind nur innerhalb dieses Workflows erlaubt, niemals in deinem persönlichen Gedächtnis.',
'flows.nodeConfig.memory.scope_user': 'Du (nur lesend)',
'flows.nodeConfig.memory.scope_flow': 'Dieser Workflow',
'flows.nodeConfig.memory.scope_flows': 'Alle Workflows (nur lesend)',
'flows.nodeConfig.memory.queryLabel': 'Abfrage',
'flows.nodeConfig.memory.queryOptionalHint': 'Optional: grenzt die Personensuche ein.',
'flows.nodeConfig.memory.flavourLabel': 'Stilprofil',
'flows.nodeConfig.memory.flavourHint':
'Welche Persona-Facette gelesen werden soll, z. B. communication.',
'flows.nodeConfig.memory.flavour_communication': 'Kommunikation',
'flows.nodeConfig.memory.flavour_coding_style': 'Coding-Stil',
'flows.nodeConfig.memory.flavour_stack': 'Stack',
'flows.nodeConfig.memory.flavour_workflow': 'Workflow',
'flows.nodeConfig.memory.flavour_environment': 'Umgebung',
'flows.nodeConfig.memory.flavour_directives': 'Vorgaben',
'flows.nodeConfig.memory.flavour_anti_preferences': 'Ablehnungen',
'flows.nodeConfig.memory.keyLabel': 'Schlüssel',
'flows.nodeConfig.memory.valueLabel': 'Wert',
'flows.nodeConfig.memory.limitLabel': 'Limit',
'flows.nodeConfig.memory.limitHint': 'Maximale Anzahl an Ergebnissen.',
'flows.nodeConfig.memory.minScoreLabel': 'Mindestbewertung',
'flows.nodeConfig.memory.minScoreHint': 'Relevanzschwelle von 0 bis 1.',
'flows.chooser.title': 'Workflow erstellen',
'flows.chooser.subtitle': 'Wählen Sie, wie Sie beginnen möchten.',
+34 -1
View File
@@ -4741,7 +4741,7 @@ const en: TranslationMap = {
'flows.copilot.continueBuilding': 'Continue building',
// ── Workflow Canvas (issue B5b.1): the read-only graph view of a saved
// flow at /flows/:id. `flows.nodeKind.*` labels the 12 tinyflows node
// flow at /flows/:id. `flows.nodeKind.*` labels the 13 tinyflows node
// kinds (`tinyflows::model::NodeKind`) shown in each canvas node card.
'flows.canvas.title': 'Workflow',
'flows.canvas.loading': 'Loading workflow…',
@@ -4766,6 +4766,7 @@ const en: TranslationMap = {
'flows.nodeKind.transform': 'Transform',
'flows.nodeKind.output_parser': 'Output parser',
'flows.nodeKind.sub_workflow': 'Sub-workflow',
'flows.nodeKind.memory': 'Memory',
// ── Editable Workflow Canvas (issue B5b.2 / Phase 3a): the node palette
// and editor toolbar layered on top of the read-only canvas above.
@@ -4917,6 +4918,38 @@ const en: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'Source',
// `memory` node (issue #5226): recall/search/flavour/people read; remember/forget write.
'flows.nodeConfig.memory.operationLabel': 'Operation',
'flows.nodeConfig.memory.operation_recall': 'Recall',
'flows.nodeConfig.memory.operation_search': 'Search',
'flows.nodeConfig.memory.operation_flavour': 'Flavour',
'flows.nodeConfig.memory.operation_people': 'People',
'flows.nodeConfig.memory.operation_remember': 'Remember',
'flows.nodeConfig.memory.operation_forget': 'Forget',
'flows.nodeConfig.memory.scopeLabel': 'Scope',
'flows.nodeConfig.memory.scopeHint': 'Where to look for this memory.',
'flows.nodeConfig.memory.scopeWriteHint':
'Writes are only allowed within this workflow, never to your personal memory.',
'flows.nodeConfig.memory.scope_user': 'You (read-only)',
'flows.nodeConfig.memory.scope_flow': 'This workflow',
'flows.nodeConfig.memory.scope_flows': 'All workflows (read-only)',
'flows.nodeConfig.memory.queryLabel': 'Query',
'flows.nodeConfig.memory.queryOptionalHint': 'Optional: narrows the people lookup.',
'flows.nodeConfig.memory.flavourLabel': 'Flavour',
'flows.nodeConfig.memory.flavourHint': 'Which persona facet to read, e.g. communication.',
'flows.nodeConfig.memory.flavour_communication': 'Communication',
'flows.nodeConfig.memory.flavour_coding_style': 'Coding style',
'flows.nodeConfig.memory.flavour_stack': 'Stack',
'flows.nodeConfig.memory.flavour_workflow': 'Workflow',
'flows.nodeConfig.memory.flavour_environment': 'Environment',
'flows.nodeConfig.memory.flavour_directives': 'Directives',
'flows.nodeConfig.memory.flavour_anti_preferences': 'Anti-preferences',
'flows.nodeConfig.memory.keyLabel': 'Key',
'flows.nodeConfig.memory.valueLabel': 'Value',
'flows.nodeConfig.memory.limitLabel': 'Limit',
'flows.nodeConfig.memory.limitHint': 'Maximum number of results.',
'flows.nodeConfig.memory.minScoreLabel': 'Minimum score',
'flows.nodeConfig.memory.minScoreHint': 'Relevance threshold from 0 to 1.',
// Phase 4a "New workflow" chooser + Phase 4c templates gallery. The chooser
// offers scratch / template / describe; the gallery lists the curated
+33
View File
@@ -4263,6 +4263,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': 'Transformar',
'flows.nodeKind.output_parser': 'Analizador de salida',
'flows.nodeKind.sub_workflow': 'Subflujo de trabajo',
'flows.nodeKind.memory': 'Memoria',
'flows.palette.title': 'Nodos',
'flows.palette.addNode': 'Añadir nodo {kind}',
'flows.editor.save': 'Guardar',
@@ -4405,6 +4406,38 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'Código fuente',
'flows.nodeConfig.memory.operationLabel': 'Operación',
'flows.nodeConfig.memory.operation_recall': 'Recordar',
'flows.nodeConfig.memory.operation_search': 'Buscar',
'flows.nodeConfig.memory.operation_flavour': 'Estilo',
'flows.nodeConfig.memory.operation_people': 'Personas',
'flows.nodeConfig.memory.operation_remember': 'Guardar',
'flows.nodeConfig.memory.operation_forget': 'Olvidar',
'flows.nodeConfig.memory.scopeLabel': 'Alcance',
'flows.nodeConfig.memory.scopeHint': 'Dónde buscar esta memoria.',
'flows.nodeConfig.memory.scopeWriteHint':
'Solo se permite escribir dentro de este flujo de trabajo, nunca en tu memoria personal.',
'flows.nodeConfig.memory.scope_user': 'Tú (solo lectura)',
'flows.nodeConfig.memory.scope_flow': 'Este flujo de trabajo',
'flows.nodeConfig.memory.scope_flows': 'Todos los flujos de trabajo (solo lectura)',
'flows.nodeConfig.memory.queryLabel': 'Consulta',
'flows.nodeConfig.memory.queryOptionalHint': 'Opcional: acota la búsqueda de personas.',
'flows.nodeConfig.memory.flavourLabel': 'Estilo',
'flows.nodeConfig.memory.flavourHint':
'Qué faceta de la persona leer, por ejemplo communication.',
'flows.nodeConfig.memory.flavour_communication': 'Comunicación',
'flows.nodeConfig.memory.flavour_coding_style': 'Estilo de código',
'flows.nodeConfig.memory.flavour_stack': 'Stack',
'flows.nodeConfig.memory.flavour_workflow': 'Flujo de trabajo',
'flows.nodeConfig.memory.flavour_environment': 'Entorno',
'flows.nodeConfig.memory.flavour_directives': 'Directivas',
'flows.nodeConfig.memory.flavour_anti_preferences': 'Preferencias negativas',
'flows.nodeConfig.memory.keyLabel': 'Clave',
'flows.nodeConfig.memory.valueLabel': 'Valor',
'flows.nodeConfig.memory.limitLabel': 'Límite',
'flows.nodeConfig.memory.limitHint': 'Número máximo de resultados.',
'flows.nodeConfig.memory.minScoreLabel': 'Puntuación mínima',
'flows.nodeConfig.memory.minScoreHint': 'Umbral de relevancia de 0 a 1.',
'flows.chooser.title': 'Crear un flujo de trabajo',
'flows.chooser.subtitle': 'Elige cómo quieres empezar.',
+33
View File
@@ -4291,6 +4291,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': 'Transformation',
'flows.nodeKind.output_parser': 'Analyseur de sortie',
'flows.nodeKind.sub_workflow': 'Sous-workflow',
'flows.nodeKind.memory': 'Mémoire',
'flows.palette.title': 'Nœuds',
'flows.palette.addNode': 'Ajouter un nœud {kind}',
'flows.editor.save': 'Enregistrer',
@@ -4435,6 +4436,38 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'Code source',
'flows.nodeConfig.memory.operationLabel': 'Opération',
'flows.nodeConfig.memory.operation_recall': 'Rappel',
'flows.nodeConfig.memory.operation_search': 'Recherche',
'flows.nodeConfig.memory.operation_flavour': 'Style',
'flows.nodeConfig.memory.operation_people': 'Personnes',
'flows.nodeConfig.memory.operation_remember': 'Mémoriser',
'flows.nodeConfig.memory.operation_forget': 'Oublier',
'flows.nodeConfig.memory.scopeLabel': 'Portée',
'flows.nodeConfig.memory.scopeHint': 'Où chercher cette mémoire.',
'flows.nodeConfig.memory.scopeWriteHint':
"L'écriture n'est autorisée qu'au sein de ce workflow, jamais dans votre mémoire personnelle.",
'flows.nodeConfig.memory.scope_user': 'Vous (lecture seule)',
'flows.nodeConfig.memory.scope_flow': 'Ce workflow',
'flows.nodeConfig.memory.scope_flows': 'Tous les workflows (lecture seule)',
'flows.nodeConfig.memory.queryLabel': 'Requête',
'flows.nodeConfig.memory.queryOptionalHint': 'Optionnel: affine la recherche de personnes.',
'flows.nodeConfig.memory.flavourLabel': 'Style',
'flows.nodeConfig.memory.flavourHint':
'Quelle facette de la persona lire, par exemple communication.',
'flows.nodeConfig.memory.flavour_communication': 'Communication',
'flows.nodeConfig.memory.flavour_coding_style': 'Style de code',
'flows.nodeConfig.memory.flavour_stack': 'Stack technique',
'flows.nodeConfig.memory.flavour_workflow': 'Flux de travail',
'flows.nodeConfig.memory.flavour_environment': 'Environnement',
'flows.nodeConfig.memory.flavour_directives': 'Directives',
'flows.nodeConfig.memory.flavour_anti_preferences': 'Préférences négatives',
'flows.nodeConfig.memory.keyLabel': 'Clé',
'flows.nodeConfig.memory.valueLabel': 'Valeur',
'flows.nodeConfig.memory.limitLabel': 'Limite',
'flows.nodeConfig.memory.limitHint': 'Nombre maximal de résultats.',
'flows.nodeConfig.memory.minScoreLabel': 'Score minimal',
'flows.nodeConfig.memory.minScoreHint': 'Seuil de pertinence de 0 à 1.',
'flows.chooser.title': 'Créer un workflow',
'flows.chooser.subtitle': 'Choisissez comment commencer.',
+32
View File
@@ -4191,6 +4191,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': 'रूपांतरण',
'flows.nodeKind.output_parser': 'आउटपुट पार्सर',
'flows.nodeKind.sub_workflow': 'सब-वर्कफ़्लो',
'flows.nodeKind.memory': 'मेमोरी',
'flows.palette.title': 'नोड',
'flows.palette.addNode': '{kind} नोड जोड़ें',
'flows.editor.save': 'सहेजें',
@@ -4330,6 +4331,37 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'सोर्स कोड',
'flows.nodeConfig.memory.operationLabel': 'ऑपरेशन',
'flows.nodeConfig.memory.operation_recall': 'याद करें',
'flows.nodeConfig.memory.operation_search': 'खोजें',
'flows.nodeConfig.memory.operation_flavour': 'शैली',
'flows.nodeConfig.memory.operation_people': 'लोग',
'flows.nodeConfig.memory.operation_remember': 'याद रखें',
'flows.nodeConfig.memory.operation_forget': 'भूल जाएँ',
'flows.nodeConfig.memory.scopeLabel': 'दायरा',
'flows.nodeConfig.memory.scopeHint': 'यह मेमोरी कहाँ खोजनी है।',
'flows.nodeConfig.memory.scopeWriteHint':
'लिखने की अनुमति केवल इसी वर्कफ़्लो के भीतर है, आपकी निजी मेमोरी में कभी नहीं।',
'flows.nodeConfig.memory.scope_user': 'आप (केवल पढ़ने के लिए)',
'flows.nodeConfig.memory.scope_flow': 'यह वर्कफ़्लो',
'flows.nodeConfig.memory.scope_flows': 'सभी वर्कफ़्लो (केवल पढ़ने के लिए)',
'flows.nodeConfig.memory.queryLabel': 'क्वेरी',
'flows.nodeConfig.memory.queryOptionalHint': 'वैकल्पिक: लोगों की खोज को सीमित करता है।',
'flows.nodeConfig.memory.flavourLabel': 'शैली',
'flows.nodeConfig.memory.flavourHint': 'कौन सा व्यक्तित्व पहलू पढ़ना है, जैसे communication।',
'flows.nodeConfig.memory.flavour_communication': 'संचार',
'flows.nodeConfig.memory.flavour_coding_style': 'कोडिंग शैली',
'flows.nodeConfig.memory.flavour_stack': 'तकनीकी स्टैक',
'flows.nodeConfig.memory.flavour_workflow': 'वर्कफ़्लो',
'flows.nodeConfig.memory.flavour_environment': 'वातावरण',
'flows.nodeConfig.memory.flavour_directives': 'निर्देश',
'flows.nodeConfig.memory.flavour_anti_preferences': 'नापसंद',
'flows.nodeConfig.memory.keyLabel': 'कुंजी',
'flows.nodeConfig.memory.valueLabel': 'मान',
'flows.nodeConfig.memory.limitLabel': 'सीमा',
'flows.nodeConfig.memory.limitHint': 'अधिकतम परिणामों की संख्या।',
'flows.nodeConfig.memory.minScoreLabel': 'न्यूनतम स्कोर',
'flows.nodeConfig.memory.minScoreHint': '0 से 1 तक की प्रासंगिकता सीमा।',
'flows.chooser.title': 'वर्कफ़्लो बनाएँ',
'flows.chooser.subtitle': 'चुनें कि आप कैसे शुरू करना चाहते हैं।',
+33
View File
@@ -4207,6 +4207,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': 'Transformasi',
'flows.nodeKind.output_parser': 'Pengurai keluaran',
'flows.nodeKind.sub_workflow': 'Sub-alur kerja',
'flows.nodeKind.memory': 'Memori',
'flows.palette.title': 'Simpul',
'flows.palette.addNode': 'Tambah simpul {kind}',
'flows.editor.save': 'Simpan',
@@ -4347,6 +4348,38 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'Kode sumber',
'flows.nodeConfig.memory.operationLabel': 'Operasi',
'flows.nodeConfig.memory.operation_recall': 'Ingat kembali',
'flows.nodeConfig.memory.operation_search': 'Cari',
'flows.nodeConfig.memory.operation_flavour': 'Gaya',
'flows.nodeConfig.memory.operation_people': 'Orang',
'flows.nodeConfig.memory.operation_remember': 'Simpan',
'flows.nodeConfig.memory.operation_forget': 'Lupakan',
'flows.nodeConfig.memory.scopeLabel': 'Cakupan',
'flows.nodeConfig.memory.scopeHint': 'Tempat mencari memori ini.',
'flows.nodeConfig.memory.scopeWriteHint':
'Penulisan hanya diizinkan dalam alur kerja ini, tidak pernah ke memori pribadi Anda.',
'flows.nodeConfig.memory.scope_user': 'Anda (hanya baca)',
'flows.nodeConfig.memory.scope_flow': 'Alur kerja ini',
'flows.nodeConfig.memory.scope_flows': 'Semua alur kerja (hanya baca)',
'flows.nodeConfig.memory.queryLabel': 'Kueri',
'flows.nodeConfig.memory.queryOptionalHint': 'Opsional: mempersempit pencarian orang.',
'flows.nodeConfig.memory.flavourLabel': 'Gaya',
'flows.nodeConfig.memory.flavourHint':
'Facet persona mana yang akan dibaca, misalnya communication.',
'flows.nodeConfig.memory.flavour_communication': 'Komunikasi',
'flows.nodeConfig.memory.flavour_coding_style': 'Gaya coding',
'flows.nodeConfig.memory.flavour_stack': 'Stack teknologi',
'flows.nodeConfig.memory.flavour_workflow': 'Alur kerja',
'flows.nodeConfig.memory.flavour_environment': 'Lingkungan',
'flows.nodeConfig.memory.flavour_directives': 'Arahan',
'flows.nodeConfig.memory.flavour_anti_preferences': 'Preferensi negatif',
'flows.nodeConfig.memory.keyLabel': 'Kunci',
'flows.nodeConfig.memory.valueLabel': 'Nilai',
'flows.nodeConfig.memory.limitLabel': 'Batas',
'flows.nodeConfig.memory.limitHint': 'Jumlah maksimum hasil.',
'flows.nodeConfig.memory.minScoreLabel': 'Skor minimum',
'flows.nodeConfig.memory.minScoreHint': 'Ambang batas relevansi dari 0 hingga 1.',
'flows.chooser.title': 'Buat alur kerja',
'flows.chooser.subtitle': 'Pilih cara Anda ingin memulai.',
+33
View File
@@ -4259,6 +4259,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': 'Trasformazione',
'flows.nodeKind.output_parser': 'Analizzatore di output',
'flows.nodeKind.sub_workflow': 'Sotto-flusso di lavoro',
'flows.nodeKind.memory': 'Memoria',
'flows.palette.title': 'Nodi',
'flows.palette.addNode': 'Aggiungi nodo {kind}',
'flows.editor.save': 'Salva',
@@ -4403,6 +4404,38 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'Codice sorgente',
'flows.nodeConfig.memory.operationLabel': 'Operazione',
'flows.nodeConfig.memory.operation_recall': 'Richiamo',
'flows.nodeConfig.memory.operation_search': 'Ricerca',
'flows.nodeConfig.memory.operation_flavour': 'Stile',
'flows.nodeConfig.memory.operation_people': 'Persone',
'flows.nodeConfig.memory.operation_remember': 'Memorizza',
'flows.nodeConfig.memory.operation_forget': 'Dimentica',
'flows.nodeConfig.memory.scopeLabel': 'Ambito',
'flows.nodeConfig.memory.scopeHint': 'Dove cercare questa memoria.',
'flows.nodeConfig.memory.scopeWriteHint':
"La scrittura è consentita solo all'interno di questo flusso di lavoro, mai nella tua memoria personale.",
'flows.nodeConfig.memory.scope_user': 'Tu (sola lettura)',
'flows.nodeConfig.memory.scope_flow': 'Questo flusso di lavoro',
'flows.nodeConfig.memory.scope_flows': 'Tutti i flussi di lavoro (sola lettura)',
'flows.nodeConfig.memory.queryLabel': 'Query',
'flows.nodeConfig.memory.queryOptionalHint': 'Facoltativo: restringe la ricerca di persone.',
'flows.nodeConfig.memory.flavourLabel': 'Stile',
'flows.nodeConfig.memory.flavourHint':
'Quale sfaccettatura della persona leggere, ad esempio communication.',
'flows.nodeConfig.memory.flavour_communication': 'Comunicazione',
'flows.nodeConfig.memory.flavour_coding_style': 'Stile di codifica',
'flows.nodeConfig.memory.flavour_stack': 'Stack tecnologico',
'flows.nodeConfig.memory.flavour_workflow': 'Flusso di lavoro',
'flows.nodeConfig.memory.flavour_environment': 'Ambiente',
'flows.nodeConfig.memory.flavour_directives': 'Direttive',
'flows.nodeConfig.memory.flavour_anti_preferences': 'Preferenze negative',
'flows.nodeConfig.memory.keyLabel': 'Chiave',
'flows.nodeConfig.memory.valueLabel': 'Valore',
'flows.nodeConfig.memory.limitLabel': 'Limite',
'flows.nodeConfig.memory.limitHint': 'Numero massimo di risultati.',
'flows.nodeConfig.memory.minScoreLabel': 'Punteggio minimo',
'flows.nodeConfig.memory.minScoreHint': 'Soglia di rilevanza da 0 a 1.',
'flows.chooser.title': 'Crea un flusso di lavoro',
'flows.chooser.subtitle': 'Scegli come iniziare.',
+32
View File
@@ -4144,6 +4144,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': '변환',
'flows.nodeKind.output_parser': '출력 파서',
'flows.nodeKind.sub_workflow': '하위 워크플로',
'flows.nodeKind.memory': '메모리',
'flows.palette.title': '노드',
'flows.palette.addNode': '{kind} 노드 추가',
'flows.editor.save': '저장',
@@ -4281,6 +4282,37 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': '소스 코드',
'flows.nodeConfig.memory.operationLabel': '작업',
'flows.nodeConfig.memory.operation_recall': '회상',
'flows.nodeConfig.memory.operation_search': '검색',
'flows.nodeConfig.memory.operation_flavour': '스타일',
'flows.nodeConfig.memory.operation_people': '사람',
'flows.nodeConfig.memory.operation_remember': '기억하기',
'flows.nodeConfig.memory.operation_forget': '잊기',
'flows.nodeConfig.memory.scopeLabel': '범위',
'flows.nodeConfig.memory.scopeHint': '이 메모리를 어디에서 찾을지 지정합니다.',
'flows.nodeConfig.memory.scopeWriteHint':
'쓰기는 이 워크플로 내에서만 허용되며, 개인 메모리에는 절대 저장되지 않습니다.',
'flows.nodeConfig.memory.scope_user': '나 (읽기 전용)',
'flows.nodeConfig.memory.scope_flow': '이 워크플로',
'flows.nodeConfig.memory.scope_flows': '모든 워크플로 (읽기 전용)',
'flows.nodeConfig.memory.queryLabel': '쿼리',
'flows.nodeConfig.memory.queryOptionalHint': '선택 사항: 사람 조회 범위를 좁힙니다.',
'flows.nodeConfig.memory.flavourLabel': '스타일',
'flows.nodeConfig.memory.flavourHint': '읽어올 페르소나 특성입니다. 예: communication.',
'flows.nodeConfig.memory.flavour_communication': '커뮤니케이션',
'flows.nodeConfig.memory.flavour_coding_style': '코딩 스타일',
'flows.nodeConfig.memory.flavour_stack': '기술 스택',
'flows.nodeConfig.memory.flavour_workflow': '워크플로',
'flows.nodeConfig.memory.flavour_environment': '환경',
'flows.nodeConfig.memory.flavour_directives': '지시사항',
'flows.nodeConfig.memory.flavour_anti_preferences': '비선호 항목',
'flows.nodeConfig.memory.keyLabel': '키',
'flows.nodeConfig.memory.valueLabel': '값',
'flows.nodeConfig.memory.limitLabel': '제한',
'flows.nodeConfig.memory.limitHint': '최대 결과 수입니다.',
'flows.nodeConfig.memory.minScoreLabel': '최소 점수',
'flows.nodeConfig.memory.minScoreHint': '0에서 1 사이의 관련성 임곗값입니다.',
'flows.chooser.title': '워크플로 만들기',
'flows.chooser.subtitle': '시작 방법을 선택하세요.',
+32
View File
@@ -4248,6 +4248,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': 'Transformacja',
'flows.nodeKind.output_parser': 'Parser wyjścia',
'flows.nodeKind.sub_workflow': 'Podprzepływ pracy',
'flows.nodeKind.memory': 'Pamięć',
'flows.palette.title': 'Węzły',
'flows.palette.addNode': 'Dodaj węzeł {kind}',
'flows.editor.save': 'Zapisz',
@@ -4390,6 +4391,37 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'Kod źródłowy',
'flows.nodeConfig.memory.operationLabel': 'Operacja',
'flows.nodeConfig.memory.operation_recall': 'Przywołanie',
'flows.nodeConfig.memory.operation_search': 'Wyszukiwanie',
'flows.nodeConfig.memory.operation_flavour': 'Styl',
'flows.nodeConfig.memory.operation_people': 'Osoby',
'flows.nodeConfig.memory.operation_remember': 'Zapamiętaj',
'flows.nodeConfig.memory.operation_forget': 'Zapomnij',
'flows.nodeConfig.memory.scopeLabel': 'Zakres',
'flows.nodeConfig.memory.scopeHint': 'Gdzie szukać tej pamięci.',
'flows.nodeConfig.memory.scopeWriteHint':
'Zapis jest dozwolony wyłącznie w obrębie tego przepływu pracy, nigdy w pamięci osobistej.',
'flows.nodeConfig.memory.scope_user': 'Ty (tylko do odczytu)',
'flows.nodeConfig.memory.scope_flow': 'Ten przepływ pracy',
'flows.nodeConfig.memory.scope_flows': 'Wszystkie przepływy pracy (tylko do odczytu)',
'flows.nodeConfig.memory.queryLabel': 'Zapytanie',
'flows.nodeConfig.memory.queryOptionalHint': 'Opcjonalnie: zawęża wyszukiwanie osób.',
'flows.nodeConfig.memory.flavourLabel': 'Styl',
'flows.nodeConfig.memory.flavourHint': 'Który aspekt persony odczytać, np. communication.',
'flows.nodeConfig.memory.flavour_communication': 'Komunikacja',
'flows.nodeConfig.memory.flavour_coding_style': 'Styl kodowania',
'flows.nodeConfig.memory.flavour_stack': 'Stos technologiczny',
'flows.nodeConfig.memory.flavour_workflow': 'Przepływ pracy',
'flows.nodeConfig.memory.flavour_environment': 'Środowisko',
'flows.nodeConfig.memory.flavour_directives': 'Wytyczne',
'flows.nodeConfig.memory.flavour_anti_preferences': 'Preferencje negatywne',
'flows.nodeConfig.memory.keyLabel': 'Klucz',
'flows.nodeConfig.memory.valueLabel': 'Wartość',
'flows.nodeConfig.memory.limitLabel': 'Limit',
'flows.nodeConfig.memory.limitHint': 'Maksymalna liczba wyników.',
'flows.nodeConfig.memory.minScoreLabel': 'Minimalny wynik',
'flows.nodeConfig.memory.minScoreHint': 'Próg trafności od 0 do 1.',
'flows.chooser.title': 'Utwórz przepływ pracy',
'flows.chooser.subtitle': 'Wybierz, jak chcesz zacząć.',
+32
View File
@@ -4252,6 +4252,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': 'Transformação',
'flows.nodeKind.output_parser': 'Analisador de saída',
'flows.nodeKind.sub_workflow': 'Subfluxo de trabalho',
'flows.nodeKind.memory': 'Memória',
'flows.palette.title': 'Nós',
'flows.palette.addNode': 'Adicionar nó {kind}',
'flows.editor.save': 'Salvar',
@@ -4394,6 +4395,37 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'Código-fonte',
'flows.nodeConfig.memory.operationLabel': 'Operação',
'flows.nodeConfig.memory.operation_recall': 'Recordar',
'flows.nodeConfig.memory.operation_search': 'Pesquisar',
'flows.nodeConfig.memory.operation_flavour': 'Estilo',
'flows.nodeConfig.memory.operation_people': 'Pessoas',
'flows.nodeConfig.memory.operation_remember': 'Lembrar',
'flows.nodeConfig.memory.operation_forget': 'Esquecer',
'flows.nodeConfig.memory.scopeLabel': 'Escopo',
'flows.nodeConfig.memory.scopeHint': 'Onde procurar esta memória.',
'flows.nodeConfig.memory.scopeWriteHint':
'A escrita só é permitida dentro deste fluxo de trabalho, nunca na sua memória pessoal.',
'flows.nodeConfig.memory.scope_user': 'Você (somente leitura)',
'flows.nodeConfig.memory.scope_flow': 'Este fluxo de trabalho',
'flows.nodeConfig.memory.scope_flows': 'Todos os fluxos de trabalho (somente leitura)',
'flows.nodeConfig.memory.queryLabel': 'Consulta',
'flows.nodeConfig.memory.queryOptionalHint': 'Opcional: restringe a busca de pessoas.',
'flows.nodeConfig.memory.flavourLabel': 'Estilo',
'flows.nodeConfig.memory.flavourHint': 'Qual faceta da persona ler, por exemplo communication.',
'flows.nodeConfig.memory.flavour_communication': 'Comunicação',
'flows.nodeConfig.memory.flavour_coding_style': 'Estilo de código',
'flows.nodeConfig.memory.flavour_stack': 'Stack técnica',
'flows.nodeConfig.memory.flavour_workflow': 'Fluxo de trabalho',
'flows.nodeConfig.memory.flavour_environment': 'Ambiente',
'flows.nodeConfig.memory.flavour_directives': 'Diretrizes',
'flows.nodeConfig.memory.flavour_anti_preferences': 'Preferências negativas',
'flows.nodeConfig.memory.keyLabel': 'Chave',
'flows.nodeConfig.memory.valueLabel': 'Valor',
'flows.nodeConfig.memory.limitLabel': 'Limite',
'flows.nodeConfig.memory.limitHint': 'Número máximo de resultados.',
'flows.nodeConfig.memory.minScoreLabel': 'Pontuação mínima',
'flows.nodeConfig.memory.minScoreHint': 'Limite de relevância de 0 a 1.',
'flows.chooser.title': 'Criar um fluxo de trabalho',
'flows.chooser.subtitle': 'Escolha como quer começar.',
+32
View File
@@ -4230,6 +4230,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': 'Преобразование',
'flows.nodeKind.output_parser': 'Парсер вывода',
'flows.nodeKind.sub_workflow': 'Подпроцесс',
'flows.nodeKind.memory': 'Память',
'flows.palette.title': 'Узлы',
'flows.palette.addNode': 'Добавить узел {kind}',
'flows.editor.save': 'Сохранить',
@@ -4374,6 +4375,37 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': 'Исходный код',
'flows.nodeConfig.memory.operationLabel': 'Операция',
'flows.nodeConfig.memory.operation_recall': 'Вспомнить',
'flows.nodeConfig.memory.operation_search': 'Поиск',
'flows.nodeConfig.memory.operation_flavour': 'Стиль',
'flows.nodeConfig.memory.operation_people': 'Люди',
'flows.nodeConfig.memory.operation_remember': 'Запомнить',
'flows.nodeConfig.memory.operation_forget': 'Забыть',
'flows.nodeConfig.memory.scopeLabel': 'Область',
'flows.nodeConfig.memory.scopeHint': 'Где искать эту память.',
'flows.nodeConfig.memory.scopeWriteHint':
'Запись разрешена только в пределах этого рабочего процесса, но никогда в вашей личной памяти.',
'flows.nodeConfig.memory.scope_user': 'Вы (только чтение)',
'flows.nodeConfig.memory.scope_flow': 'Этот рабочий процесс',
'flows.nodeConfig.memory.scope_flows': 'Все рабочие процессы (только чтение)',
'flows.nodeConfig.memory.queryLabel': 'Запрос',
'flows.nodeConfig.memory.queryOptionalHint': 'Необязательно: сужает поиск людей.',
'flows.nodeConfig.memory.flavourLabel': 'Стиль',
'flows.nodeConfig.memory.flavourHint': 'Какую грань личности прочитать, например communication.',
'flows.nodeConfig.memory.flavour_communication': 'Общение',
'flows.nodeConfig.memory.flavour_coding_style': 'Стиль кода',
'flows.nodeConfig.memory.flavour_stack': 'Технологический стек',
'flows.nodeConfig.memory.flavour_workflow': 'Рабочий процесс',
'flows.nodeConfig.memory.flavour_environment': 'Окружение',
'flows.nodeConfig.memory.flavour_directives': 'Директивы',
'flows.nodeConfig.memory.flavour_anti_preferences': 'Нежелательные предпочтения',
'flows.nodeConfig.memory.keyLabel': 'Ключ',
'flows.nodeConfig.memory.valueLabel': 'Значение',
'flows.nodeConfig.memory.limitLabel': 'Лимит',
'flows.nodeConfig.memory.limitHint': 'Максимальное количество результатов.',
'flows.nodeConfig.memory.minScoreLabel': 'Минимальная оценка',
'flows.nodeConfig.memory.minScoreHint': 'Порог релевантности от 0 до 1.',
'flows.chooser.title': 'Создать рабочий процесс',
'flows.chooser.subtitle': 'Выберите, с чего начать.',
+31
View File
@@ -3972,6 +3972,7 @@ const messages: TranslationMap = {
'flows.nodeKind.transform': '转换',
'flows.nodeKind.output_parser': '输出解析器',
'flows.nodeKind.sub_workflow': '子工作流',
'flows.nodeKind.memory': '记忆',
'flows.palette.title': '节点',
'flows.palette.addNode': '添加{kind}节点',
'flows.editor.save': '保存',
@@ -4103,6 +4104,36 @@ const messages: TranslationMap = {
'flows.nodeConfig.code.language_javascript': 'JavaScript',
'flows.nodeConfig.code.language_python': 'Python',
'flows.nodeConfig.code.sourceLabel': '源代码',
'flows.nodeConfig.memory.operationLabel': '操作',
'flows.nodeConfig.memory.operation_recall': '回忆',
'flows.nodeConfig.memory.operation_search': '搜索',
'flows.nodeConfig.memory.operation_flavour': '风格',
'flows.nodeConfig.memory.operation_people': '人物',
'flows.nodeConfig.memory.operation_remember': '记住',
'flows.nodeConfig.memory.operation_forget': '忘记',
'flows.nodeConfig.memory.scopeLabel': '范围',
'flows.nodeConfig.memory.scopeHint': '在哪里查找这条记忆。',
'flows.nodeConfig.memory.scopeWriteHint': '写入仅允许在此工作流内进行,绝不会写入你的个人记忆。',
'flows.nodeConfig.memory.scope_user': '你(只读)',
'flows.nodeConfig.memory.scope_flow': '此工作流',
'flows.nodeConfig.memory.scope_flows': '所有工作流(只读)',
'flows.nodeConfig.memory.queryLabel': '查询',
'flows.nodeConfig.memory.queryOptionalHint': '可选:缩小人物查找范围。',
'flows.nodeConfig.memory.flavourLabel': '风格',
'flows.nodeConfig.memory.flavourHint': '要读取哪个人格面向,例如 communication。',
'flows.nodeConfig.memory.flavour_communication': '沟通',
'flows.nodeConfig.memory.flavour_coding_style': '编码风格',
'flows.nodeConfig.memory.flavour_stack': '技术栈',
'flows.nodeConfig.memory.flavour_workflow': '工作流程',
'flows.nodeConfig.memory.flavour_environment': '环境',
'flows.nodeConfig.memory.flavour_directives': '指令',
'flows.nodeConfig.memory.flavour_anti_preferences': '负向偏好',
'flows.nodeConfig.memory.keyLabel': '键',
'flows.nodeConfig.memory.valueLabel': '值',
'flows.nodeConfig.memory.limitLabel': '数量上限',
'flows.nodeConfig.memory.limitHint': '返回结果的最大数量。',
'flows.nodeConfig.memory.minScoreLabel': '最低分数',
'flows.nodeConfig.memory.minScoreHint': '相关性阈值,范围为 0 到 1。',
'flows.chooser.title': '创建工作流',
'flows.chooser.subtitle': '选择你想要的开始方式。',