From 39fe7dbb330dce69b6efc1723d377b566930b900 Mon Sep 17 00:00:00 2001 From: Cyrus Gray <144336577+graycyrus@users.noreply.github.com> Date: Wed, 29 Jul 2026 08:28:24 +0530 Subject: [PATCH] =?UTF-8?q?feat(flows):=20frontend=20for=20the=20`memory`?= =?UTF-8?q?=20node=20=E2=80=94=20palette,=20config=20panel,=20i18n=20?= =?UTF-8?q?=C3=9714=20(#5228)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/memoryFields.test.tsx | 118 +++++++++ .../flows/canvas/nodeConfig/memoryFields.tsx | 235 ++++++++++++++++++ .../canvas/nodeConfig/nodeConfigFields.tsx | 57 +++++ .../canvas/nodeConfig/nodeConfigForms.tsx | 4 + app/src/lib/flows/nodeKindMeta.ts | 18 +- app/src/lib/flows/nodeSummary.test.ts | 10 + app/src/lib/flows/nodeSummary.ts | 15 ++ app/src/lib/flows/types.ts | 15 +- app/src/lib/i18n/ar.ts | 32 +++ app/src/lib/i18n/bn.ts | 32 +++ app/src/lib/i18n/de.ts | 33 +++ app/src/lib/i18n/en.ts | 35 ++- app/src/lib/i18n/es.ts | 33 +++ app/src/lib/i18n/fr.ts | 33 +++ app/src/lib/i18n/hi.ts | 32 +++ app/src/lib/i18n/id.ts | 33 +++ app/src/lib/i18n/it.ts | 33 +++ app/src/lib/i18n/ko.ts | 32 +++ app/src/lib/i18n/pl.ts | 32 +++ app/src/lib/i18n/pt.ts | 32 +++ app/src/lib/i18n/ru.ts | 32 +++ app/src/lib/i18n/zh-CN.ts | 31 +++ 22 files changed, 919 insertions(+), 8 deletions(-) create mode 100644 app/src/components/flows/canvas/nodeConfig/__tests__/memoryFields.test.tsx create mode 100644 app/src/components/flows/canvas/nodeConfig/memoryFields.tsx diff --git a/app/src/components/flows/canvas/nodeConfig/__tests__/memoryFields.test.tsx b/app/src/components/flows/canvas/nodeConfig/__tests__/memoryFields.test.tsx new file mode 100644 index 000000000..5a5d13ea5 --- /dev/null +++ b/app/src/components/flows/canvas/nodeConfig/__tests__/memoryFields.test.tsx @@ -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 = {}) { + const onChange = vi.fn(); + render(); + 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 }); + }); +}); diff --git a/app/src/components/flows/canvas/nodeConfig/memoryFields.tsx b/app/src/components/flows/canvas/nodeConfig/memoryFields.tsx new file mode 100644 index 000000000..2ab29bd19 --- /dev/null +++ b/app/src/components/flows/canvas/nodeConfig/memoryFields.tsx @@ -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(['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(['remember', 'forget']); + +/** Operations that take a free-text `query`. */ +const QUERY_OPERATIONS = new Set(['recall', 'search', 'people']); + +/** Operations whose result set can be capped/thresholded. */ +const RESULT_OPERATIONS = new Set(['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; + onChange: (patch: Record) => 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 = { 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 ( +
+ ({ + value: op, + label: t(`flows.nodeConfig.memory.operation_${op}`), + }))} + /> + + {SCOPED_OPERATIONS.has(operation) && ( + onChange({ scope: v })} + testId="node-config-memory-scope" + options={scopeOptions} + /> + )} + + {QUERY_OPERATIONS.has(operation) && ( + onChange({ query: v })} + placeholder="=item.title" + upstreamOptions={upstreamOptions} + testId="node-config-memory-query" + /> + )} + + {operation === 'flavour' && ( + 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') && ( + onChange({ key: v })} + placeholder="=item.id" + upstreamOptions={upstreamOptions} + testId="node-config-memory-key" + /> + )} + + {operation === 'remember' && ( + onChange({ value: v })} + placeholder="=item" + upstreamOptions={upstreamOptions} + testId="node-config-memory-value" + /> + )} + + {RESULT_OPERATIONS.has(operation) && ( +
+ onChange({ limit: v })} + min={1} + step={1} + testId="node-config-memory-limit" + /> + onChange({ min_score: v })} + min={0} + max={1} + step={0.05} + testId="node-config-memory-min-score" + /> +
+ )} +
+ ); +} diff --git a/app/src/components/flows/canvas/nodeConfig/nodeConfigFields.tsx b/app/src/components/flows/canvas/nodeConfig/nodeConfigFields.tsx index e557fa43d..b0bd28059 100644 --- a/app/src/components/flows/canvas/nodeConfig/nodeConfigFields.tsx +++ b/app/src/components/flows/canvas/nodeConfig/nodeConfigFields.tsx @@ -36,6 +36,12 @@ export function configString(config: Record, 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, key: string): number | undefined { + const value = config[key]; + return typeof value === 'number' && Number.isFinite(value) ? value : undefined; +} + /** Read a `Record` map off config (e.g. HTTP headers / transform set). */ export function configStringMap( config: Record, @@ -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 ( + + { + const raw = e.target.value; + onChange(raw === '' ? undefined : Number(raw)); + }} + /> + + ); +} + /** * Compact "insert an upstream value" dropdown (Feature: `nodes` scope picker). * Always renders with the empty placeholder selected; picking an option calls diff --git a/app/src/components/flows/canvas/nodeConfig/nodeConfigForms.tsx b/app/src/components/flows/canvas/nodeConfig/nodeConfigForms.tsx index 39e681a7f..d10ccfb46 100644 --- a/app/src/components/flows/canvas/nodeConfig/nodeConfigForms.tsx +++ b/app/src/components/flows/canvas/nodeConfig/nodeConfigForms.tsx @@ -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> = { switch: SwitchForm, transform: TransformForm, code: CodeForm, + memory: MemoryForm, }; diff --git a/app/src/lib/flows/nodeKindMeta.ts b/app/src/lib/flows/nodeKindMeta.ts index b0d6d3d4a..a88ca529b 100644 --- a/app/src/lib/flows/nodeKindMeta.ts +++ b/app/src/lib/flows/nodeKindMeta.ts @@ -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 * ``-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 = { 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 = { /** * 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 diff --git a/app/src/lib/flows/nodeSummary.test.ts b/app/src/lib/flows/nodeSummary.test.ts index f277d5f2d..83d864581 100644 --- a/app/src/lib/flows/nodeSummary.test.ts +++ b/app/src/lib/flows/nodeSummary.test.ts @@ -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'); + }); }); diff --git a/app/src/lib/flows/nodeSummary.ts b/app/src/lib/flows/nodeSummary.ts index d1905acfe..c39378817 100644 --- a/app/src/lib/flows/nodeSummary.ts +++ b/app/src/lib/flows/nodeSummary.ts @@ -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 ''; } diff --git a/app/src/lib/flows/types.ts b/app/src/lib/flows/types.ts index 7851c9f9f..704eca20d 100644 --- a/app/src/lib/flows/types.ts +++ b/app/src/lib/flows/types.ts @@ -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`. diff --git a/app/src/lib/i18n/ar.ts b/app/src/lib/i18n/ar.ts index 7e49f8021..e2806cacb 100644 --- a/app/src/lib/i18n/ar.ts +++ b/app/src/lib/i18n/ar.ts @@ -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': 'اختر كيف تريد أن تبدأ.', diff --git a/app/src/lib/i18n/bn.ts b/app/src/lib/i18n/bn.ts index d070aef6e..4918567ff 100644 --- a/app/src/lib/i18n/bn.ts +++ b/app/src/lib/i18n/bn.ts @@ -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': 'আপনি কীভাবে শুরু করতে চান তা বেছে নিন।', diff --git a/app/src/lib/i18n/de.ts b/app/src/lib/i18n/de.ts index a18f98ba1..3a3a0c900 100644 --- a/app/src/lib/i18n/de.ts +++ b/app/src/lib/i18n/de.ts @@ -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.', diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts index 448a30fd1..88638139e 100644 --- a/app/src/lib/i18n/en.ts +++ b/app/src/lib/i18n/en.ts @@ -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 diff --git a/app/src/lib/i18n/es.ts b/app/src/lib/i18n/es.ts index 5318855fa..d06f9f4c7 100644 --- a/app/src/lib/i18n/es.ts +++ b/app/src/lib/i18n/es.ts @@ -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.', diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts index f1b393601..5e485f507 100644 --- a/app/src/lib/i18n/fr.ts +++ b/app/src/lib/i18n/fr.ts @@ -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.', diff --git a/app/src/lib/i18n/hi.ts b/app/src/lib/i18n/hi.ts index d61df65f2..a5ff61712 100644 --- a/app/src/lib/i18n/hi.ts +++ b/app/src/lib/i18n/hi.ts @@ -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': 'चुनें कि आप कैसे शुरू करना चाहते हैं।', diff --git a/app/src/lib/i18n/id.ts b/app/src/lib/i18n/id.ts index 8942170e4..b56586988 100644 --- a/app/src/lib/i18n/id.ts +++ b/app/src/lib/i18n/id.ts @@ -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.', diff --git a/app/src/lib/i18n/it.ts b/app/src/lib/i18n/it.ts index 647ba4720..fb444c5be 100644 --- a/app/src/lib/i18n/it.ts +++ b/app/src/lib/i18n/it.ts @@ -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.', diff --git a/app/src/lib/i18n/ko.ts b/app/src/lib/i18n/ko.ts index 8f98a3237..02cb83784 100644 --- a/app/src/lib/i18n/ko.ts +++ b/app/src/lib/i18n/ko.ts @@ -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': '시작 방법을 선택하세요.', diff --git a/app/src/lib/i18n/pl.ts b/app/src/lib/i18n/pl.ts index 679f2f734..1461f8e6b 100644 --- a/app/src/lib/i18n/pl.ts +++ b/app/src/lib/i18n/pl.ts @@ -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ąć.', diff --git a/app/src/lib/i18n/pt.ts b/app/src/lib/i18n/pt.ts index ee442f7b6..cd4a3e57e 100644 --- a/app/src/lib/i18n/pt.ts +++ b/app/src/lib/i18n/pt.ts @@ -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.', diff --git a/app/src/lib/i18n/ru.ts b/app/src/lib/i18n/ru.ts index 6bb4c5593..3d3e9ca2e 100644 --- a/app/src/lib/i18n/ru.ts +++ b/app/src/lib/i18n/ru.ts @@ -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': 'Выберите, с чего начать.', diff --git a/app/src/lib/i18n/zh-CN.ts b/app/src/lib/i18n/zh-CN.ts index 8aaea9009..70d1b6e7e 100644 --- a/app/src/lib/i18n/zh-CN.ts +++ b/app/src/lib/i18n/zh-CN.ts @@ -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': '选择你想要的开始方式。',