@things-factory/ai-assistant 10.1.206 → 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 +3 -3
- 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
|
@@ -93,6 +93,8 @@ export interface AssistantChatInputArgs {
|
|
|
93
93
|
attachmentIds?: string[];
|
|
94
94
|
/** The AI agent the person picked. Omitted, the domain default answers. */
|
|
95
95
|
agentId?: string;
|
|
96
|
+
/** The route of the page the dock is on - the server offers that screen's tools first (ADR-0061 decision 9). */
|
|
97
|
+
page?: string;
|
|
96
98
|
}
|
|
97
99
|
/**
|
|
98
100
|
* Build the neutral input.
|
|
@@ -50,7 +50,8 @@ export function buildAssistantChatInput(args) {
|
|
|
50
50
|
...(args.truncateAfterMessageId ? { truncateAfterMessageId: args.truncateAfterMessageId } : {}),
|
|
51
51
|
...(args.hostContext ? { hostContext: args.hostContext } : {}),
|
|
52
52
|
...(args.attachmentIds?.length ? { attachmentIds: args.attachmentIds } : {}),
|
|
53
|
-
...(args.agentId ? { agentId: args.agentId } : {})
|
|
53
|
+
...(args.agentId ? { agentId: args.agentId } : {}),
|
|
54
|
+
...(args.page ? { page: args.page } : {})
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
57
|
//# sourceMappingURL=chat-input-builder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-input-builder.js","sourceRoot":"","sources":["../../client/components/chat-input-builder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA+CH;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAA2B;IAChE,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACjC,QAAQ,EAAE,IAAI,CAAC,OAAO;QACtB,YAAY,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACpC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,qDAAqD;QACrD,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC7E,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzH,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnD,CAAA;AACH,CAAC;
|
|
1
|
+
{"version":3,"file":"chat-input-builder.js","sourceRoot":"","sources":["../../client/components/chat-input-builder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA+CH;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAA2B;IAChE,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACjC,QAAQ,EAAE,IAAI,CAAC,OAAO;QACtB,YAAY,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACpC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,qDAAqD;QACrD,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC7E,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzH,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnD,CAAA;AACH,CAAC;AA6BD;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAA4B;IAClE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,kGAAkG;YAChG,kEAAkE,CACrE,CAAA;IACH,CAAC;IAED,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACjC,QAAQ,EAAE,IAAI,CAAC,OAAO;QACtB,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;QACzF,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5E,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1C,CAAA;AACH,CAAC","sourcesContent":["/**\n * boardAIChat GraphQL mutation 의 input 객체 빌더.\n *\n * board-ai-chat.ts (Lit / Material Web 의존) 와 분리 — pure 함수만 두면 jest 의 node\n * 환경에서도 import 가능. 회귀 테스트 (selectedRefids 누락 등) 가 단위 테스트로 잠긴다.\n */\n\nexport interface ChatMutationInputArgs {\n sessionId?: string\n history: Array<{ role: string; content: string }>\n liveBoard: any\n scopes?: string[]\n /** 편집으로 접은 지점(마지막으로 남긴 서버 메시지 id) — 서버가 그 뒤 이력을 에이전트 문맥에서 뺀다. */\n truncateAfterMessageId?: string\n /** 이 대화면이 무엇을 보고 있는지(예: { spaceId }) — 도구가 대상을 알 수 있게. 없으면 모델이 식별자를 되묻는다. */\n hostContext?: any\n /**\n * 이 대화면이 쓸 수 있는 외부 도구 카테고리(registerToolCategory 이름).\n * 미지정 = 등록된 전부(현행 동작). 지정하면 정의와 **실행** 모두 그 범위로 제한된다.\n */\n toolCategories?: string[]\n /** 코어 보드 편집 도구(addComponent·스타일링·씬 조작) 사용 여부. 기본 true. */\n boardTools?: boolean\n /**\n * 첫 턴에 도구 호출을 강제할지 — 라이브 상태를 다루는 대화면에서 켠다.\n * 근거 없이 상태를 단언하는 답(도구를 아예 부르지 않은 답)을 원천에서 막는다.\n */\n requireGroundingTools?: boolean\n knownTypes?: string[]\n categories?: string[]\n componentSchemas?: any\n /**\n * 선택된 컴포넌트의 `refid` 배열 — things-scene universal numeric handle.\n * id (데이터 바인딩 이름) 와는 다른 개념.\n */\n selectedRefids: number[]\n /**\n * popup 에서 명시 선택한 #mention 매핑 — {token, refid}.\n * 사용자 textarea 에는 #token 만 보이고, 정확한 refid 는 이 별도 채널로 server 에 전달.\n * server 가 mention map 으로 LLM 에 정확한 refid 정보 첨부 (사용자 노출 0).\n */\n mentions?: Array<{ token: string; refid: number }>\n /**\n * The registered conversation this board chat is (ADR-0061 decision 7). When sent, the server\n * takes the persona, tool categories, model and limits from that mode and ignores the per-request\n * ones above. Omitted for a surface that declares no mode.\n */\n mode?: { kind: string; target?: string }\n /** The AI agent the person picked. Omitted, the domain default answers. */\n agentId?: string\n}\n\n/**\n * GraphQL mutation 의 input 객체를 빌드.\n *\n * 핵심 보호 — `selectedRefids` 가 항상 포함되어야 한다. 누락이 사용자에게 보고된\n * \"AI 가 선택된 컴포넌트를 모른다\" 의 root cause 였다.\n */\nexport function buildChatMutationInput(args: ChatMutationInputArgs): Record<string, any> {\n return {\n sessionId: args.sessionId ?? null,\n messages: args.history,\n currentBoard: args.liveBoard ?? null,\n scopes: args.scopes,\n ...(args.truncateAfterMessageId ? { truncateAfterMessageId: args.truncateAfterMessageId } : {}),\n ...(args.hostContext ? { hostContext: args.hostContext } : {}),\n /* 미지정은 서버 기본(전부 허용)에 맡긴다 — null 을 보내 굳이 덮어쓰지 않는다. */\n ...(args.toolCategories ? { toolCategories: args.toolCategories } : {}),\n ...(args.boardTools === false ? { boardTools: false } : {}),\n ...(args.requireGroundingTools ? { requireGroundingTools: true } : {}),\n knownTypes: args.knownTypes,\n categories: args.categories,\n componentSchemas: args.componentSchemas,\n selectedRefids: args.selectedRefids,\n ...(args.mentions && args.mentions.length > 0 && { mentions: args.mentions }),\n ...(args.mode?.kind ? { mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) } } : {}),\n ...(args.agentId ? { agentId: args.agentId } : {})\n }\n}\n\n/**\n * Input for `assistantChat` — the door for a surface that has no document of its own.\n *\n * Deliberately a separate builder rather than a flag on the one above. The two mutations take\n * different inputs, and folding them together would mean a board field reaching a surface that\n * has no board, or a required field going missing with nothing to notice it. The one thing they\n * share is the conversation, and that is passed the same way.\n */\nexport interface AssistantChatInputArgs {\n sessionId?: string\n history: Array<{ role: string; content: string }>\n /**\n * Which conversation this is and what it is about. The persona, the tool categories, the model\n * and the limits belong to the mode the app server registered (ADR-0061 decision 7) — a caller\n * that could choose its own tool categories was a caller choosing its own authority.\n */\n mode: { kind: string; target?: string }\n truncateAfterMessageId?: string\n hostContext?: any\n /** Files stored with the conversation by attachToConversation, sent with this turn. */\n attachmentIds?: string[]\n /** The AI agent the person picked. Omitted, the domain default answers. */\n agentId?: string\n /** The route of the page the dock is on - the server offers that screen's tools first (ADR-0061 decision 9). */\n page?: string\n}\n\n/**\n * Build the neutral input.\n *\n * Refuses a conversation with no mode instead of sending one. A turn with no mode has no persona\n * and no tools, and an answer given as nobody reads like a bad model rather than a missing\n * argument — the server refuses it too, and this says so where the surface is.\n */\nexport function buildAssistantChatInput(args: AssistantChatInputArgs): Record<string, any> {\n if (!args.mode?.kind?.trim()) {\n throw new Error(\n '[ox-assistant-chat] mode.kind is required when the chat talks to assistantChat — the app server ' +\n 'registers the mode and the screen names it (ADR-0061 decision 7)'\n )\n }\n\n return {\n sessionId: args.sessionId ?? null,\n messages: args.history,\n mode: { kind: args.mode.kind, ...(args.mode.target ? { target: args.mode.target } : {}) },\n ...(args.truncateAfterMessageId ? { truncateAfterMessageId: args.truncateAfterMessageId } : {}),\n ...(args.hostContext ? { hostContext: args.hostContext } : {}),\n ...(args.attachmentIds?.length ? { attachmentIds: args.attachmentIds } : {}),\n ...(args.agentId ? { agentId: args.agentId } : {}),\n ...(args.page ? { page: args.page } : {})\n }\n}\n"]}
|
|
@@ -32,6 +32,10 @@ describe('the neutral door carries no document', () => {
|
|
|
32
32
|
expect(input).not.toHaveProperty(field);
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
|
+
it('★ the open page goes along - the server offers that screen\'s tools first (ADR-0061 decision 9)', () => {
|
|
36
|
+
expect(buildAssistantChatInput(args({ page: 'cash-transaction-list' })).page).toBe('cash-transaction-list');
|
|
37
|
+
expect(buildAssistantChatInput(args())).not.toHaveProperty('page');
|
|
38
|
+
});
|
|
35
39
|
it('a one-off question has no session — null, not absent, so the server reads it as ad-hoc', () => {
|
|
36
40
|
expect(buildAssistantChatInput(args()).sessionId).toBeNull();
|
|
37
41
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-input-builder.test.js","sourceRoot":"","sources":["../../client/components/chat-input-builder.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * What this guards: **the two doors take different inputs, and neither one leaks into the other.**\n *\n * The chat component talks to one of two server operations. `boardAIChat` is board authoring and\n * carries the board model; `assistantChat` is for a surface with no document of its own. Sending\n * a board field to the neutral door, or dropping a required one, produces a GraphQL error the\n * user sees as \"the assistant is broken\".\n *\n * The required system prompt is the one that would be silent. A conversation with no persona\n * still answers — as nobody, on a surface that never said what it is — and that reads as a poor\n * model rather than a missing argument.\n */\nimport { buildAssistantChatInput, buildChatMutationInput } from './chat-input-builder'\n\nconst history = [{ role: 'user', content: 'what figures do we have' }]\n\nconst args = (over: any = {}) => ({ history, mode: { kind: 'figure.modeller', target: 'fig-1' }, ...over })\n\ndescribe('the neutral door carries no document', () => {\n it('★ no board field reaches assistantChat', () => {\n const input = buildAssistantChatInput(args({ sessionId: 's-1' }))\n\n for (const field of ['currentBoard', 'selectedRefids', 'boardTools', 'knownTypes', 'componentSchemas', 'scopes', 'mentions']) {\n expect(input).not.toHaveProperty(field)\n }\n })\n\n it('carries the conversation, the session and which conversation this is', () => {\n const input = buildAssistantChatInput(args({ sessionId: 's-1' }))\n\n expect(input.sessionId).toBe('s-1')\n expect(input.messages).toBe(history)\n expect(input.mode).toEqual({ kind: 'figure.modeller', target: 'fig-1' })\n })\n\n it('★ the persona, the tools and the limits are not on the wire — they are the server\\'s', () => {\n const input = buildAssistantChatInput(args({ sessionId: 's-1' }))\n\n for (const field of ['systemPrompt', 'toolCategories', 'model', 'temperature', 'maxTokens', 'requireGroundingTools']) {\n expect(input).not.toHaveProperty(field)\n }\n })\n\n it('a one-off question has no session — null, not absent, so the server reads it as ad-hoc', () => {\n expect(buildAssistantChatInput(args()).sessionId).toBeNull()\n })\n})\n\ndescribe('the mode is required', () => {\n it('★ refuses a turn with no mode instead of sending one', () => {\n expect(() => buildAssistantChatInput(args({ mode: { kind: '' } }))).toThrow(/mode.kind is required/)\n expect(() => buildAssistantChatInput(args({ mode: { kind: ' ' } }))).toThrow(/mode.kind is required/)\n expect(() => buildAssistantChatInput(args({ mode: undefined }))).toThrow(/mode.kind is required/)\n })\n\n it('a conversation about nothing in particular carries no target', () => {\n expect(buildAssistantChatInput(args({ mode: { kind: 'twin.none' } })).mode).toEqual({ kind: 'twin.none' })\n })\n})\n\ndescribe('optional fields are sent only when they mean something', () => {\n\n it('host context and truncation travel when given', () => {\n const input = buildAssistantChatInput(args({ hostContext: { figureId: 'fig-1' }, truncateAfterMessageId: 'm-9' }))\n\n expect(input.hostContext).toEqual({ figureId: 'fig-1' })\n expect(input.truncateAfterMessageId).toBe('m-9')\n })\n})\n\ndescribe('the board door is unchanged', () => {\n it('★ still carries selectedRefids — a missing one was why the AI did not know what was selected', () => {\n const input = buildChatMutationInput({ history, liveBoard: null, selectedRefids: [3, 7] } as any)\n expect(input.selectedRefids).toEqual([3, 7])\n })\n\n it('does not take a system prompt — the board conversation has its persona on the server', () => {\n const input = buildChatMutationInput({ history, liveBoard: null, selectedRefids: [] } as any)\n expect(input).not.toHaveProperty('systemPrompt')\n })\n})\n"]}
|
|
1
|
+
{"version":3,"file":"chat-input-builder.test.js","sourceRoot":"","sources":["../../client/components/chat-input-builder.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * What this guards: **the two doors take different inputs, and neither one leaks into the other.**\n *\n * The chat component talks to one of two server operations. `boardAIChat` is board authoring and\n * carries the board model; `assistantChat` is for a surface with no document of its own. Sending\n * a board field to the neutral door, or dropping a required one, produces a GraphQL error the\n * user sees as \"the assistant is broken\".\n *\n * The required system prompt is the one that would be silent. A conversation with no persona\n * still answers — as nobody, on a surface that never said what it is — and that reads as a poor\n * model rather than a missing argument.\n */\nimport { buildAssistantChatInput, buildChatMutationInput } from './chat-input-builder'\n\nconst history = [{ role: 'user', content: 'what figures do we have' }]\n\nconst args = (over: any = {}) => ({ history, mode: { kind: 'figure.modeller', target: 'fig-1' }, ...over })\n\ndescribe('the neutral door carries no document', () => {\n it('★ no board field reaches assistantChat', () => {\n const input = buildAssistantChatInput(args({ sessionId: 's-1' }))\n\n for (const field of ['currentBoard', 'selectedRefids', 'boardTools', 'knownTypes', 'componentSchemas', 'scopes', 'mentions']) {\n expect(input).not.toHaveProperty(field)\n }\n })\n\n it('carries the conversation, the session and which conversation this is', () => {\n const input = buildAssistantChatInput(args({ sessionId: 's-1' }))\n\n expect(input.sessionId).toBe('s-1')\n expect(input.messages).toBe(history)\n expect(input.mode).toEqual({ kind: 'figure.modeller', target: 'fig-1' })\n })\n\n it('★ the persona, the tools and the limits are not on the wire — they are the server\\'s', () => {\n const input = buildAssistantChatInput(args({ sessionId: 's-1' }))\n\n for (const field of ['systemPrompt', 'toolCategories', 'model', 'temperature', 'maxTokens', 'requireGroundingTools']) {\n expect(input).not.toHaveProperty(field)\n }\n })\n\n it('★ the open page goes along - the server offers that screen\\'s tools first (ADR-0061 decision 9)', () => {\n expect(buildAssistantChatInput(args({ page: 'cash-transaction-list' })).page).toBe('cash-transaction-list')\n expect(buildAssistantChatInput(args())).not.toHaveProperty('page')\n })\n\n it('a one-off question has no session — null, not absent, so the server reads it as ad-hoc', () => {\n expect(buildAssistantChatInput(args()).sessionId).toBeNull()\n })\n})\n\ndescribe('the mode is required', () => {\n it('★ refuses a turn with no mode instead of sending one', () => {\n expect(() => buildAssistantChatInput(args({ mode: { kind: '' } }))).toThrow(/mode.kind is required/)\n expect(() => buildAssistantChatInput(args({ mode: { kind: ' ' } }))).toThrow(/mode.kind is required/)\n expect(() => buildAssistantChatInput(args({ mode: undefined }))).toThrow(/mode.kind is required/)\n })\n\n it('a conversation about nothing in particular carries no target', () => {\n expect(buildAssistantChatInput(args({ mode: { kind: 'twin.none' } })).mode).toEqual({ kind: 'twin.none' })\n })\n})\n\ndescribe('optional fields are sent only when they mean something', () => {\n\n it('host context and truncation travel when given', () => {\n const input = buildAssistantChatInput(args({ hostContext: { figureId: 'fig-1' }, truncateAfterMessageId: 'm-9' }))\n\n expect(input.hostContext).toEqual({ figureId: 'fig-1' })\n expect(input.truncateAfterMessageId).toBe('m-9')\n })\n})\n\ndescribe('the board door is unchanged', () => {\n it('★ still carries selectedRefids — a missing one was why the AI did not know what was selected', () => {\n const input = buildChatMutationInput({ history, liveBoard: null, selectedRefids: [3, 7] } as any)\n expect(input.selectedRefids).toEqual([3, 7])\n })\n\n it('does not take a system prompt — the board conversation has its persona on the server', () => {\n const input = buildChatMutationInput({ history, liveBoard: null, selectedRefids: [] } as any)\n expect(input).not.toHaveProperty('systemPrompt')\n })\n})\n"]}
|