@things-factory/ai-assistant 10.1.205 → 10.1.207
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/client/components/assistant-chat.ts +2 -1
- package/client/components/chat-input-builder.test.ts +5 -0
- package/client/components/chat-input-builder.ts +4 -1
- package/dist-client/components/assistant-chat.js +2 -1
- package/dist-client/components/assistant-chat.js.map +1 -1
- package/dist-client/components/chat-input-builder.d.ts +2 -0
- package/dist-client/components/chat-input-builder.js +2 -1
- package/dist-client/components/chat-input-builder.js.map +1 -1
- package/dist-client/components/chat-input-builder.test.js +4 -0
- package/dist-client/components/chat-input-builder.test.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/assistant-chat-resolver.d.ts +1 -0
- package/dist-server/service/assistant-chat-resolver.js +20 -0
- package/dist-server/service/assistant-chat-resolver.js.map +1 -1
- package/dist-server/service/assistant-mode/mode-categories.d.ts +29 -0
- package/dist-server/service/assistant-mode/mode-categories.js +37 -1
- package/dist-server/service/assistant-mode/mode-categories.js.map +1 -1
- package/dist-server/service/assistant-mode/registry.js +20 -0
- package/dist-server/service/assistant-mode/registry.js.map +1 -1
- package/dist-server/service/assistant-mode/types.d.ts +6 -1
- package/dist-server/service/assistant-mode/types.js.map +1 -1
- package/dist-server/service/chat-message/used-tools.d.ts +11 -0
- package/dist-server/service/chat-message/used-tools.js +33 -0
- package/dist-server/service/chat-message/used-tools.js.map +1 -0
- package/dist-server/service/testing/mode-conformance.d.ts +4 -0
- package/dist-server/service/testing/mode-conformance.js +4 -0
- package/dist-server/service/testing/mode-conformance.js.map +1 -1
- package/dist-server/service/unattended-run/run-unattended.js +2 -0
- package/dist-server/service/unattended-run/run-unattended.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/server/service/assistant-chat-resolver.ts +21 -1
- package/server/service/assistant-mode/mode-categories.ts +47 -1
- package/server/service/assistant-mode/mode-front.test.ts +65 -0
- package/server/service/assistant-mode/open-subject-door.test.ts +16 -0
- package/server/service/assistant-mode/registry.test.ts +56 -0
- package/server/service/assistant-mode/registry.ts +20 -2
- package/server/service/assistant-mode/types.ts +14 -2
- package/server/service/chat-message/used-tools.test.ts +19 -0
- package/server/service/chat-message/used-tools.ts +25 -0
- package/server/service/testing/mode-conformance.ts +7 -1
- package/server/service/unattended-run/run-unattended.ts +2 -0
- package/test/assistant-dock.test.ts +1 -1
|
@@ -25,6 +25,7 @@ exports.openSubjectLineFor = openSubjectLineFor;
|
|
|
25
25
|
const ai_client_base_1 = require("@things-factory/ai-client-base");
|
|
26
26
|
const registry_js_1 = require("../assistant-mode/registry.js");
|
|
27
27
|
const resolve_js_1 = require("../assistant-mode/resolve.js");
|
|
28
|
+
const mode_categories_js_1 = require("../assistant-mode/mode-categories.js");
|
|
28
29
|
/** 시험용 번역기 — 키를 그대로 돌려준다. 문장이 나와야 할 자리에 키가 나오면 눈에 띈다. */
|
|
29
30
|
const keyEchoTranslate = key => key;
|
|
30
31
|
exports.keyEchoTranslate = keyEchoTranslate;
|
|
@@ -41,6 +42,8 @@ async function runRegisteredMode(kind, options = {}) {
|
|
|
41
42
|
messages: [{ role: 'user', content: '한 턴' }],
|
|
42
43
|
systemPrompt: settings.systemPrompt,
|
|
43
44
|
toolCategories: settings.toolCategories,
|
|
45
|
+
/* What the door puts in front on that screen (ADR-0061 decision 9) - a mode too large for one call is offered as a person meets it. */
|
|
46
|
+
front: { categories: (0, mode_categories_js_1.modeFrontCategories)((0, registry_js_1.getAssistantMode)(kind) ?? {}, options.screen ?? {}) },
|
|
44
47
|
requireToolOnFirstTurn: (requiredToolOnFirstTurn = Boolean(settings.requireToolOnFirstTurn)),
|
|
45
48
|
temperature: settings.temperature,
|
|
46
49
|
maxTokens: settings.maxTokens,
|
|
@@ -58,6 +61,7 @@ async function runRegisteredMode(kind, options = {}) {
|
|
|
58
61
|
});
|
|
59
62
|
return {
|
|
60
63
|
offeredTools: [...result.offeredTools],
|
|
64
|
+
openableTools: [...result.openableTools],
|
|
61
65
|
systemPrompt: settings.systemPrompt,
|
|
62
66
|
requiredToolOnFirstTurn,
|
|
63
67
|
reply: result.reply
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mode-conformance.js","sourceRoot":"","sources":["../../../server/service/testing/mode-conformance.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 등록된 모드가 **선언한 대로 도는가** — 글자가 아니라 문을 지나서 본다.\n *\n * 모드 선언은 소스에 적힌 값이고, 사람이 만나는 것은 그 값이 문을 지나 만들어 낸 것이다. 그 둘이\n * 어긋나는 자리가 오늘 하루에만 여럿 나왔다: 범주에 없는 도구를 프롬프트가 부르라고 했고, 키는 있는데\n * 번역이 없었고, 부품에 규칙이 있는데 부르는 쪽이 없었다. 전부 「선언은 맞는데 돌지 않는」 모양이다.\n *\n * 그래서 앱의 시험이 이것으로 **자기 모드를 실제로 돌려 본다**:\n * · 그 이름으로 모드가 서는가\n * · 그 모드가 여는 도구가 무엇인가 — 모델에게 실제로 건네지는 목록\n * · 첫 턴 도구 호출을 강제하는가\n * · 화면에 낼 말이 **키가 아니라 문장**으로 나오는가\n * · 슬래시가 가리키는 도구가 그 모드 안에 있는가\n */\nimport { openSubjectLine, registryAssistantChat } from '@things-factory/ai-client-base'\n\nimport { getAssistantMode, getAssistantModeWarnings } from '../assistant-mode/registry.js'\nimport { assistantModeView, resolveModeSettings, type Translate } from '../assistant-mode/resolve.js'\n\nexport interface ModeRunResult {\n /** 이 모드가 모델에게 실제로 건넨 도구 이름. */\n offeredTools: string[]\n /** 모델이 받은 시스템 프롬프트 — 서버의 것인지 확인할 수 있다. */\n systemPrompt: string\n /** 첫 턴에 도구를 강제했는가. */\n requiredToolOnFirstTurn: boolean\n reply: string\n}\n\n/** 시험용 번역기 — 키를 그대로 돌려준다. 문장이 나와야 할 자리에 키가 나오면 눈에 띈다. */\nexport const keyEchoTranslate: Translate = key => key\n\n/**\n * 등록된 모드로 한 턴을 돌린다 — 프롬프트도 도구도 **등록에서** 나온다.\n *\n * 시험이 자기 값을 넘기면 그 시험은 선언을 안 본 것이다. 그래서 여기서는 `kind` 만 받는다.\n */\nexport async function runRegisteredMode(\n kind: string,\n options: { client: any; hostContext?: any; state?: any; toolAccess?: any; reply?: string } = {} as any\n): Promise<ModeRunResult> {\n const settings = resolveModeSettings({ kind }, keyEchoTranslate)\n let requiredToolOnFirstTurn = false\n\n const result = await registryAssistantChat({\n client: options.client,\n messages: [{ role: 'user', content: '한 턴' }] as any,\n systemPrompt: settings.systemPrompt,\n toolCategories: settings.toolCategories,\n requireToolOnFirstTurn: (requiredToolOnFirstTurn = Boolean(settings.requireToolOnFirstTurn)),\n temperature: settings.temperature,\n maxTokens: settings.maxTokens,\n hostContext: options.hostContext,\n /*\n * This harness asks about **the mode** — \"what does this mode open\", not \"does this person pass the\n * doors\". Since the gate moved onto `doors` (ADR-0071 decision 2), passing no check closes every tool\n * that names a door, and an app's conformance test would quietly lose **exactly the tools that read\n * real data**. So the default is someone who passes every door; an app that wants the other case\n * writes `toolAccess`. The permission axis has its own tests (`tool-access.test.ts` ·\n * `tool-scope.test.ts`).\n */\n toolAccess: options.toolAccess ?? (async () => true),\n state: options.state ?? { domain: { id: 'dom-1' }, user: { id: 'u-1' } }\n })\n\n return {\n offeredTools: [...result.offeredTools],\n systemPrompt: settings.systemPrompt,\n requiredToolOnFirstTurn,\n reply: result.reply\n }\n}\n\nexport interface ModeWording {\n placeholder?: string\n /** 빈 대화의 제목과 한 줄 — 없으면 공용 대화창이 보드 제품의 글을 낸다. */\n intro?: { title: string; subtitle: string }\n /** 빈 대화의 예시 — 이 대화가 실제로 할 수 있는 말. */\n examples?: Array<{ label: string; items: string[] }>\n slash: { name: string; description: string; template: string }[]\n footerNotice?: string\n stagedNotice?: string\n}\n\n/** 화면에 낼 말 — 주어진 사전으로 옮긴 결과. 키가 그대로 남으면 사전에 없는 것이다. */\nexport function modeWording(kind: string, dictionary: Record<string, string>): ModeWording {\n const view = assistantModeView(kind, key => dictionary[key] ?? key)\n return {\n placeholder: view.placeholder,\n intro: view.intro,\n examples: view.examples,\n slash: view.slashTemplates,\n footerNotice: view.footerNotice,\n stagedNotice: view.stagedNotice\n }\n}\n\n/** 사전에 없어서 키가 그대로 화면에 나갈 말들. 비어 있어야 한다. */\nexport function missingWording(kind: string, dictionary: Record<string, string>): string[] {\n const missing: string[] = []\n const spec = getAssistantMode(kind)\n if (!spec) return [`mode:${kind}`]\n\n for (const key of [spec.placeholderKey, spec.footerNoticeKey, spec.stagedNoticeKey]) {\n if (key && !(key in dictionary)) missing.push(key)\n }\n /* 빈 대화의 말도 사전에 있어야 한다 — 없으면 화면에 키가 그대로 찍힌다. */\n for (const key of [spec.introKeys?.title, spec.introKeys?.subtitle]) {\n if (key && !(key in dictionary)) missing.push(key)\n }\n for (const group of spec.exampleKeys ?? []) {\n for (const key of [group.labelKey, ...group.itemKeys]) {\n if (!(key in dictionary)) missing.push(key)\n }\n }\n for (const slash of spec.slashTemplates ?? []) {\n for (const key of [slash.descriptionKey, slash.templateKey]) {\n if (!(key in dictionary)) missing.push(key)\n }\n }\n return missing\n}\n\n/** 이 모드가 자기 도구로 못 하는 일을 제안하는가 — 부팅 보고와 같은 답. */\nexport function modePromisesItCannotKeep(kind: string): string[] {\n return getAssistantModeWarnings()\n .filter(warning => warning.params.kind === kind)\n .map(warning => `${warning.code}:${warning.params.slash}→${warning.params.tool}`)\n}\n\n/**\n * What an app's prompt carries that every conversation already gets (design/ai/screen-pattern.md §6 · §7).\n *\n * The absence rule is added by the door to every conversation. A copy in an app's prompt says it twice, and the\n * copies drift - five apps had five spellings of it.\n */\nexport function modeCarriesSharedRules(kind: string): string[] {\n const prompt = String(getAssistantMode(kind)?.systemPrompt ?? '')\n const found: string[] = []\n if (/name our plumbing|do not describe our machinery/i.test(prompt)) found.push('absence-rule-copy')\n if (/^\\s*Next:/im.test(prompt) || /end the reply with one last line/i.test(prompt)) found.push('next-request-rule-copy')\n return found\n}\n\n/** Subjects that more than one conversation names - a screen has one (design/ai/screen-pattern.md §3). */\nexport function subjectsClaimedTwice(): string[] {\n return getAssistantModeWarnings()\n .filter(warning => warning.code === 'assistant-mode-subject-claimed-twice')\n .map(warning => String(warning.params.subject))\n}\n\n/** The line the model reads for an open subject - an app's test checks its describer names its own record. */\nexport function openSubjectLineFor(anchorType: string, anchorId: string, domainId: string, state?: any): Promise<string | undefined> {\n return openSubjectLine({ anchorType, anchorId, domain: { id: domainId }, state })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mode-conformance.js","sourceRoot":"","sources":["../../../server/service/testing/mode-conformance.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 등록된 모드가 **선언한 대로 도는가** — 글자가 아니라 문을 지나서 본다.\n *\n * 모드 선언은 소스에 적힌 값이고, 사람이 만나는 것은 그 값이 문을 지나 만들어 낸 것이다. 그 둘이\n * 어긋나는 자리가 오늘 하루에만 여럿 나왔다: 범주에 없는 도구를 프롬프트가 부르라고 했고, 키는 있는데\n * 번역이 없었고, 부품에 규칙이 있는데 부르는 쪽이 없었다. 전부 「선언은 맞는데 돌지 않는」 모양이다.\n *\n * 그래서 앱의 시험이 이것으로 **자기 모드를 실제로 돌려 본다**:\n * · 그 이름으로 모드가 서는가\n * · 그 모드가 여는 도구가 무엇인가 — 모델에게 실제로 건네지는 목록\n * · 첫 턴 도구 호출을 강제하는가\n * · 화면에 낼 말이 **키가 아니라 문장**으로 나오는가\n * · 슬래시가 가리키는 도구가 그 모드 안에 있는가\n */\nimport { openSubjectLine, registryAssistantChat } from '@things-factory/ai-client-base'\n\nimport { getAssistantMode, getAssistantModeWarnings } from '../assistant-mode/registry.js'\nimport { assistantModeView, resolveModeSettings, type Translate } from '../assistant-mode/resolve.js'\nimport { modeFrontCategories, type TurnScreen } from '../assistant-mode/mode-categories.js'\n\nexport interface ModeRunResult {\n /** 이 모드가 모델에게 실제로 건넨 도구 이름. */\n offeredTools: string[]\n /** 건네지 않았지만 모델이 openTools 로 꺼낼 수 있던 도구 — 둘을 합치면 이 모드로 쓸 수 있는 전부(ADR-0061 결정 9). */\n openableTools: string[]\n /** 모델이 받은 시스템 프롬프트 — 서버의 것인지 확인할 수 있다. */\n systemPrompt: string\n /** 첫 턴에 도구를 강제했는가. */\n requiredToolOnFirstTurn: boolean\n reply: string\n}\n\n/** 시험용 번역기 — 키를 그대로 돌려준다. 문장이 나와야 할 자리에 키가 나오면 눈에 띈다. */\nexport const keyEchoTranslate: Translate = key => key\n\n/**\n * 등록된 모드로 한 턴을 돌린다 — 프롬프트도 도구도 **등록에서** 나온다.\n *\n * 시험이 자기 값을 넘기면 그 시험은 선언을 안 본 것이다. 그래서 여기서는 `kind` 만 받는다.\n */\nexport async function runRegisteredMode(\n kind: string,\n options: { client: any; hostContext?: any; state?: any; toolAccess?: any; reply?: string; screen?: TurnScreen } = {} as any\n): Promise<ModeRunResult> {\n const settings = resolveModeSettings({ kind }, keyEchoTranslate)\n let requiredToolOnFirstTurn = false\n\n const result = await registryAssistantChat({\n client: options.client,\n messages: [{ role: 'user', content: '한 턴' }] as any,\n systemPrompt: settings.systemPrompt,\n toolCategories: settings.toolCategories,\n /* What the door puts in front on that screen (ADR-0061 decision 9) - a mode too large for one call is offered as a person meets it. */\n front: { categories: modeFrontCategories(getAssistantMode(kind) ?? {}, options.screen ?? {}) },\n requireToolOnFirstTurn: (requiredToolOnFirstTurn = Boolean(settings.requireToolOnFirstTurn)),\n temperature: settings.temperature,\n maxTokens: settings.maxTokens,\n hostContext: options.hostContext,\n /*\n * This harness asks about **the mode** — \"what does this mode open\", not \"does this person pass the\n * doors\". Since the gate moved onto `doors` (ADR-0071 decision 2), passing no check closes every tool\n * that names a door, and an app's conformance test would quietly lose **exactly the tools that read\n * real data**. So the default is someone who passes every door; an app that wants the other case\n * writes `toolAccess`. The permission axis has its own tests (`tool-access.test.ts` ·\n * `tool-scope.test.ts`).\n */\n toolAccess: options.toolAccess ?? (async () => true),\n state: options.state ?? { domain: { id: 'dom-1' }, user: { id: 'u-1' } }\n })\n\n return {\n offeredTools: [...result.offeredTools],\n openableTools: [...result.openableTools],\n systemPrompt: settings.systemPrompt,\n requiredToolOnFirstTurn,\n reply: result.reply\n }\n}\n\nexport interface ModeWording {\n placeholder?: string\n /** 빈 대화의 제목과 한 줄 — 없으면 공용 대화창이 보드 제품의 글을 낸다. */\n intro?: { title: string; subtitle: string }\n /** 빈 대화의 예시 — 이 대화가 실제로 할 수 있는 말. */\n examples?: Array<{ label: string; items: string[] }>\n slash: { name: string; description: string; template: string }[]\n footerNotice?: string\n stagedNotice?: string\n}\n\n/** 화면에 낼 말 — 주어진 사전으로 옮긴 결과. 키가 그대로 남으면 사전에 없는 것이다. */\nexport function modeWording(kind: string, dictionary: Record<string, string>): ModeWording {\n const view = assistantModeView(kind, key => dictionary[key] ?? key)\n return {\n placeholder: view.placeholder,\n intro: view.intro,\n examples: view.examples,\n slash: view.slashTemplates,\n footerNotice: view.footerNotice,\n stagedNotice: view.stagedNotice\n }\n}\n\n/** 사전에 없어서 키가 그대로 화면에 나갈 말들. 비어 있어야 한다. */\nexport function missingWording(kind: string, dictionary: Record<string, string>): string[] {\n const missing: string[] = []\n const spec = getAssistantMode(kind)\n if (!spec) return [`mode:${kind}`]\n\n for (const key of [spec.placeholderKey, spec.footerNoticeKey, spec.stagedNoticeKey]) {\n if (key && !(key in dictionary)) missing.push(key)\n }\n /* 빈 대화의 말도 사전에 있어야 한다 — 없으면 화면에 키가 그대로 찍힌다. */\n for (const key of [spec.introKeys?.title, spec.introKeys?.subtitle]) {\n if (key && !(key in dictionary)) missing.push(key)\n }\n for (const group of spec.exampleKeys ?? []) {\n for (const key of [group.labelKey, ...group.itemKeys]) {\n if (!(key in dictionary)) missing.push(key)\n }\n }\n for (const slash of spec.slashTemplates ?? []) {\n for (const key of [slash.descriptionKey, slash.templateKey]) {\n if (!(key in dictionary)) missing.push(key)\n }\n }\n return missing\n}\n\n/** 이 모드가 자기 도구로 못 하는 일을 제안하는가 — 부팅 보고와 같은 답. */\nexport function modePromisesItCannotKeep(kind: string): string[] {\n return getAssistantModeWarnings()\n .filter(warning => warning.params.kind === kind)\n .map(warning => `${warning.code}:${warning.params.slash}→${warning.params.tool}`)\n}\n\n/**\n * What an app's prompt carries that every conversation already gets (design/ai/screen-pattern.md §6 · §7).\n *\n * The absence rule is added by the door to every conversation. A copy in an app's prompt says it twice, and the\n * copies drift - five apps had five spellings of it.\n */\nexport function modeCarriesSharedRules(kind: string): string[] {\n const prompt = String(getAssistantMode(kind)?.systemPrompt ?? '')\n const found: string[] = []\n if (/name our plumbing|do not describe our machinery/i.test(prompt)) found.push('absence-rule-copy')\n if (/^\\s*Next:/im.test(prompt) || /end the reply with one last line/i.test(prompt)) found.push('next-request-rule-copy')\n return found\n}\n\n/** Subjects that more than one conversation names - a screen has one (design/ai/screen-pattern.md §3). */\nexport function subjectsClaimedTwice(): string[] {\n return getAssistantModeWarnings()\n .filter(warning => warning.code === 'assistant-mode-subject-claimed-twice')\n .map(warning => String(warning.params.subject))\n}\n\n/** The line the model reads for an open subject - an app's test checks its describer names its own record. */\nexport function openSubjectLineFor(anchorType: string, anchorId: string, domainId: string, state?: any): Promise<string | undefined> {\n return openSubjectLine({ anchorType, anchorId, domain: { id: domainId }, state })\n}\n"]}
|
|
@@ -154,6 +154,8 @@ async function runAssistantUnattended(input, deps = defaultDeps()) {
|
|
|
154
154
|
messages,
|
|
155
155
|
systemPrompt,
|
|
156
156
|
toolCategories: categories,
|
|
157
|
+
/* No screen and no conversation - the mode's own tools in front, the lists opened as the run needs (ADR-0061 decision 9). */
|
|
158
|
+
front: { categories: mode.toolCategories ?? [] },
|
|
157
159
|
mode: { kind: mode.kind, ...(input.mode.target ? { target: input.mode.target } : {}) },
|
|
158
160
|
toolAccess: deps.toolAccess(state),
|
|
159
161
|
hostContext: input.hostContext,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-unattended.js","sourceRoot":"","sources":["../../../server/service/unattended-run/run-unattended.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Run an assistant mode with no person in it, for scheduled work, and keep what it wrote as a draft\n * (operato-application/design/ai/unattended-run.md, ADR-0142 §8.1, user ruling C 2026-10-20).\n *\n * A server function and no GraphQL door: a door would let a person name someone else as `runAs`. The caller is a\n * server's scheduled task - Office's periodic strategy review is the first.\n *\n * identity (domain, source, subject, scheduledAt) is one row - a retry hands the stored run back (§5)\n * who the run-as user, judged again now; refused when they cannot ask the assistant themselves (§2)\n * tools the mode's read tools behind that user's own doors; no 입력안, no cards kept (§3)\n * limits the mode's: model requests, tokens, wall time (§4)\n * outcome succeeded with a draft, failed or refused with a code - never an empty success (§7)\n *\n * Every tool call goes through the registry door, so the recorders write it down as they do a turn's (§6).\n */\nimport { aiClientFor, findToolSpec, registryAssistantChat, type AgentClient, type AiClientRequest, type ToolAccessCheck, type ToolCallRecord } from '@things-factory/ai-client-base'\nimport { User, canUserCheckinDomain, checkPermission } from '@things-factory/auth-base'\nimport { getRepository } from '@things-factory/shell'\n\nimport { getAssistantMode } from '../assistant-mode/registry.js'\nimport { unattendedToolCategories } from '../assistant-mode/mode-categories.js'\nimport { doorsForAnswer } from '../chat-message/message-doors.js'\nimport { groundingSourcesFor } from '../grounding-sources.js'\nimport { toolAccessFor } from '../tool-access.js'\nimport { AssistantRun } from './assistant-run.js'\nimport { ABANDON_MARGIN_MS, unattendedLimits } from './limits.js'\nimport { budgetedClient, RunBudgetExceeded } from './run-budget.js'\nimport { judgeRunAs, type RunAsChecks } from './run-as.js'\nimport { typeormRunStore, type RunEnding, type RunStore } from './run-store.js'\nimport { unattendedPrompt } from './unattended-prompt.js'\n\nexport interface UnattendedRunInput {\n /** The domain the run is in, as loaded - its parent and owner decide who can enter it. */\n domain: { id: string; [key: string]: any }\n /** The user the run acts as - the policy owner. Their privileges and doors only. */\n runAs: string\n /** Which occurrence this is. The same three again is the same run. */\n run: {\n /** Who scheduled it - `<app>.<work>`, e.g. `office.strategy-review`. */\n source: string\n /** What it is about in the source's terms - a policy id. */\n subject: string\n scheduledAt: Date\n }\n /** A registered mode that declares `unattended`. */\n mode: { kind: string; target?: string }\n /** What this occurrence is asked to do - the caller server's text, read by the model (English). */\n instruction: string\n /** What the run is about, for the tools. Filled by the caller's server; still never used for authorization. */\n hostContext?: Record<string, any>\n /** The language of the draft - the owner's. Absent: their account's locale. */\n lng?: string\n}\n\n/** What the run reaches outside itself. The defaults are the server's; the tests hand their own. */\nexport interface UnattendedRunDeps {\n store: RunStore\n runAs: RunAsChecks<any, any>\n clientFor: (request: AiClientRequest) => Promise<AgentClient>\n toolAccess: (state: any) => ToolAccessCheck\n now: () => Date\n}\n\nconst KEY = /^[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*$/\n\nfunction invalid(field: string): Error {\n const error: any = new Error(`runAssistantUnattended: '${field}' is missing or not usable`)\n error.code = 'assistant-run-input-invalid'\n error.params = { field }\n return error\n}\n\nfunction checkInput(input: UnattendedRunInput) {\n if (!input?.domain?.id) throw invalid('domain')\n if (!input.runAs) throw invalid('runAs')\n if (!KEY.test(String(input.run?.source ?? '')) || input.run.source.length > 64) throw invalid('run.source')\n if (!String(input.run.subject ?? '').trim() || String(input.run.subject).length > 64) throw invalid('run.subject')\n if (!(input.run.scheduledAt instanceof Date) || !Number.isFinite(input.run.scheduledAt.valueOf())) throw invalid('run.scheduledAt')\n if (!input.mode?.kind) throw invalid('mode.kind')\n if (!String(input.instruction ?? '').trim()) throw invalid('instruction')\n}\n\nfunction defaultDeps(): UnattendedRunDeps {\n return {\n store: typeormRunStore(() => getRepository(AssistantRun)),\n runAs: {\n findUser: id => getRepository(User).findOne({ where: { id } }),\n mayCheckin: (user, domain) => canUserCheckinDomain(user, domain),\n /* No request: no address to refuse from, and no license prohibition - those cover writes this run never makes. */\n permits: (gate, user, domain) => checkPermission(gate as any, user, domain, false, [])\n },\n clientFor: aiClientFor,\n toolAccess: state => toolAccessFor({ state }),\n now: () => new Date()\n }\n}\n\n/** Why the loop stopped, as the run's code. Only a clean end with text is a draft. */\nfunction failureOf(loop: any, baseText: string): { code: string; params?: any } | undefined {\n const abort = loop?.abortReason\n if (abort?.type === 'max_iterations') return { code: 'assistant-run-step-limit', params: { steps: abort.iter } }\n if (abort?.type === 'provider_error') return { code: 'assistant-run-model-error', params: { message: String(abort.message ?? '').slice(0, 500) } }\n if (abort) return { code: 'assistant-run-model-error', params: { stopped: abort.type } }\n if (!baseText.trim()) return { code: 'assistant-run-empty-draft' }\n return undefined\n}\n\nconst TIMED_OUT = Symbol('timed-out')\n\nexport async function runAssistantUnattended(input: UnattendedRunInput, deps: UnattendedRunDeps = defaultDeps()): Promise<AssistantRun> {\n checkInput(input)\n const { domain, run } = input\n const mode = getAssistantMode(input.mode.kind)\n const limits = unattendedLimits(mode?.unattended)\n const startedAt = deps.now()\n /* The account as stored now - read once, and judged after the occurrence is held. */\n const account = await deps.runAs.findUser(input.runAs)\n\n const { claimed, row } = await deps.store.claim(\n { domainId: domain.id, source: run.source, subject: run.subject, scheduledAt: run.scheduledAt },\n {\n name: `${run.source} ${run.subject} ${run.scheduledAt.toISOString()}`,\n modeKind: input.mode.kind,\n modeTarget: input.mode.target,\n runAsId: account?.id,\n startedAt,\n deadlineAt: new Date(startedAt.valueOf() + limits.timeoutMs + ABANDON_MARGIN_MS)\n },\n startedAt\n )\n /* Succeeded, or running elsewhere and still alive - the caller gets that row and the model is not called again. */\n if (!claimed) return row\n\n const held = { id: row.id!, attempt: row.attempt }\n const end = (ending: RunEnding) => deps.store.finish(held, { finishedAt: deps.now(), ...ending })\n const refuse = (code: string, params?: any) => end({ status: 'refused', outcomeCode: code, outcomeParams: params ? JSON.stringify(params) : null } as any)\n const fail = (code: string, params?: any, spent?: RunEnding) =>\n end({ status: 'failed', outcomeCode: code, outcomeParams: params ? JSON.stringify(params) : null, ...spent } as any)\n\n if (!mode) return refuse('unknown-assistant-mode', { kind: input.mode.kind })\n if (!mode.unattended) return refuse('assistant-mode-not-unattended', { kind: mode.kind })\n\n const judged = await judgeRunAs(account, domain, deps.runAs)\n if ('refused' in judged) return refuse(judged.refused, { userId: input.runAs })\n const user = judged.user\n\n let agentClient: AgentClient\n try {\n agentClient = await deps.clientFor({ domain: domain as any, user, modeKind: mode.kind })\n } catch (error: any) {\n /* No AI here, or not the agent asked for - nothing ran. Its own code, as the conversation door says it. */\n if (error?.code) return refuse(String(error.code), error.params)\n return fail('assistant-run-model-error', { message: String(error?.message ?? error).slice(0, 500) })\n }\n\n const { client, spend } = budgetedClient(agentClient, limits)\n const reads: ToolCallRecord[] = []\n let over = false\n const state: any = { domain, user, lng: input.lng ?? user.locale, aiAgentId: agentClient.agent.id }\n const categories = unattendedToolCategories(mode)\n const systemPrompt = unattendedPrompt(mode.systemPrompt, state.lng)\n const messages = [{ role: 'user' as const, content: input.instruction }]\n\n const abort = new AbortController()\n let timer: ReturnType<typeof setTimeout> | undefined\n const timeout = new Promise<typeof TIMED_OUT>(resolve => {\n timer = setTimeout(() => {\n abort.abort()\n resolve(TIMED_OUT)\n }, limits.timeoutMs)\n })\n\n const spent = (): RunEnding => ({\n reads: JSON.stringify(reads),\n agentId: agentClient.agent.id,\n clientId: agentClient.id,\n modelCalls: spend.modelCalls,\n inputTokens: spend.inputTokens,\n outputTokens: spend.outputTokens\n })\n\n let result: any\n try {\n result = await Promise.race([\n registryAssistantChat({\n client,\n messages,\n systemPrompt,\n toolCategories: categories,\n mode: { kind: mode.kind, ...(input.mode.target ? { target: input.mode.target } : {}) },\n toolAccess: deps.toolAccess(state),\n hostContext: input.hostContext,\n state,\n temperature: mode.temperature,\n maxTokens: mode.maxTokens,\n requireToolOnFirstTurn: mode.requireToolOnFirstTurn,\n /* Each step is one request. A run that reaches the last step without an answer failed - no wrap-up call (§4). */\n maxIterations: limits.maxModelCalls,\n salvageOnMaxIterations: false,\n signal: abort.signal,\n onToolCall: record => {\n if (!over) reads.push(record)\n },\n run: { id: held.id, source: run.source, subject: run.subject, scheduledAt: run.scheduledAt.toISOString() },\n groundingSources: groundingSourcesFor({ systemPrompt, messages: messages as any, hostContext: input.hostContext })\n }),\n timeout\n ])\n } catch (error: any) {\n over = true\n clearTimeout(timer)\n if (error instanceof RunBudgetExceeded) return fail(error.code, error.params, spent())\n return fail('assistant-run-model-error', { message: String(error?.message ?? error).slice(0, 500) }, spent())\n }\n over = true\n clearTimeout(timer)\n\n if (result === TIMED_OUT) return fail('assistant-run-timeout', { timeoutMs: limits.timeoutMs }, spent())\n /* The loop turns a refused request into a provider error; the budget says which limit it was. */\n if (spend.exceeded) return fail('assistant-run-budget-exceeded', spend.exceeded, spent())\n\n const failure = failureOf(result.loop, result.baseText ?? '')\n if (failure) return fail(failure.code, failure.params, spent())\n\n const doors = doorsForAnswer(\n (result.loop.toolUsages ?? []).map((usage: any) => usage?.name).filter(Boolean),\n name => (findToolSpec(name, categories) as any)?.doors\n )\n return end({\n status: 'succeeded',\n draft: result.baseText,\n doors,\n cardsDropped: result.loop.proposals?.length ?? 0,\n groundingWarnings: result.groundingWarnings?.length ? JSON.stringify(result.groundingWarnings).slice(0, 1024) : null,\n ...spent()\n } as any)\n}\n\n/** The stored run of an occurrence, for the application that scheduled it - read behind that application's own door. */\nexport async function findAssistantRun(identity: { domainId: string; source: string; subject: string; scheduledAt: Date } | { id: string }): Promise<AssistantRun | null> {\n const repository = getRepository(AssistantRun)\n if ('id' in identity) return repository.findOne({ where: { id: identity.id } })\n return repository.findOne({\n where: { domain: { id: identity.domainId }, source: identity.source, subject: identity.subject, scheduledAt: identity.scheduledAt } as any\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"run-unattended.js","sourceRoot":"","sources":["../../../server/service/unattended-run/run-unattended.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Run an assistant mode with no person in it, for scheduled work, and keep what it wrote as a draft\n * (operato-application/design/ai/unattended-run.md, ADR-0142 §8.1, user ruling C 2026-10-20).\n *\n * A server function and no GraphQL door: a door would let a person name someone else as `runAs`. The caller is a\n * server's scheduled task - Office's periodic strategy review is the first.\n *\n * identity (domain, source, subject, scheduledAt) is one row - a retry hands the stored run back (§5)\n * who the run-as user, judged again now; refused when they cannot ask the assistant themselves (§2)\n * tools the mode's read tools behind that user's own doors; no 입력안, no cards kept (§3)\n * limits the mode's: model requests, tokens, wall time (§4)\n * outcome succeeded with a draft, failed or refused with a code - never an empty success (§7)\n *\n * Every tool call goes through the registry door, so the recorders write it down as they do a turn's (§6).\n */\nimport { aiClientFor, findToolSpec, registryAssistantChat, type AgentClient, type AiClientRequest, type ToolAccessCheck, type ToolCallRecord } from '@things-factory/ai-client-base'\nimport { User, canUserCheckinDomain, checkPermission } from '@things-factory/auth-base'\nimport { getRepository } from '@things-factory/shell'\n\nimport { getAssistantMode } from '../assistant-mode/registry.js'\nimport { unattendedToolCategories } from '../assistant-mode/mode-categories.js'\nimport { doorsForAnswer } from '../chat-message/message-doors.js'\nimport { groundingSourcesFor } from '../grounding-sources.js'\nimport { toolAccessFor } from '../tool-access.js'\nimport { AssistantRun } from './assistant-run.js'\nimport { ABANDON_MARGIN_MS, unattendedLimits } from './limits.js'\nimport { budgetedClient, RunBudgetExceeded } from './run-budget.js'\nimport { judgeRunAs, type RunAsChecks } from './run-as.js'\nimport { typeormRunStore, type RunEnding, type RunStore } from './run-store.js'\nimport { unattendedPrompt } from './unattended-prompt.js'\n\nexport interface UnattendedRunInput {\n /** The domain the run is in, as loaded - its parent and owner decide who can enter it. */\n domain: { id: string; [key: string]: any }\n /** The user the run acts as - the policy owner. Their privileges and doors only. */\n runAs: string\n /** Which occurrence this is. The same three again is the same run. */\n run: {\n /** Who scheduled it - `<app>.<work>`, e.g. `office.strategy-review`. */\n source: string\n /** What it is about in the source's terms - a policy id. */\n subject: string\n scheduledAt: Date\n }\n /** A registered mode that declares `unattended`. */\n mode: { kind: string; target?: string }\n /** What this occurrence is asked to do - the caller server's text, read by the model (English). */\n instruction: string\n /** What the run is about, for the tools. Filled by the caller's server; still never used for authorization. */\n hostContext?: Record<string, any>\n /** The language of the draft - the owner's. Absent: their account's locale. */\n lng?: string\n}\n\n/** What the run reaches outside itself. The defaults are the server's; the tests hand their own. */\nexport interface UnattendedRunDeps {\n store: RunStore\n runAs: RunAsChecks<any, any>\n clientFor: (request: AiClientRequest) => Promise<AgentClient>\n toolAccess: (state: any) => ToolAccessCheck\n now: () => Date\n}\n\nconst KEY = /^[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*$/\n\nfunction invalid(field: string): Error {\n const error: any = new Error(`runAssistantUnattended: '${field}' is missing or not usable`)\n error.code = 'assistant-run-input-invalid'\n error.params = { field }\n return error\n}\n\nfunction checkInput(input: UnattendedRunInput) {\n if (!input?.domain?.id) throw invalid('domain')\n if (!input.runAs) throw invalid('runAs')\n if (!KEY.test(String(input.run?.source ?? '')) || input.run.source.length > 64) throw invalid('run.source')\n if (!String(input.run.subject ?? '').trim() || String(input.run.subject).length > 64) throw invalid('run.subject')\n if (!(input.run.scheduledAt instanceof Date) || !Number.isFinite(input.run.scheduledAt.valueOf())) throw invalid('run.scheduledAt')\n if (!input.mode?.kind) throw invalid('mode.kind')\n if (!String(input.instruction ?? '').trim()) throw invalid('instruction')\n}\n\nfunction defaultDeps(): UnattendedRunDeps {\n return {\n store: typeormRunStore(() => getRepository(AssistantRun)),\n runAs: {\n findUser: id => getRepository(User).findOne({ where: { id } }),\n mayCheckin: (user, domain) => canUserCheckinDomain(user, domain),\n /* No request: no address to refuse from, and no license prohibition - those cover writes this run never makes. */\n permits: (gate, user, domain) => checkPermission(gate as any, user, domain, false, [])\n },\n clientFor: aiClientFor,\n toolAccess: state => toolAccessFor({ state }),\n now: () => new Date()\n }\n}\n\n/** Why the loop stopped, as the run's code. Only a clean end with text is a draft. */\nfunction failureOf(loop: any, baseText: string): { code: string; params?: any } | undefined {\n const abort = loop?.abortReason\n if (abort?.type === 'max_iterations') return { code: 'assistant-run-step-limit', params: { steps: abort.iter } }\n if (abort?.type === 'provider_error') return { code: 'assistant-run-model-error', params: { message: String(abort.message ?? '').slice(0, 500) } }\n if (abort) return { code: 'assistant-run-model-error', params: { stopped: abort.type } }\n if (!baseText.trim()) return { code: 'assistant-run-empty-draft' }\n return undefined\n}\n\nconst TIMED_OUT = Symbol('timed-out')\n\nexport async function runAssistantUnattended(input: UnattendedRunInput, deps: UnattendedRunDeps = defaultDeps()): Promise<AssistantRun> {\n checkInput(input)\n const { domain, run } = input\n const mode = getAssistantMode(input.mode.kind)\n const limits = unattendedLimits(mode?.unattended)\n const startedAt = deps.now()\n /* The account as stored now - read once, and judged after the occurrence is held. */\n const account = await deps.runAs.findUser(input.runAs)\n\n const { claimed, row } = await deps.store.claim(\n { domainId: domain.id, source: run.source, subject: run.subject, scheduledAt: run.scheduledAt },\n {\n name: `${run.source} ${run.subject} ${run.scheduledAt.toISOString()}`,\n modeKind: input.mode.kind,\n modeTarget: input.mode.target,\n runAsId: account?.id,\n startedAt,\n deadlineAt: new Date(startedAt.valueOf() + limits.timeoutMs + ABANDON_MARGIN_MS)\n },\n startedAt\n )\n /* Succeeded, or running elsewhere and still alive - the caller gets that row and the model is not called again. */\n if (!claimed) return row\n\n const held = { id: row.id!, attempt: row.attempt }\n const end = (ending: RunEnding) => deps.store.finish(held, { finishedAt: deps.now(), ...ending })\n const refuse = (code: string, params?: any) => end({ status: 'refused', outcomeCode: code, outcomeParams: params ? JSON.stringify(params) : null } as any)\n const fail = (code: string, params?: any, spent?: RunEnding) =>\n end({ status: 'failed', outcomeCode: code, outcomeParams: params ? JSON.stringify(params) : null, ...spent } as any)\n\n if (!mode) return refuse('unknown-assistant-mode', { kind: input.mode.kind })\n if (!mode.unattended) return refuse('assistant-mode-not-unattended', { kind: mode.kind })\n\n const judged = await judgeRunAs(account, domain, deps.runAs)\n if ('refused' in judged) return refuse(judged.refused, { userId: input.runAs })\n const user = judged.user\n\n let agentClient: AgentClient\n try {\n agentClient = await deps.clientFor({ domain: domain as any, user, modeKind: mode.kind })\n } catch (error: any) {\n /* No AI here, or not the agent asked for - nothing ran. Its own code, as the conversation door says it. */\n if (error?.code) return refuse(String(error.code), error.params)\n return fail('assistant-run-model-error', { message: String(error?.message ?? error).slice(0, 500) })\n }\n\n const { client, spend } = budgetedClient(agentClient, limits)\n const reads: ToolCallRecord[] = []\n let over = false\n const state: any = { domain, user, lng: input.lng ?? user.locale, aiAgentId: agentClient.agent.id }\n const categories = unattendedToolCategories(mode)\n const systemPrompt = unattendedPrompt(mode.systemPrompt, state.lng)\n const messages = [{ role: 'user' as const, content: input.instruction }]\n\n const abort = new AbortController()\n let timer: ReturnType<typeof setTimeout> | undefined\n const timeout = new Promise<typeof TIMED_OUT>(resolve => {\n timer = setTimeout(() => {\n abort.abort()\n resolve(TIMED_OUT)\n }, limits.timeoutMs)\n })\n\n const spent = (): RunEnding => ({\n reads: JSON.stringify(reads),\n agentId: agentClient.agent.id,\n clientId: agentClient.id,\n modelCalls: spend.modelCalls,\n inputTokens: spend.inputTokens,\n outputTokens: spend.outputTokens\n })\n\n let result: any\n try {\n result = await Promise.race([\n registryAssistantChat({\n client,\n messages,\n systemPrompt,\n toolCategories: categories,\n /* No screen and no conversation - the mode's own tools in front, the lists opened as the run needs (ADR-0061 decision 9). */\n front: { categories: mode.toolCategories ?? [] },\n mode: { kind: mode.kind, ...(input.mode.target ? { target: input.mode.target } : {}) },\n toolAccess: deps.toolAccess(state),\n hostContext: input.hostContext,\n state,\n temperature: mode.temperature,\n maxTokens: mode.maxTokens,\n requireToolOnFirstTurn: mode.requireToolOnFirstTurn,\n /* Each step is one request. A run that reaches the last step without an answer failed - no wrap-up call (§4). */\n maxIterations: limits.maxModelCalls,\n salvageOnMaxIterations: false,\n signal: abort.signal,\n onToolCall: record => {\n if (!over) reads.push(record)\n },\n run: { id: held.id, source: run.source, subject: run.subject, scheduledAt: run.scheduledAt.toISOString() },\n groundingSources: groundingSourcesFor({ systemPrompt, messages: messages as any, hostContext: input.hostContext })\n }),\n timeout\n ])\n } catch (error: any) {\n over = true\n clearTimeout(timer)\n if (error instanceof RunBudgetExceeded) return fail(error.code, error.params, spent())\n return fail('assistant-run-model-error', { message: String(error?.message ?? error).slice(0, 500) }, spent())\n }\n over = true\n clearTimeout(timer)\n\n if (result === TIMED_OUT) return fail('assistant-run-timeout', { timeoutMs: limits.timeoutMs }, spent())\n /* The loop turns a refused request into a provider error; the budget says which limit it was. */\n if (spend.exceeded) return fail('assistant-run-budget-exceeded', spend.exceeded, spent())\n\n const failure = failureOf(result.loop, result.baseText ?? '')\n if (failure) return fail(failure.code, failure.params, spent())\n\n const doors = doorsForAnswer(\n (result.loop.toolUsages ?? []).map((usage: any) => usage?.name).filter(Boolean),\n name => (findToolSpec(name, categories) as any)?.doors\n )\n return end({\n status: 'succeeded',\n draft: result.baseText,\n doors,\n cardsDropped: result.loop.proposals?.length ?? 0,\n groundingWarnings: result.groundingWarnings?.length ? JSON.stringify(result.groundingWarnings).slice(0, 1024) : null,\n ...spent()\n } as any)\n}\n\n/** The stored run of an occurrence, for the application that scheduled it - read behind that application's own door. */\nexport async function findAssistantRun(identity: { domainId: string; source: string; subject: string; scheduledAt: Date } | { id: string }): Promise<AssistantRun | null> {\n const repository = getRepository(AssistantRun)\n if ('id' in identity) return repository.findOne({ where: { id: identity.id } })\n return repository.findOne({\n where: { domain: { id: identity.domainId }, source: identity.source, subject: identity.subject, scheduledAt: identity.scheduledAt } as any\n })\n}\n"]}
|