@things-factory/ai-assistant 10.1.97 → 10.1.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/service/data-entry/entry-tools.js +6 -2
- package/dist-server/service/data-entry/entry-tools.js.map +1 -1
- package/dist-server/service/workspace/workspace-blocks.js +9 -1
- package/dist-server/service/workspace/workspace-blocks.js.map +1 -1
- package/dist-server/service/workspace/workspace-tools.js +43 -1
- package/dist-server/service/workspace/workspace-tools.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/server/service/data-entry/data-entry-update.test.ts +15 -0
- package/server/service/data-entry/entry-tools.ts +6 -2
- package/server/service/workspace/workspace-blocks.ts +8 -1
- package/server/service/workspace/workspace-tools.ts +43 -1
- package/server/service/workspace/workspace.test.ts +16 -0
|
@@ -50,7 +50,11 @@ const ai_client_base_1 = require("@things-factory/ai-client-base");
|
|
|
50
50
|
const entry_update_js_1 = require("./entry-update.js");
|
|
51
51
|
const entry_target_js_1 = require("./entry-target.js");
|
|
52
52
|
function fieldSchema(field) {
|
|
53
|
-
|
|
53
|
+
/*
|
|
54
|
+
* One type per field - Gemini's function schema (an OpenAPI 3.0 subset) refuses a list of types, and the
|
|
55
|
+
* whole request with it: "12,400" or "₩12,400" is the model's to write as 12400 (2026-10-01, office).
|
|
56
|
+
*/
|
|
57
|
+
const type = field.type === 'number' ? 'number' : field.type === 'integer' ? 'integer' : field.type === 'boolean' ? 'boolean' : 'string';
|
|
54
58
|
const said = [
|
|
55
59
|
field.description,
|
|
56
60
|
field.required ? 'required' : 'optional',
|
|
@@ -213,7 +217,7 @@ function entryCategoryOf(target, run = exports.runAsCaller) {
|
|
|
213
217
|
description: 'The total the material itself prints, when it prints one - the card compares it with the sum of the rows. Never compute it yourself.',
|
|
214
218
|
properties: {
|
|
215
219
|
field: { type: 'string', description: `Which field it totals${target.fields.filter(field => field.total).length ? ` (${target.fields.filter(field => field.total).map(field => field.name).join(', ')})` : ''}.` },
|
|
216
|
-
value: { type:
|
|
220
|
+
value: { type: 'number' },
|
|
217
221
|
attachmentId: { type: 'string' },
|
|
218
222
|
where: { type: 'string' }
|
|
219
223
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry-tools.js","sourceRoot":"","sources":["../../../server/service/data-entry/entry-tools.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The propose tool of each entry target (operato-application/design/ai/data-entry.md §3).\n *\n * One tool per target, so each carries its own door: the tool is offered only to a person who could enter\n * those records by hand, and its card cannot be made for someone who could not press it.\n *\n * The tool writes nothing. It reads the rows the model made from the material, puts each value in its\n * field's type, asks the target's own checks, and hands back a proposal - the card.\n */\nimport { registerToolCategory, type ToolCategory, type ToolSpec } from '@things-factory/ai-client-base'\n\nimport { findRecords, proposeChanges, changeableFields, type GraphqlRunner } from './entry-update.js'\nimport {\n changeToolName,\n findToolName,\n rootFieldOf,\n declareEntryTarget,\n entryTotals,\n verifyRowsAgainstSources,\n entryCategoryName,\n entryDoorOf,\n entryToolName,\n isBlocked,\n normalizeEntryRows,\n recordEntryEvent,\n withCheckedIssues,\n type EntryField,\n type EntryRow,\n type EntryTarget\n} from './entry-target.js'\n\nfunction fieldSchema(field: EntryField): any {\n const type = field.type === 'number' || field.type === 'integer' ? ['number', 'string'] : field.type === 'boolean' ? ['boolean', 'string'] : 'string'\n const said = [\n field.description,\n field.required ? 'required' : 'optional',\n field.type === 'date' ? 'YYYY-MM-DD' : undefined,\n field.type === 'enum' ? `one of: ${field.values!.join(', ')}` : undefined\n ]\n .filter(Boolean)\n .join('. ')\n return { type, description: said }\n}\n\n/** What the card needs besides the rows - the columns, in the target's order. */\nfunction columnsOf(target: EntryTarget) {\n return target.fields.map(field => ({ name: field.name, type: field.type, required: !!field.required, ...(field.labelKey ? { labelKey: field.labelKey } : {}) }))\n}\n\nexport type SourceTextLoader = (attachmentIds: string[], context: any) => Promise<Map<string, string>>\n\nconst readSources: SourceTextLoader = async (ids, context) => (await import('./entry-sources.js')).loadSourceTexts(ids, context)\n\nexport async function proposeEntries(target: EntryTarget, args: any, context: any, loadTexts: SourceTextLoader = readSources) {\n const tool = entryToolName(target.key)\n const told = (event: Omit<Parameters<typeof recordEntryEvent>[1], 'phase' | 'tool'>) => recordEntryEvent(target, { phase: 'propose', tool, ...event }, { state: context?.state })\n const { rows: normalized, cut } = normalizeEntryRows(target, args?.rows)\n if (!normalized.length) {\n await told({ rows: 0, ok: false, error: 'no-rows', detail: args })\n return { rejected: true, error: 'no-rows', message: 'no rows were given. Read the material and pass every record as a row.' }\n }\n\n /* The server looks in the cited files itself - the model's citation is not proof (design/ai/data-entry.md §4-1). */\n const declared = args?.declaredTotal && typeof args.declaredTotal === 'object' ? args.declaredTotal : undefined\n const cited = [...normalized.flatMap(row => row.sources.map(source => source.attachmentId)), declared?.attachmentId].filter((id): id is string => typeof id === 'string' && !!id)\n const texts = cited.length ? await loadTexts(cited, context).catch(() => new Map<string, string>()) : new Map<string, string>()\n const read = verifyRowsAgainstSources(normalized, target.fields, texts)\n\n let rows: EntryRow[] = read\n let checkFailed: string | undefined\n if (target.check) {\n try {\n rows = withCheckedIssues(read, await target.check(read, { state: context?.state }))\n } catch (error: any) {\n checkFailed = error?.message || 'check-failed'\n }\n }\n\n const blocked = rows.filter(isBlocked).length\n const totals = entryTotals(rows, target.fields, declared, texts)\n const verified = rows.filter(row => row.evidence).length\n const unmatched = rows.filter(row => row.issues.some(issue => issue.code === 'not-in-source')).length\n await told({ rows: rows.length, ok: true, detail: args })\n return {\n proposed: true,\n kind: 'entry',\n target: target.key,\n labelKey: target.labelKey,\n label: `${rows.length} ${target.key} rows`,\n icon: 'playlist_add',\n columns: columnsOf(target),\n rows,\n counts: { rows: rows.length, ready: rows.length - blocked, blocked, cut, verified, unmatched },\n ...(totals.length ? { totals } : {}),\n ...(typeof args?.note === 'string' && args.note.trim() ? { reason: args.note.trim().slice(0, 300) } : {}),\n ...(checkFailed ? { checkFailed } : {}),\n /* For the model - the card is the answer; it says what the person must still decide. */\n message:\n `proposed ${rows.length} rows (${rows.length - blocked} ready, ${blocked} blocked${cut ? `, ${cut} cut over the limit` : ''}; ` +\n `${verified} found line by line in their files, ${unmatched} with a value the file does not have). ` +\n totals.filter(total => total.difference).map(total => `${total.field}: the material's total differs from the rows by ${total.difference}. `).join('') +\n 'nothing is written until the person presses the button on the card. Tell them which rows are blocked or do not match their file, and why, and ask for what is missing.'\n }\n}\n\n/**\n * The application's API as the person who asked - the tool's own request state, no transaction handed on.\n * Loaded late so the rules are tested without the shell.\n */\nexport const runAsCaller = (context: any): GraphqlRunner => async (document, variables) => {\n const [{ GraphqlLocalClient }, { default: gql }] = await Promise.all([import('@things-factory/shell'), import('graphql-tag')])\n const { tx: _tx, afterCommit: _after, ...state } = context?.state ?? {}\n const operation = /^\\s*mutation\\b/.test(document) ? 'mutate' : 'query'\n const result: any =\n operation === 'mutate'\n ? await GraphqlLocalClient.client.mutate({ mutation: gql(document), variables, context: { state, t: context?.t } })\n : await GraphqlLocalClient.client.query({ query: gql(document), variables, context: { state, t: context?.t }, fetchPolicy: 'no-cache' })\n return { data: result.data, errors: result.errors }\n}\n\n/** The two tools of changing records - finding candidates, and the 수정안 (design/ai/data-entry.md §5-1). */\nfunction changeSpecs(target: EntryTarget, run: (context: any) => GraphqlRunner): ToolSpec[] {\n const update = target.update\n if (!update) return []\n const readDoor = rootFieldOf(update.read.query, 'query')\n const specs: ToolSpec[] = [\n {\n kind: 'read',\n name: changeToolName(target.key),\n /* The person must be able to read the records and change them by hand. */\n doors: [rootFieldOf(update.mutation, 'mutation'), readDoor],\n description:\n `Propose changes to existing records: ${target.description} ` +\n 'Name each record by ref: \"current\" (the one on screen), \"selected\" (every row selected on screen) or an id a find tool returned. ' +\n 'The server reads each record, and the card shows what it holds now and what it becomes; nothing changes until the person presses it.',\n schema: {\n type: 'object',\n properties: {\n changes: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n ref: { type: 'string', description: '\"current\", \"selected\", or a record id from a find tool - never a guessed id.' },\n values: { type: 'object', description: 'Only the fields that change, with their new values.', properties: Object.fromEntries(changeableFields(target).map(field => [field.name, fieldSchema(field)])) }\n },\n required: ['ref', 'values']\n }\n },\n note: { type: 'string', description: 'One line for the card.' }\n },\n required: ['changes']\n },\n builder: (args: any, context: any) => proposeChanges(target, args, context, run(context))\n } as ToolSpec\n ]\n if (update.find) {\n specs.push({\n kind: 'read',\n name: findToolName(target.key),\n doors: [rootFieldOf(update.find.query, 'query')],\n description: `Find existing records to change from what the person described: ${target.description} Returns candidates with their ids.`,\n schema: { type: 'object', properties: Object.fromEntries(update.find.criteria.map(field => [field.name, fieldSchema(field)])) },\n builder: (args: any, context: any) => findRecords(target, args, run(context))\n } as ToolSpec)\n }\n return specs\n}\n\nexport function entryCategoryOf(target: EntryTarget, run: (context: any) => GraphqlRunner = runAsCaller): ToolCategory {\n const name = entryToolName(target.key)\n const spec = {\n kind: 'read',\n name,\n /* The person must be able to enter these records themselves - the tool is not a second way in. */\n doors: [entryDoorOf(target.mutation)],\n description:\n `Propose records to enter: ${target.description} ` +\n 'Pass every record found in the material as one row. The rows are shown to the person as a card; nothing is written until they press it.',\n schema: {\n type: 'object',\n properties: {\n rows: {\n type: 'array',\n description: `At most ${target.maxRows ?? 200} rows.`,\n items: {\n type: 'object',\n properties: {\n values: {\n type: 'object',\n properties: Object.fromEntries(target.fields.map(field => [field.name, fieldSchema(field)]))\n },\n sources: {\n type: 'array',\n description: 'Where this row came from: the attachment id and where in it (\"row 3\", \"the receipt photo\"), or where: \"the person\\'s message\".',\n items: { type: 'object', properties: { attachmentId: { type: 'string' }, where: { type: 'string' } } }\n }\n },\n required: ['values', 'sources']\n }\n },\n note: { type: 'string', description: 'One line for the card - what this set is (\"September card statement\").' },\n declaredTotal: {\n type: 'object',\n description: 'The total the material itself prints, when it prints one - the card compares it with the sum of the rows. Never compute it yourself.',\n properties: {\n field: { type: 'string', description: `Which field it totals${target.fields.filter(field => field.total).length ? ` (${target.fields.filter(field => field.total).map(field => field.name).join(', ')})` : ''}.` },\n value: { type: ['number', 'string'] },\n attachmentId: { type: 'string' },\n where: { type: 'string' }\n }\n }\n },\n required: ['rows']\n },\n builder: (args: any, context: any) => proposeEntries(target, args, context)\n } as ToolSpec\n\n return {\n name: entryCategoryName(target.key),\n description: `data entry: ${target.key}`,\n guidance: [\n `- When the person gives material to enter as records (files, photos, pasted text), read all of it first - call readChatAttachment for a file sent in an earlier turn - then call ${name} once with every record as a row.`,\n '- Never invent a value. Leave a field out when the material does not say it; the card marks it, and you ask the person for it.',\n '- Give each row its sources: which attachment and where in it. A value the person gave or corrected in the conversation adds the source where: \"the person\\'s message\". The server looks for each row\\'s numbers and dates in the cited file and marks what it does not find.',\n '- When the material prints a total, pass it as declaredTotal exactly as printed - do not add it up yourself.',\n `- ${name} only proposes. Nothing is entered until the person presses the button on the card - never say the records were entered.`,\n `- When the person corrects or drops rows, call ${name} again with the whole corrected set. The newest card replaces the earlier ones; an earlier card can no longer be entered.`,\n ...(target.update\n ? [\n `- To change records that exist, call ${changeToolName(target.key)}. Use ref \"current\" for the record on screen and \"selected\" for the rows selected there.${target.update.find ? ` Otherwise call ${findToolName(target.key)} first with what the person said; if more than one record matches, list them and ask which - never pick one yourself.` : ' Otherwise ask the person to open or select the record.'}`,\n `- ${changeToolName(target.key)} only proposes; the card shows each record's value now and after. Never say a record was changed.`\n ]\n : [])\n ].join('\\n'),\n specs: [spec, ...changeSpecs(target, run)]\n }\n}\n\n/**\n * What an application calls at boot to let the assistant enter one of its records.\n *\n * A mode opens it by naming the key in `entryTargets` (the tool is not in every conversation of the app).\n */\nexport function registerEntryTarget(target: EntryTarget): void {\n declareEntryTarget(target)\n registerToolCategory(entryCategoryOf(target))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"entry-tools.js","sourceRoot":"","sources":["../../../server/service/data-entry/entry-tools.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The propose tool of each entry target (operato-application/design/ai/data-entry.md §3).\n *\n * One tool per target, so each carries its own door: the tool is offered only to a person who could enter\n * those records by hand, and its card cannot be made for someone who could not press it.\n *\n * The tool writes nothing. It reads the rows the model made from the material, puts each value in its\n * field's type, asks the target's own checks, and hands back a proposal - the card.\n */\nimport { registerToolCategory, type ToolCategory, type ToolSpec } from '@things-factory/ai-client-base'\n\nimport { findRecords, proposeChanges, changeableFields, type GraphqlRunner } from './entry-update.js'\nimport {\n changeToolName,\n findToolName,\n rootFieldOf,\n declareEntryTarget,\n entryTotals,\n verifyRowsAgainstSources,\n entryCategoryName,\n entryDoorOf,\n entryToolName,\n isBlocked,\n normalizeEntryRows,\n recordEntryEvent,\n withCheckedIssues,\n type EntryField,\n type EntryRow,\n type EntryTarget\n} from './entry-target.js'\n\nfunction fieldSchema(field: EntryField): any {\n /*\n * One type per field - Gemini's function schema (an OpenAPI 3.0 subset) refuses a list of types, and the\n * whole request with it: \"12,400\" or \"₩12,400\" is the model's to write as 12400 (2026-10-01, office).\n */\n const type = field.type === 'number' ? 'number' : field.type === 'integer' ? 'integer' : field.type === 'boolean' ? 'boolean' : 'string'\n const said = [\n field.description,\n field.required ? 'required' : 'optional',\n field.type === 'date' ? 'YYYY-MM-DD' : undefined,\n field.type === 'enum' ? `one of: ${field.values!.join(', ')}` : undefined\n ]\n .filter(Boolean)\n .join('. ')\n return { type, description: said }\n}\n\n/** What the card needs besides the rows - the columns, in the target's order. */\nfunction columnsOf(target: EntryTarget) {\n return target.fields.map(field => ({ name: field.name, type: field.type, required: !!field.required, ...(field.labelKey ? { labelKey: field.labelKey } : {}) }))\n}\n\nexport type SourceTextLoader = (attachmentIds: string[], context: any) => Promise<Map<string, string>>\n\nconst readSources: SourceTextLoader = async (ids, context) => (await import('./entry-sources.js')).loadSourceTexts(ids, context)\n\nexport async function proposeEntries(target: EntryTarget, args: any, context: any, loadTexts: SourceTextLoader = readSources) {\n const tool = entryToolName(target.key)\n const told = (event: Omit<Parameters<typeof recordEntryEvent>[1], 'phase' | 'tool'>) => recordEntryEvent(target, { phase: 'propose', tool, ...event }, { state: context?.state })\n const { rows: normalized, cut } = normalizeEntryRows(target, args?.rows)\n if (!normalized.length) {\n await told({ rows: 0, ok: false, error: 'no-rows', detail: args })\n return { rejected: true, error: 'no-rows', message: 'no rows were given. Read the material and pass every record as a row.' }\n }\n\n /* The server looks in the cited files itself - the model's citation is not proof (design/ai/data-entry.md §4-1). */\n const declared = args?.declaredTotal && typeof args.declaredTotal === 'object' ? args.declaredTotal : undefined\n const cited = [...normalized.flatMap(row => row.sources.map(source => source.attachmentId)), declared?.attachmentId].filter((id): id is string => typeof id === 'string' && !!id)\n const texts = cited.length ? await loadTexts(cited, context).catch(() => new Map<string, string>()) : new Map<string, string>()\n const read = verifyRowsAgainstSources(normalized, target.fields, texts)\n\n let rows: EntryRow[] = read\n let checkFailed: string | undefined\n if (target.check) {\n try {\n rows = withCheckedIssues(read, await target.check(read, { state: context?.state }))\n } catch (error: any) {\n checkFailed = error?.message || 'check-failed'\n }\n }\n\n const blocked = rows.filter(isBlocked).length\n const totals = entryTotals(rows, target.fields, declared, texts)\n const verified = rows.filter(row => row.evidence).length\n const unmatched = rows.filter(row => row.issues.some(issue => issue.code === 'not-in-source')).length\n await told({ rows: rows.length, ok: true, detail: args })\n return {\n proposed: true,\n kind: 'entry',\n target: target.key,\n labelKey: target.labelKey,\n label: `${rows.length} ${target.key} rows`,\n icon: 'playlist_add',\n columns: columnsOf(target),\n rows,\n counts: { rows: rows.length, ready: rows.length - blocked, blocked, cut, verified, unmatched },\n ...(totals.length ? { totals } : {}),\n ...(typeof args?.note === 'string' && args.note.trim() ? { reason: args.note.trim().slice(0, 300) } : {}),\n ...(checkFailed ? { checkFailed } : {}),\n /* For the model - the card is the answer; it says what the person must still decide. */\n message:\n `proposed ${rows.length} rows (${rows.length - blocked} ready, ${blocked} blocked${cut ? `, ${cut} cut over the limit` : ''}; ` +\n `${verified} found line by line in their files, ${unmatched} with a value the file does not have). ` +\n totals.filter(total => total.difference).map(total => `${total.field}: the material's total differs from the rows by ${total.difference}. `).join('') +\n 'nothing is written until the person presses the button on the card. Tell them which rows are blocked or do not match their file, and why, and ask for what is missing.'\n }\n}\n\n/**\n * The application's API as the person who asked - the tool's own request state, no transaction handed on.\n * Loaded late so the rules are tested without the shell.\n */\nexport const runAsCaller = (context: any): GraphqlRunner => async (document, variables) => {\n const [{ GraphqlLocalClient }, { default: gql }] = await Promise.all([import('@things-factory/shell'), import('graphql-tag')])\n const { tx: _tx, afterCommit: _after, ...state } = context?.state ?? {}\n const operation = /^\\s*mutation\\b/.test(document) ? 'mutate' : 'query'\n const result: any =\n operation === 'mutate'\n ? await GraphqlLocalClient.client.mutate({ mutation: gql(document), variables, context: { state, t: context?.t } })\n : await GraphqlLocalClient.client.query({ query: gql(document), variables, context: { state, t: context?.t }, fetchPolicy: 'no-cache' })\n return { data: result.data, errors: result.errors }\n}\n\n/** The two tools of changing records - finding candidates, and the 수정안 (design/ai/data-entry.md §5-1). */\nfunction changeSpecs(target: EntryTarget, run: (context: any) => GraphqlRunner): ToolSpec[] {\n const update = target.update\n if (!update) return []\n const readDoor = rootFieldOf(update.read.query, 'query')\n const specs: ToolSpec[] = [\n {\n kind: 'read',\n name: changeToolName(target.key),\n /* The person must be able to read the records and change them by hand. */\n doors: [rootFieldOf(update.mutation, 'mutation'), readDoor],\n description:\n `Propose changes to existing records: ${target.description} ` +\n 'Name each record by ref: \"current\" (the one on screen), \"selected\" (every row selected on screen) or an id a find tool returned. ' +\n 'The server reads each record, and the card shows what it holds now and what it becomes; nothing changes until the person presses it.',\n schema: {\n type: 'object',\n properties: {\n changes: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n ref: { type: 'string', description: '\"current\", \"selected\", or a record id from a find tool - never a guessed id.' },\n values: { type: 'object', description: 'Only the fields that change, with their new values.', properties: Object.fromEntries(changeableFields(target).map(field => [field.name, fieldSchema(field)])) }\n },\n required: ['ref', 'values']\n }\n },\n note: { type: 'string', description: 'One line for the card.' }\n },\n required: ['changes']\n },\n builder: (args: any, context: any) => proposeChanges(target, args, context, run(context))\n } as ToolSpec\n ]\n if (update.find) {\n specs.push({\n kind: 'read',\n name: findToolName(target.key),\n doors: [rootFieldOf(update.find.query, 'query')],\n description: `Find existing records to change from what the person described: ${target.description} Returns candidates with their ids.`,\n schema: { type: 'object', properties: Object.fromEntries(update.find.criteria.map(field => [field.name, fieldSchema(field)])) },\n builder: (args: any, context: any) => findRecords(target, args, run(context))\n } as ToolSpec)\n }\n return specs\n}\n\nexport function entryCategoryOf(target: EntryTarget, run: (context: any) => GraphqlRunner = runAsCaller): ToolCategory {\n const name = entryToolName(target.key)\n const spec = {\n kind: 'read',\n name,\n /* The person must be able to enter these records themselves - the tool is not a second way in. */\n doors: [entryDoorOf(target.mutation)],\n description:\n `Propose records to enter: ${target.description} ` +\n 'Pass every record found in the material as one row. The rows are shown to the person as a card; nothing is written until they press it.',\n schema: {\n type: 'object',\n properties: {\n rows: {\n type: 'array',\n description: `At most ${target.maxRows ?? 200} rows.`,\n items: {\n type: 'object',\n properties: {\n values: {\n type: 'object',\n properties: Object.fromEntries(target.fields.map(field => [field.name, fieldSchema(field)]))\n },\n sources: {\n type: 'array',\n description: 'Where this row came from: the attachment id and where in it (\"row 3\", \"the receipt photo\"), or where: \"the person\\'s message\".',\n items: { type: 'object', properties: { attachmentId: { type: 'string' }, where: { type: 'string' } } }\n }\n },\n required: ['values', 'sources']\n }\n },\n note: { type: 'string', description: 'One line for the card - what this set is (\"September card statement\").' },\n declaredTotal: {\n type: 'object',\n description: 'The total the material itself prints, when it prints one - the card compares it with the sum of the rows. Never compute it yourself.',\n properties: {\n field: { type: 'string', description: `Which field it totals${target.fields.filter(field => field.total).length ? ` (${target.fields.filter(field => field.total).map(field => field.name).join(', ')})` : ''}.` },\n value: { type: 'number' },\n attachmentId: { type: 'string' },\n where: { type: 'string' }\n }\n }\n },\n required: ['rows']\n },\n builder: (args: any, context: any) => proposeEntries(target, args, context)\n } as ToolSpec\n\n return {\n name: entryCategoryName(target.key),\n description: `data entry: ${target.key}`,\n guidance: [\n `- When the person gives material to enter as records (files, photos, pasted text), read all of it first - call readChatAttachment for a file sent in an earlier turn - then call ${name} once with every record as a row.`,\n '- Never invent a value. Leave a field out when the material does not say it; the card marks it, and you ask the person for it.',\n '- Give each row its sources: which attachment and where in it. A value the person gave or corrected in the conversation adds the source where: \"the person\\'s message\". The server looks for each row\\'s numbers and dates in the cited file and marks what it does not find.',\n '- When the material prints a total, pass it as declaredTotal exactly as printed - do not add it up yourself.',\n `- ${name} only proposes. Nothing is entered until the person presses the button on the card - never say the records were entered.`,\n `- When the person corrects or drops rows, call ${name} again with the whole corrected set. The newest card replaces the earlier ones; an earlier card can no longer be entered.`,\n ...(target.update\n ? [\n `- To change records that exist, call ${changeToolName(target.key)}. Use ref \"current\" for the record on screen and \"selected\" for the rows selected there.${target.update.find ? ` Otherwise call ${findToolName(target.key)} first with what the person said; if more than one record matches, list them and ask which - never pick one yourself.` : ' Otherwise ask the person to open or select the record.'}`,\n `- ${changeToolName(target.key)} only proposes; the card shows each record's value now and after. Never say a record was changed.`\n ]\n : [])\n ].join('\\n'),\n specs: [spec, ...changeSpecs(target, run)]\n }\n}\n\n/**\n * What an application calls at boot to let the assistant enter one of its records.\n *\n * A mode opens it by naming the key in `entryTargets` (the tool is not in every conversation of the app).\n */\nexport function registerEntryTarget(target: EntryTarget): void {\n declareEntryTarget(target)\n registerToolCategory(entryCategoryOf(target))\n}\n"]}
|
|
@@ -64,7 +64,15 @@ function block(raw) {
|
|
|
64
64
|
return unreadable(kind, 'no-columns');
|
|
65
65
|
const given = Array.isArray(raw.rows) ? raw.rows : [];
|
|
66
66
|
/* A row is an object by column name, or an array in column order - models write both. */
|
|
67
|
-
|
|
67
|
+
/* A number column reads "12,400" as 12400 - cells come as text from a model told one type per field. */
|
|
68
|
+
const cell = (column, value) => {
|
|
69
|
+
const read = scalar(value);
|
|
70
|
+
if (column.type !== 'number' || typeof read !== 'string')
|
|
71
|
+
return read;
|
|
72
|
+
const number = Number(read.replace(/[,\s₩$€]/g, ''));
|
|
73
|
+
return read.trim() && Number.isFinite(number) ? number : read;
|
|
74
|
+
};
|
|
75
|
+
const rows = given.slice(0, exports.MAX_TABLE_ROWS).map((row) => Object.fromEntries(columns.map((column, i) => [column.name, cell(column, Array.isArray(row) ? row[i] : row?.[column.name])])));
|
|
68
76
|
return { kind, columns, rows, ...(text(raw.caption, 300) ? { caption: text(raw.caption, 300) } : {}), ...(given.length > exports.MAX_TABLE_ROWS ? { cut: given.length - exports.MAX_TABLE_ROWS } : {}) };
|
|
69
77
|
}
|
|
70
78
|
case 'chart': {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-blocks.js","sourceRoot":"","sources":["../../../server/service/workspace/workspace-blocks.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The AI 작업판 - what a workspace is made of (operato-application/design/ai/workspace.md §3).\n *\n * The model does not write HTML. A workspace is a list of blocks of known kinds; the screen draws each, so\n * theme, language and accessibility are the framework's. A block that does not fit is not dropped: it is\n * kept as a fact in its place (\"a block that cannot be drawn\"), so the person sees that something was meant\n * to be there. Pure - tested without a server.\n */\n\nexport const WORKSPACE_BLOCK_KINDS = ['heading', 'text', 'metrics', 'table', 'chart', 'steps'] as const\nexport type WorkspaceBlockKind = (typeof WORKSPACE_BLOCK_KINDS)[number]\n\nexport const MAX_BLOCKS = 40\nexport const MAX_TABLE_ROWS = 1000\nexport const MAX_SERIES = 8\nconst MAX_TEXT = 20_000\n\nexport interface WorkspaceBlock {\n kind: WorkspaceBlockKind | 'unreadable'\n [key: string]: unknown\n}\n\nexport interface Workspace {\n name: string\n title: string\n summary?: string\n blocks: WorkspaceBlock[]\n}\n\nconst text = (value: unknown, max = 500): string | undefined => (typeof value === 'string' && value.trim() ? value.trim().slice(0, max) : undefined)\nconst scalar = (value: unknown): string | number | boolean | null =>\n value === null || value === undefined ? null : typeof value === 'number' || typeof value === 'boolean' ? value : String(value).slice(0, 500)\n\n/** A short key of the workspace - the same name in one conversation is the same workspace, a new version. */\nexport function workspaceName(name: unknown, title: string): string {\n const given = typeof name === 'string' ? name.trim().toLowerCase() : ''\n if (/^[a-z0-9][a-z0-9-]{0,63}$/.test(given)) return given\n const slug = (given || title)\n .toLowerCase()\n .replace(/[^a-z0-9가-힣]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 64)\n return slug || 'workspace'\n}\n\nfunction unreadable(kind: unknown, reason: string): WorkspaceBlock {\n return { kind: 'unreadable', was: typeof kind === 'string' ? kind.slice(0, 40) : null, reason }\n}\n\nfunction block(raw: any): WorkspaceBlock {\n const kind = raw?.kind\n switch (kind) {\n case 'heading': {\n const value = text(raw.text, 200)\n if (!value) return unreadable(kind, 'no-text')\n const level = [1, 2, 3].includes(raw.level) ? raw.level : 2\n return { kind, text: value, level }\n }\n case 'text': {\n const value = text(raw.markdown ?? raw.text, MAX_TEXT)\n return value ? { kind, markdown: value } : unreadable(kind, 'no-text')\n }\n case 'metrics': {\n const items = (Array.isArray(raw.items) ? raw.items : [])\n .filter((one: any) => text(one?.label) && one?.value !== undefined && one?.value !== null)\n .slice(0, 12)\n .map((one: any) => ({ label: text(one.label, 80), value: scalar(one.value), ...(text(one.unit, 20) ? { unit: text(one.unit, 20) } : {}), ...(text(one.note, 200) ? { note: text(one.note, 200) } : {}) }))\n return items.length ? { kind, items } : unreadable(kind, 'no-items')\n }\n case 'table': {\n const columns = (Array.isArray(raw.columns) ? raw.columns : [])\n .filter((one: any) => text(one?.name, 80))\n .slice(0, 30)\n .map((one: any) => ({ name: text(one.name, 80)!, ...(text(one.label, 80) ? { label: text(one.label, 80) } : {}), type: ['number', 'date', 'string'].includes(one.type) ? one.type : 'string' }))\n if (!columns.length) return unreadable(kind, 'no-columns')\n const given = Array.isArray(raw.rows) ? raw.rows : []\n /* A row is an object by column name, or an array in column order - models write both. */\n const rows = given.slice(0, MAX_TABLE_ROWS).map((row: any) =>\n Object.fromEntries(columns.map((column: any, i: number) => [column.name, scalar(Array.isArray(row) ? row[i] : row?.[column.name])]))\n )\n return { kind, columns, rows, ...(text(raw.caption, 300) ? { caption: text(raw.caption, 300) } : {}), ...(given.length > MAX_TABLE_ROWS ? { cut: given.length - MAX_TABLE_ROWS } : {}) }\n }\n case 'chart': {\n const x = (Array.isArray(raw.x) ? raw.x : []).slice(0, 200).map((one: unknown) => String(one ?? '').slice(0, 60))\n const series = (Array.isArray(raw.series) ? raw.series : [])\n .filter((one: any) => text(one?.name, 60) && Array.isArray(one?.values))\n .map((one: any) => ({ name: text(one.name, 60)!, values: x.map((_: unknown, i: number) => (typeof one.values[i] === 'number' && Number.isFinite(one.values[i]) ? one.values[i] : null)) }))\n if (!x.length || !series.length) return unreadable(kind, 'no-series')\n /* Never a ninth hue: past eight, the first seven stay and the rest fold into one \"other\". */\n const shown =\n series.length > MAX_SERIES\n ? [\n ...series.slice(0, MAX_SERIES - 1),\n { name: 'other', other: true, values: x.map((_: unknown, i: number) => series.slice(MAX_SERIES - 1).reduce((acc: number, one: any) => acc + (one.values[i] ?? 0), 0)) }\n ]\n : series\n return { kind, chart: raw.chart === 'line' || raw.type === 'line' || raw.shape === 'line' ? 'line' : 'bar', x, series: shown, ...(text(raw.unit, 20) ? { unit: text(raw.unit, 20) } : {}), ...(text(raw.caption, 300) ? { caption: text(raw.caption, 300) } : {}) }\n }\n case 'steps': {\n const items = (Array.isArray(raw.items) ? raw.items : [])\n .filter((one: any) => text(one?.label, 200))\n .slice(0, 50)\n .map((one: any) => ({ label: text(one.label, 200), state: ['done', 'doing', 'todo', 'failed'].includes(one.state) ? one.state : 'todo', ...(text(one.note, 300) ? { note: text(one.note, 300) } : {}) }))\n return items.length ? { kind, items } : unreadable(kind, 'no-items')\n }\n default:\n return unreadable(kind, 'unknown-kind')\n }\n}\n\n/** The workspace as the screen reads it. */\nexport function normalizeWorkspace(args: any): { workspace?: Workspace; error?: string } {\n const title = text(args?.title, 200)\n if (!title) return { error: 'no-title' }\n const given = Array.isArray(args?.blocks) ? args.blocks : []\n if (!given.length) return { error: 'no-blocks' }\n const blocks = given.slice(0, MAX_BLOCKS).map(block)\n if (given.length > MAX_BLOCKS) blocks.push({ kind: 'unreadable', was: null, reason: 'too-many-blocks', cut: given.length - MAX_BLOCKS })\n return { workspace: { name: workspaceName(args?.name, title), title, ...(text(args?.summary, 500) ? { summary: text(args.summary, 500) } : {}), blocks } }\n}\n\n/** One line of what is in it - for the card in the conversation and for the model. */\nexport function workspaceContents(workspace: Workspace): Record<string, number> {\n const counts: Record<string, number> = {}\n for (const one of workspace.blocks) if (['table', 'chart', 'metrics', 'steps'].includes(one.kind)) counts[one.kind] = (counts[one.kind] ?? 0) + 1\n return counts\n}\n"]}
|
|
1
|
+
{"version":3,"file":"workspace-blocks.js","sourceRoot":"","sources":["../../../server/service/workspace/workspace-blocks.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The AI 작업판 - what a workspace is made of (operato-application/design/ai/workspace.md §3).\n *\n * The model does not write HTML. A workspace is a list of blocks of known kinds; the screen draws each, so\n * theme, language and accessibility are the framework's. A block that does not fit is not dropped: it is\n * kept as a fact in its place (\"a block that cannot be drawn\"), so the person sees that something was meant\n * to be there. Pure - tested without a server.\n */\n\nexport const WORKSPACE_BLOCK_KINDS = ['heading', 'text', 'metrics', 'table', 'chart', 'steps'] as const\nexport type WorkspaceBlockKind = (typeof WORKSPACE_BLOCK_KINDS)[number]\n\nexport const MAX_BLOCKS = 40\nexport const MAX_TABLE_ROWS = 1000\nexport const MAX_SERIES = 8\nconst MAX_TEXT = 20_000\n\nexport interface WorkspaceBlock {\n kind: WorkspaceBlockKind | 'unreadable'\n [key: string]: unknown\n}\n\nexport interface Workspace {\n name: string\n title: string\n summary?: string\n blocks: WorkspaceBlock[]\n}\n\nconst text = (value: unknown, max = 500): string | undefined => (typeof value === 'string' && value.trim() ? value.trim().slice(0, max) : undefined)\nconst scalar = (value: unknown): string | number | boolean | null =>\n value === null || value === undefined ? null : typeof value === 'number' || typeof value === 'boolean' ? value : String(value).slice(0, 500)\n\n/** A short key of the workspace - the same name in one conversation is the same workspace, a new version. */\nexport function workspaceName(name: unknown, title: string): string {\n const given = typeof name === 'string' ? name.trim().toLowerCase() : ''\n if (/^[a-z0-9][a-z0-9-]{0,63}$/.test(given)) return given\n const slug = (given || title)\n .toLowerCase()\n .replace(/[^a-z0-9가-힣]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 64)\n return slug || 'workspace'\n}\n\nfunction unreadable(kind: unknown, reason: string): WorkspaceBlock {\n return { kind: 'unreadable', was: typeof kind === 'string' ? kind.slice(0, 40) : null, reason }\n}\n\nfunction block(raw: any): WorkspaceBlock {\n const kind = raw?.kind\n switch (kind) {\n case 'heading': {\n const value = text(raw.text, 200)\n if (!value) return unreadable(kind, 'no-text')\n const level = [1, 2, 3].includes(raw.level) ? raw.level : 2\n return { kind, text: value, level }\n }\n case 'text': {\n const value = text(raw.markdown ?? raw.text, MAX_TEXT)\n return value ? { kind, markdown: value } : unreadable(kind, 'no-text')\n }\n case 'metrics': {\n const items = (Array.isArray(raw.items) ? raw.items : [])\n .filter((one: any) => text(one?.label) && one?.value !== undefined && one?.value !== null)\n .slice(0, 12)\n .map((one: any) => ({ label: text(one.label, 80), value: scalar(one.value), ...(text(one.unit, 20) ? { unit: text(one.unit, 20) } : {}), ...(text(one.note, 200) ? { note: text(one.note, 200) } : {}) }))\n return items.length ? { kind, items } : unreadable(kind, 'no-items')\n }\n case 'table': {\n const columns = (Array.isArray(raw.columns) ? raw.columns : [])\n .filter((one: any) => text(one?.name, 80))\n .slice(0, 30)\n .map((one: any) => ({ name: text(one.name, 80)!, ...(text(one.label, 80) ? { label: text(one.label, 80) } : {}), type: ['number', 'date', 'string'].includes(one.type) ? one.type : 'string' }))\n if (!columns.length) return unreadable(kind, 'no-columns')\n const given = Array.isArray(raw.rows) ? raw.rows : []\n /* A row is an object by column name, or an array in column order - models write both. */\n /* A number column reads \"12,400\" as 12400 - cells come as text from a model told one type per field. */\n const cell = (column: any, value: unknown) => {\n const read = scalar(value)\n if (column.type !== 'number' || typeof read !== 'string') return read\n const number = Number(read.replace(/[,\\s₩$€]/g, ''))\n return read.trim() && Number.isFinite(number) ? number : read\n }\n const rows = given.slice(0, MAX_TABLE_ROWS).map((row: any) =>\n Object.fromEntries(columns.map((column: any, i: number) => [column.name, cell(column, Array.isArray(row) ? row[i] : row?.[column.name])]))\n )\n return { kind, columns, rows, ...(text(raw.caption, 300) ? { caption: text(raw.caption, 300) } : {}), ...(given.length > MAX_TABLE_ROWS ? { cut: given.length - MAX_TABLE_ROWS } : {}) }\n }\n case 'chart': {\n const x = (Array.isArray(raw.x) ? raw.x : []).slice(0, 200).map((one: unknown) => String(one ?? '').slice(0, 60))\n const series = (Array.isArray(raw.series) ? raw.series : [])\n .filter((one: any) => text(one?.name, 60) && Array.isArray(one?.values))\n .map((one: any) => ({ name: text(one.name, 60)!, values: x.map((_: unknown, i: number) => (typeof one.values[i] === 'number' && Number.isFinite(one.values[i]) ? one.values[i] : null)) }))\n if (!x.length || !series.length) return unreadable(kind, 'no-series')\n /* Never a ninth hue: past eight, the first seven stay and the rest fold into one \"other\". */\n const shown =\n series.length > MAX_SERIES\n ? [\n ...series.slice(0, MAX_SERIES - 1),\n { name: 'other', other: true, values: x.map((_: unknown, i: number) => series.slice(MAX_SERIES - 1).reduce((acc: number, one: any) => acc + (one.values[i] ?? 0), 0)) }\n ]\n : series\n return { kind, chart: raw.chart === 'line' || raw.type === 'line' || raw.shape === 'line' ? 'line' : 'bar', x, series: shown, ...(text(raw.unit, 20) ? { unit: text(raw.unit, 20) } : {}), ...(text(raw.caption, 300) ? { caption: text(raw.caption, 300) } : {}) }\n }\n case 'steps': {\n const items = (Array.isArray(raw.items) ? raw.items : [])\n .filter((one: any) => text(one?.label, 200))\n .slice(0, 50)\n .map((one: any) => ({ label: text(one.label, 200), state: ['done', 'doing', 'todo', 'failed'].includes(one.state) ? one.state : 'todo', ...(text(one.note, 300) ? { note: text(one.note, 300) } : {}) }))\n return items.length ? { kind, items } : unreadable(kind, 'no-items')\n }\n default:\n return unreadable(kind, 'unknown-kind')\n }\n}\n\n/** The workspace as the screen reads it. */\nexport function normalizeWorkspace(args: any): { workspace?: Workspace; error?: string } {\n const title = text(args?.title, 200)\n if (!title) return { error: 'no-title' }\n const given = Array.isArray(args?.blocks) ? args.blocks : []\n if (!given.length) return { error: 'no-blocks' }\n const blocks = given.slice(0, MAX_BLOCKS).map(block)\n if (given.length > MAX_BLOCKS) blocks.push({ kind: 'unreadable', was: null, reason: 'too-many-blocks', cut: given.length - MAX_BLOCKS })\n return { workspace: { name: workspaceName(args?.name, title), title, ...(text(args?.summary, 500) ? { summary: text(args.summary, 500) } : {}), blocks } }\n}\n\n/** One line of what is in it - for the card in the conversation and for the model. */\nexport function workspaceContents(workspace: Workspace): Record<string, number> {\n const counts: Record<string, number> = {}\n for (const one of workspace.blocks) if (['table', 'chart', 'metrics', 'steps'].includes(one.kind)) counts[one.kind] = (counts[one.kind] ?? 0) + 1\n return counts\n}\n"]}
|
|
@@ -41,9 +41,51 @@ const spec = {
|
|
|
41
41
|
name: { type: 'string', description: 'A short key, lowercase with dashes ("contracts-by-customer"). The same name again makes a new version of the same workspace.' },
|
|
42
42
|
title: { type: 'string' },
|
|
43
43
|
summary: { type: 'string', description: 'One line under the title.' },
|
|
44
|
+
/*
|
|
45
|
+
* Every field of every block is declared, each with one type: Gemini's function schema (an OpenAPI 3.0
|
|
46
|
+
* subset) refuses a list of types and may leave out a field it was not told of (2026-10-01).
|
|
47
|
+
*/
|
|
44
48
|
blocks: {
|
|
45
49
|
type: 'array',
|
|
46
|
-
items: {
|
|
50
|
+
items: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
53
|
+
kind: { type: 'string', enum: [...workspace_blocks_js_1.WORKSPACE_BLOCK_KINDS] },
|
|
54
|
+
text: { type: 'string', description: 'heading' },
|
|
55
|
+
level: { type: 'integer', description: 'heading: 1-3' },
|
|
56
|
+
markdown: { type: 'string', description: 'text' },
|
|
57
|
+
caption: { type: 'string', description: 'table, chart' },
|
|
58
|
+
unit: { type: 'string', description: 'chart' },
|
|
59
|
+
chart: { type: 'string', enum: ['bar', 'line'], description: 'chart' },
|
|
60
|
+
items: {
|
|
61
|
+
type: 'array',
|
|
62
|
+
description: 'metrics: {label,value,unit,note}; steps: {label,state,note}',
|
|
63
|
+
items: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
label: { type: 'string' },
|
|
67
|
+
value: { type: 'string' },
|
|
68
|
+
unit: { type: 'string' },
|
|
69
|
+
note: { type: 'string' },
|
|
70
|
+
state: { type: 'string', enum: ['done', 'doing', 'todo', 'failed'] }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
columns: {
|
|
75
|
+
type: 'array',
|
|
76
|
+
description: 'table',
|
|
77
|
+
items: { type: 'object', properties: { name: { type: 'string' }, label: { type: 'string' }, type: { type: 'string', enum: ['number', 'date', 'string'] } } }
|
|
78
|
+
},
|
|
79
|
+
rows: { type: 'array', description: 'table: each row is its cells in column order', items: { type: 'array', items: { type: 'string' } } },
|
|
80
|
+
x: { type: 'array', description: 'chart: the labels along the bottom', items: { type: 'string' } },
|
|
81
|
+
series: {
|
|
82
|
+
type: 'array',
|
|
83
|
+
description: 'chart',
|
|
84
|
+
items: { type: 'object', properties: { name: { type: 'string' }, values: { type: 'array', items: { type: 'number' } } } }
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
required: ['kind']
|
|
88
|
+
}
|
|
47
89
|
}
|
|
48
90
|
},
|
|
49
91
|
required: ['name', 'title', 'blocks']
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-tools.js","sourceRoot":"","sources":["../../../server/service/workspace/workspace-tools.ts"],"names":[],"mappings":";;;AAcA,0CAaC;AA3BD;;;;;;;GAOG;AACH,mEAAuG;AAEvG,+DAAoG;AAEvF,QAAA,kBAAkB,GAAG,qBAAqB,CAAA;AAEvD,SAAgB,eAAe,CAAC,IAAS;IACvC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAA,wCAAkB,EAAC,IAAI,CAAC,CAAA;IACrD,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,mDAAmD,EAAE,CAAA;IAC9G,MAAM,QAAQ,GAAG,IAAA,uCAAiB,EAAC,SAAS,CAAC,CAAA;IAC7C,OAAO;QACL,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,SAAS;QACT,QAAQ;QACR,OAAO,EAAE,QAAQ,SAAS,CAAC,KAAK,uBAAuB,SAAS,CAAC,MAAM,CAAC,MAAM,sHAAsH;KACrM,CAAA;AACH,CAAC;AAED,MAAM,IAAI,GAAG;IACX,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,EAAE;IACT,WAAW,EACT,iHAAiH;QACjH,qJAAqJ;QACrJ,+IAA+I;IACjJ,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8HAA8H,EAAE;YACrK,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,2BAA2B,EAAE;YACrE,MAAM,EAAE;gBACN,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"workspace-tools.js","sourceRoot":"","sources":["../../../server/service/workspace/workspace-tools.ts"],"names":[],"mappings":";;;AAcA,0CAaC;AA3BD;;;;;;;GAOG;AACH,mEAAuG;AAEvG,+DAAoG;AAEvF,QAAA,kBAAkB,GAAG,qBAAqB,CAAA;AAEvD,SAAgB,eAAe,CAAC,IAAS;IACvC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,IAAA,wCAAkB,EAAC,IAAI,CAAC,CAAA;IACrD,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,mDAAmD,EAAE,CAAA;IAC9G,MAAM,QAAQ,GAAG,IAAA,uCAAiB,EAAC,SAAS,CAAC,CAAA;IAC7C,OAAO;QACL,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,SAAS;QACT,QAAQ;QACR,OAAO,EAAE,QAAQ,SAAS,CAAC,KAAK,uBAAuB,SAAS,CAAC,MAAM,CAAC,MAAM,sHAAsH;KACrM,CAAA;AACH,CAAC;AAED,MAAM,IAAI,GAAG;IACX,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,EAAE;IACT,WAAW,EACT,iHAAiH;QACjH,qJAAqJ;QACrJ,+IAA+I;IACjJ,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8HAA8H,EAAE;YACrK,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,2BAA2B,EAAE;YACrE;;;eAGG;YACH,MAAM,EAAE;gBACN,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,2CAAqB,CAAC,EAAE;wBAC1D,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE;wBAChD,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE;wBACvD,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE;wBACjD,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE;wBACxD,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE;wBAC9C,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE;wBACtE,KAAK,EAAE;4BACL,IAAI,EAAE,OAAO;4BACb,WAAW,EAAE,6DAA6D;4BAC1E,KAAK,EAAE;gCACL,IAAI,EAAE,QAAQ;gCACd,UAAU,EAAE;oCACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oCACzB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oCACzB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oCACxB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oCACxB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE;iCACrE;6BACF;yBACF;wBACD,OAAO,EAAE;4BACP,IAAI,EAAE,OAAO;4BACb,WAAW,EAAE,OAAO;4BACpB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE;yBAC7J;wBACD,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,8CAA8C,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;wBACzI,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,oCAAoC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;wBAClG,MAAM,EAAE;4BACN,IAAI,EAAE,OAAO;4BACb,WAAW,EAAE,OAAO;4BACpB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE;yBAC1H;qBACF;oBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;iBACnB;aACF;SACF;QACD,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC;KACtC;IACD,OAAO,EAAE,CAAC,IAAS,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;CAClC,CAAA;AAEA,QAAA,iBAAiB,GAAiB;IAC7C,IAAI,EAAE,0BAAkB;IACxB,WAAW,EAAE,sEAAsE;IACnF,QAAQ,EAAE;QACR,qNAAqN;QACrN,6GAA6G;QAC7G,6HAA6H;QAC7H,2FAA2F;KAC5F,CAAC,IAAI,CAAC,IAAI,CAAC;IACZ,KAAK,EAAE,CAAC,IAAI,CAAC;CACd,CAAA;AAED,IAAA,qCAAoB,EAAC,yBAAiB,CAAC,CAAA","sourcesContent":["/*\n * showInWorkspace - the model puts a large result on the AI 작업판 (operato-application/design/ai/workspace.md §5).\n *\n * The result rides on the answer as a proposal of kind 'workspace', so the conversation's gates hold for it:\n * who may read the conversation reads the workspace, and a value behind a door the reader cannot pass hides\n * it with the answer (ADR-0073). The tool itself reaches no door - what it carries came from the turn's other\n * tools, whose doors the answer row collects.\n */\nimport { registerToolCategory, type ToolCategory, type ToolSpec } from '@things-factory/ai-client-base'\n\nimport { normalizeWorkspace, workspaceContents, WORKSPACE_BLOCK_KINDS } from './workspace-blocks.js'\n\nexport const WORKSPACE_CATEGORY = 'assistant-workspace'\n\nexport function showInWorkspace(args: any) {\n const { workspace, error } = normalizeWorkspace(args)\n if (!workspace) return { rejected: true, error, message: 'a workspace needs a title and at least one block.' }\n const contents = workspaceContents(workspace)\n return {\n proposed: true,\n kind: 'workspace',\n icon: 'dashboard',\n label: workspace.title,\n workspace,\n contents,\n message: `put \"${workspace.title}\" on the workspace (${workspace.blocks.length} blocks). The person opens it from the card - say in one line what it holds; do not repeat its tables in the answer.`\n }\n}\n\nconst spec = {\n kind: 'read',\n name: 'showInWorkspace',\n doors: [],\n description:\n 'Put a large result on the AI workspace - a page the person opens from a card, beside the conversation. Blocks: ' +\n 'heading{text,level}, text{markdown}, metrics{items:[{label,value,unit,note}]}, table{columns:[{name,label,type:number|date|string}],rows,caption}, ' +\n 'chart{chart:bar|line, x:[labels], series:[{name,values:[numbers]}], unit, caption}, steps{items:[{label,state:done|doing|todo|failed,note}]}.',\n schema: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'A short key, lowercase with dashes (\"contracts-by-customer\"). The same name again makes a new version of the same workspace.' },\n title: { type: 'string' },\n summary: { type: 'string', description: 'One line under the title.' },\n /*\n * Every field of every block is declared, each with one type: Gemini's function schema (an OpenAPI 3.0\n * subset) refuses a list of types and may leave out a field it was not told of (2026-10-01).\n */\n blocks: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n kind: { type: 'string', enum: [...WORKSPACE_BLOCK_KINDS] },\n text: { type: 'string', description: 'heading' },\n level: { type: 'integer', description: 'heading: 1-3' },\n markdown: { type: 'string', description: 'text' },\n caption: { type: 'string', description: 'table, chart' },\n unit: { type: 'string', description: 'chart' },\n chart: { type: 'string', enum: ['bar', 'line'], description: 'chart' },\n items: {\n type: 'array',\n description: 'metrics: {label,value,unit,note}; steps: {label,state,note}',\n items: {\n type: 'object',\n properties: {\n label: { type: 'string' },\n value: { type: 'string' },\n unit: { type: 'string' },\n note: { type: 'string' },\n state: { type: 'string', enum: ['done', 'doing', 'todo', 'failed'] }\n }\n }\n },\n columns: {\n type: 'array',\n description: 'table',\n items: { type: 'object', properties: { name: { type: 'string' }, label: { type: 'string' }, type: { type: 'string', enum: ['number', 'date', 'string'] } } }\n },\n rows: { type: 'array', description: 'table: each row is its cells in column order', items: { type: 'array', items: { type: 'string' } } },\n x: { type: 'array', description: 'chart: the labels along the bottom', items: { type: 'string' } },\n series: {\n type: 'array',\n description: 'chart',\n items: { type: 'object', properties: { name: { type: 'string' }, values: { type: 'array', items: { type: 'number' } } } }\n }\n },\n required: ['kind']\n }\n }\n },\n required: ['name', 'title', 'blocks']\n },\n builder: (args: any) => showInWorkspace(args)\n} as ToolSpec\n\nexport const workspaceCategory: ToolCategory = {\n name: WORKSPACE_CATEGORY,\n description: 'the AI workspace - a page for results too large for the conversation',\n guidance: [\n '- When a result is more than a short table (over 20 rows, or two or more tables or charts), or the person asks for a page, call showInWorkspace. Keep the answer short: the point, and that it is on the workspace.',\n '- Every number in a block comes from a tool result in this conversation - never computed or guessed by you.',\n '- To change a workspace, call showInWorkspace again with the same name and the whole new content; it becomes a new version.',\n '- Never say something is on the workspace unless you called showInWorkspace in this turn.'\n ].join('\\n'),\n specs: [spec]\n}\n\nregisterToolCategory(workspaceCategory)\n"]}
|