@things-factory/ai-assistant 10.1.40 → 10.1.44
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-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/assistant-capability-resolver.js +1 -1
- package/dist-server/service/assistant-capability-resolver.js.map +1 -1
- package/dist-server/service/assistant-chat-resolver.js +1 -1
- package/dist-server/service/assistant-chat-resolver.js.map +1 -1
- package/dist-server/service/assistant-mode/assistant-mode-resolver.js +2 -2
- package/dist-server/service/assistant-mode/assistant-mode-resolver.js.map +1 -1
- package/dist-server/service/chat-session-resolver.js +21 -21
- package/dist-server/service/chat-session-resolver.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/server/service/assistant-capability-resolver.ts +1 -1
- package/server/service/assistant-chat-resolver.ts +1 -1
- package/server/service/assistant-mode/assistant-mode-resolver.ts +2 -2
- package/server/service/chat-session/session-visibility.test.ts +47 -0
- package/server/service/chat-session-resolver.ts +21 -21
- package/test/open-doors.test.ts +16 -0
|
@@ -76,7 +76,7 @@ tslib_1.__decorate([
|
|
|
76
76
|
(0, type_graphql_1.Query)(() => AiCapabilityView, {
|
|
77
77
|
description: 'Whether this deployment has an AI configured, and which registrations are incomplete. Read this before offering to ask the assistant — do not find out by failing.'
|
|
78
78
|
}),
|
|
79
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query")'),
|
|
79
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
80
80
|
tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
|
|
81
81
|
tslib_1.__metadata("design:type", Function),
|
|
82
82
|
tslib_1.__metadata("design:paramtypes", [Object]),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-capability-resolver.js","sourceRoot":"","sources":["../../server/service/assistant-capability-resolver.ts"],"names":[],"mappings":";;;;AAAA;;;;;;;;;;GAUG;AACH,+CAAiF;AACjF,qDAA6C;AAC7C,qCAAkC;AAElC,mEAAsF;AAEtF,wDAAoE;AAGpE,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;CAS1B,CAAA;AAPC;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,iEAAiE,EAAE,CAAC;;mDAC9E;AAGZ;IADC,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE,EAAE,WAAW,EAAE,+DAA+D,EAAE,CAAC;;qDAChG;AAGX;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,6DAA6D,EAAE,CAAC;;sDACvE;AARX,qBAAqB;IAD1B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,6EAA6E,EAAE,CAAC;GACrG,qBAAqB,CAS1B;AAGD,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;CAerB,CAAA;AAbC;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,qHAAqH,EAAE,CAAC;;+CACjI;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC;;kDACjD;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;;+CACzE;AAGd;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oFAAoF,EAAE,CAAC;;gDAC9G;AAGf;IADC,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,qBAAqB,CAAC,EAAE,EAAE,WAAW,EAAE,kFAAkF,EAAE,CAAC;;kDACzG;AAd7B,gBAAgB;IADrB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,4EAA4E,EAAE,CAAC;GACpG,gBAAgB,CAerB;AAGM,IAAM,2BAA2B,GAAjC,MAAM,2BAA2B;IAKhC,AAAN,KAAK,CAAC,YAAY,CAAQ,QAAa;QACrC,MAAM,UAAU,GAAG,IAAA,6BAAY,GAAE,CAAA;QACjC,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAA,wCAAuB,GAAE,EAAE,GAAG,IAAA,mCAAwB,GAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC7F,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC,CAAC,CAAA;QAEH,OAAO,EAAE,GAAG,UAAU,EAAE,QAAQ,EAAsB,CAAA;IACxD,CAAC;CACF,CAAA;AAfY,kEAA2B;AAKhC;IAJL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,gBAAgB,EAAE;QAC7B,WAAW,EAAE,oKAAoK;KAClL,CAAC;IACD,IAAA,wBAAS,EAAC,
|
|
1
|
+
{"version":3,"file":"assistant-capability-resolver.js","sourceRoot":"","sources":["../../server/service/assistant-capability-resolver.ts"],"names":[],"mappings":";;;;AAAA;;;;;;;;;;GAUG;AACH,+CAAiF;AACjF,qDAA6C;AAC7C,qCAAkC;AAElC,mEAAsF;AAEtF,wDAAoE;AAGpE,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;CAS1B,CAAA;AAPC;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,iEAAiE,EAAE,CAAC;;mDAC9E;AAGZ;IADC,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,6BAAW,EAAE,EAAE,WAAW,EAAE,+DAA+D,EAAE,CAAC;;qDAChG;AAGX;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,6DAA6D,EAAE,CAAC;;sDACvE;AARX,qBAAqB;IAD1B,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,6EAA6E,EAAE,CAAC;GACrG,qBAAqB,CAS1B;AAGD,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;CAerB,CAAA;AAbC;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,qHAAqH,EAAE,CAAC;;+CACjI;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC;;kDACjD;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;;+CACzE;AAGd;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oFAAoF,EAAE,CAAC;;gDAC9G;AAGf;IADC,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,CAAC,qBAAqB,CAAC,EAAE,EAAE,WAAW,EAAE,kFAAkF,EAAE,CAAC;;kDACzG;AAd7B,gBAAgB;IADrB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,4EAA4E,EAAE,CAAC;GACpG,gBAAgB,CAerB;AAGM,IAAM,2BAA2B,GAAjC,MAAM,2BAA2B;IAKhC,AAAN,KAAK,CAAC,YAAY,CAAQ,QAAa;QACrC,MAAM,UAAU,GAAG,IAAA,6BAAY,GAAE,CAAA;QACjC,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAA,wCAAuB,GAAE,EAAE,GAAG,IAAA,mCAAwB,GAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC7F,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC,CAAC,CAAA;QAEH,OAAO,EAAE,GAAG,UAAU,EAAE,QAAQ,EAAsB,CAAA;IACxD,CAAC;CACF,CAAA;AAfY,kEAA2B;AAKhC;IAJL,IAAA,oBAAK,EAAC,GAAG,EAAE,CAAC,gBAAgB,EAAE;QAC7B,WAAW,EAAE,oKAAoK;KAClL,CAAC;IACD,IAAA,wBAAS,EAAC,oFAAoF,CAAC;IAC5E,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;+DASxB;sCAdU,2BAA2B;IADvC,IAAA,uBAAQ,GAAE;GACE,2BAA2B,CAevC","sourcesContent":["/*\n * One window for one question: what state is this deployment's AI in.\n *\n * Two things were being answered by failure or by a log line — whether a provider is configured at\n * all (standard A7-a), and which registrations are not yet what the contracts ask for (A1 stage one,\n * and the modes' `/` sentences). They are the same question asked by different people, so they come\n * back together, as codes.\n *\n * A screen reads this **before** offering to ask the assistant. It is a query: reading what a\n * deployment can do is reading.\n */\nimport { Ctx, Directive, Field, ObjectType, Query, Resolver } from 'type-graphql'\nimport { GraphQLJSON } from 'graphql-scalars'\nimport '@things-factory/auth-base'\n\nimport { aiCapability, getToolRegistryWarnings } from '@things-factory/ai-client-base'\n\nimport { getAssistantModeWarnings } from './assistant-mode/index.js'\n\n@ObjectType({ description: 'A registration that was accepted but is not yet what the contract asks for.' })\nclass AiRegistrationWarning {\n @Field({ description: 'What is missing, as a code — never prose a screen has to parse.' })\n code: string\n\n @Field(() => GraphQLJSON, { description: 'Which registration it is about (category, tool, mode, slash).' })\n params: any\n\n @Field({ description: 'The same thing in a sentence, for whoever reads a boot log.' })\n message: string\n}\n\n@ObjectType({ description: \"Whether this deployment has an AI, and what its registrations are missing.\" })\nclass AiCapabilityView {\n @Field({ description: \"'configured' or 'not-configured'. A screen that has not asked yet knows a third state of its own: it does not know.\" })\n state: string\n\n @Field({ nullable: true, description: 'Which provider answers.' })\n provider?: string\n\n @Field({ nullable: true, description: 'The default model this deployment would use.' })\n model?: string\n\n @Field({ nullable: true, description: 'Why it is not configured: no-config · no-provider · unknown-provider · no-adapter.' })\n reason?: string\n\n @Field(() => [AiRegistrationWarning], { description: 'Tools with no declared privilege, and modes offering work their tools cannot do.' })\n warnings: AiRegistrationWarning[]\n}\n\n@Resolver()\nexport class AssistantCapabilityResolver {\n @Query(() => AiCapabilityView, {\n description: 'Whether this deployment has an AI configured, and which registrations are incomplete. Read this before offering to ask the assistant — do not find out by failing.'\n })\n @Directive('@privilege(category: \"ai-assistant\", privilege: \"query\", domainOwnerGranted: true)')\n async aiCapability(@Ctx() _context: any): Promise<AiCapabilityView> {\n const capability = aiCapability()\n const warnings = [...getToolRegistryWarnings(), ...getAssistantModeWarnings()].map(warning => ({\n code: warning.code,\n params: warning.params,\n message: warning.message\n }))\n\n return { ...capability, warnings } as AiCapabilityView\n }\n}\n"]}
|
|
@@ -492,7 +492,7 @@ tslib_1.__decorate([
|
|
|
492
492
|
* name, and declaring an alias would open a second name that nothing needs.
|
|
493
493
|
*/
|
|
494
494
|
,
|
|
495
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
495
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
496
496
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('input')),
|
|
497
497
|
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
498
498
|
tslib_1.__metadata("design:type", Function),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-chat-resolver.js","sourceRoot":"","sources":["../../server/service/assistant-chat-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The neutral door into a conversation.\n *\n * ── Why this exists (2026-09-11) ───────────────────────────────────────────────\n * The chat component moved here from `board-ai`, but the only mutation it could call stayed\n * behind. operato-figure mounted the dock, registered four tools, and got\n * `Cannot query field \"boardAIChat\" on type \"Mutation\"` — it does not load board-ai. The twin has\n * the same shape and it does not show: its space deliberation runs through board-ai's door purely\n * because the board authoring half of the same product already loaded that package.\n *\n * ── Why the board resolver was not moved ───────────────────────────────────────\n * `board-ai-resolver.ts` is 692 lines and the larger part of it is board authoring itself:\n * `currentBoard` as a BoardModel, PatchEntry, board patch broadcasts, refid injection for\n * component mentions. Moving that here would teach this package the board document, which is the\n * dependency we just cut, pointing the other way.\n *\n * So the door moved and the editor did not. `boardAIChat` stays in board-ai for board authoring;\n * this one is for a surface that asks questions and runs registered tools.\n *\n * ── What this is not ───────────────────────────────────────────────────────────\n * It has no document, so it takes no document and returns no patch. Tools that write to a\n * document are not offered here — `registryAssistantChat` enforces that, and it decides what may\n * be dispatched from the same list it offers, so the two cannot drift apart.\n */\nimport { Arg, Ctx, Directive, Field, InputType, Mutation, ObjectType, Resolver } from 'type-graphql'\nimport { GraphQLJSON } from 'graphql-scalars'\nimport '@things-factory/auth-base'\n\nimport { config } from '@things-factory/env'\nimport { getRepository } from '@things-factory/shell'\nimport {\n createAIClient,\n findToolSpec,\n getDefaultAIClient,\n registryAssistantChat,\n type AIClient\n} from '@things-factory/ai-client-base'\n\nimport { ChatSession } from './chat-session/chat-session.js'\nimport { assertStationSession } from './chat-session/station-session-guard.js'\nimport { ChatMessage } from './chat-message/chat-message.js'\nimport { buildLlmHistory } from './chat-message/llm-history.js'\nimport { splitByCap } from './chat-message/history-summary.js'\nimport { foldSessionHistory } from './chat-message/fold-session-history.js'\nimport { groundingSourcesFor } from './grounding-sources.js'\nimport { publishChatMessage } from './chat-message/chat-message-publish.js'\nimport { publishSessionActivity } from './chat-session/session-activity-publish.js'\nimport { activityPatch, autoSessionName } from './chat-session/session-inbox.js'\nimport { toolAccessFor } from './tool-access.js'\nimport { ChatSessionParticipant } from './chat-session-participant/chat-session-participant.js'\nimport { doorsForAnswer } from './chat-message/message-doors.js'\nimport { judgeSessionKind } from './chat-session/session-kind.js'\nimport { readerGate } from './chat-message/reader-gate.js'\nimport { inviteMentionedUsers } from './chat-session-participant/mention-invite.js'\nimport { buildInviteNotification } from './chat-session-participant/invite-notification.js'\nimport { resolveModeSettings } from './assistant-mode/resolve.js'\nimport { requestTranslator } from './request-translator.js'\n\n/**\n * How many stored messages reach the model.\n *\n * A deliberation runs for days. Without a cap the prompt grows without bound and the oldest turns\n * bury the recent ones. Kept the same as the board conversation's cap so the two surfaces of one\n * product do not remember differently.\n */\nconst LLM_HISTORY_MAX_TURNS = 40\n\n@InputType()\nclass AssistantMessageInput {\n @Field({ description: \"Role: 'user' or 'assistant'\" })\n role: string\n\n @Field({ description: 'Message content' })\n content: string\n}\n\n@InputType({ description: 'Which conversation this is, and what it is about.' })\nclass AssistantModeInput {\n @Field({ description: 'A mode kind the app server registered — `<app>.<surface>`, e.g. \"twin.space\".' })\n kind: string\n\n @Field({\n nullable: true,\n description:\n 'What this conversation is about — the space, the station, the model being edited. With `kind` it is the conversation\\'s lifetime: when either changes the screen closes the conversation and starts another.'\n })\n target?: string\n}\n\n@InputType({ description: 'One turn of a conversation that has no document of its own.' })\nclass AssistantChatInput {\n @Field({\n nullable: true,\n description: 'ChatSession id. Omit for a one-off question with no persistence.'\n })\n sessionId?: string\n\n @Field(() => [AssistantMessageInput], {\n description:\n 'Conversation, newest last. For a persisted session only the last user message is appended; the rest is read from storage.'\n })\n messages: AssistantMessageInput[]\n\n @Field(() => AssistantModeInput, {\n description:\n 'Which conversation this is. The persona, the tool categories, the model and the limits come from the mode the app server registered (ADR-0061 decision 7) — a caller choosing its own tool categories would be a caller choosing its own authority.'\n })\n mode: AssistantModeInput\n\n @Field(() => GraphQLJSON, {\n nullable: true,\n description:\n 'What this surface is looking at (e.g. { figureId } while modelling). Tools read it so the model never has to ask the user for identifiers. Client-supplied — never used for authorization; a tool verifies the scope belongs to the caller tenant.'\n })\n hostContext?: any\n\n @Field({\n nullable: true,\n description:\n 'Last message to keep from the stored history. The client keeps its own truncation when a user edits a message, so pass the last kept id to hide the retracted span from the model.'\n })\n truncateAfterMessageId?: string\n\n}\n\n@ObjectType()\nclass AssistantChatOutput {\n @Field({ description: 'The reply, including the notice when the loop stopped early.' })\n reply: string\n\n @Field({ description: 'AI client identifier (provider:model).' })\n clientId: string\n\n @Field({ nullable: true, description: 'Echo of the session id, when persisted.' })\n sessionId?: string\n\n @Field({ nullable: true, description: 'Stored ChatMessage id of the user input.' })\n userMessageId?: string\n\n @Field({ nullable: true, description: 'Stored ChatMessage id of the reply.' })\n assistantMessageId?: string\n\n @Field(() => GraphQLJSON, {\n nullable: true,\n description:\n 'Set when this turn did not fit: { omitted, summarized } — how many earlier messages the model did not see, and whether a summary stood in for them. Without it the person watches the conversation lose its beginning in silence, while the model is told.'\n })\n historyFolded?: any\n\n @Field(() => GraphQLJSON, {\n nullable: true,\n description: 'Tool usages during the turn — {name, arguments, result, kind}. The surface shows them foldably.'\n })\n toolUsages?: any\n\n @Field(() => [String], {\n nullable: true,\n description:\n 'Tool names offered to the model this turn. A surface that expected a category and sees it missing here knows the registration did not happen, rather than reading a vague answer as a refusal.'\n })\n offeredTools?: string[]\n\n @Field(() => GraphQLJSON, {\n nullable: true,\n description:\n 'Actions a tool recorded without executing — each carries what the tool returned alongside `proposed: true`. The surface renders a confirm button; executing is the user\\'s act.'\n })\n proposals?: any\n\n @Field(() => GraphQLJSON, {\n nullable: true,\n description:\n 'Identifiers in the reply that appear in nothing the model received. Hallucination candidates surfaced to the user; the reply itself is not altered. Null when the answer is grounded.'\n })\n groundingWarnings?: any\n}\n\nfunction resolveAIClient(): AIClient | undefined {\n const existing = getDefaultAIClient()\n if (existing) return existing\n const cfg = config.get('aiClient', null) as any\n if (cfg && cfg.provider) return createAIClient(cfg)\n return undefined\n}\n\n@Resolver()\nexport class AssistantChatResolver {\n /*\n * No `@transaction`, for the same reason the board conversation has none: a model call takes\n * seconds to tens of seconds, and holding a write lock for that long collides with everything\n * else on SQLite. Writes are short and around the call. For a conversation, a partly stored\n * history is safer than a lost one.\n */\n @Mutation(() => AssistantChatOutput, {\n description:\n 'Ask the assistant a question on a surface that has no document of its own. Tools come from the registry; with a sessionId the conversation is persisted.'\n })\n /*\n * `ai-assistant:mutation` — the same door as the board conversation's, which moved here on\n * 2026-09-10. No `legacy` alias: this operation is new, so no one was ever passing under an old\n * name, and declaring an alias would open a second name that nothing needs.\n */\n @Directive('@privilege(category: \"ai-assistant\", privilege: \"mutation\")')\n async assistantChat(@Arg('input') input: AssistantChatInput, @Ctx() context: any): Promise<AssistantChatOutput> {\n const client = resolveAIClient()\n if (!client) {\n throw new Error(\n 'No AI client configured. Set config.aiClient = { provider, model, apiKey } or call setDefaultAIClient(...).'\n )\n }\n\n /*\n * Read the declaration before anything is stored. A kind this server does not have is a\n * mismatched deployment, and the person should see that instead of a conversation with no\n * persona and no tools.\n */\n const t = requestTranslator(context)\n const settings = resolveModeSettings(input.mode, t)\n\n const { domain, user } = context.state\n const sessionRepo = getRepository(ChatSession)\n const messageRepo = getRepository(ChatMessage)\n\n let session: ChatSession | undefined\n if (input.sessionId) {\n const found = await sessionRepo.findOneBy({ id: input.sessionId, domain: { id: domain.id } as any })\n if (!found) throw new Error(`ChatSession ${input.sessionId} not found`)\n if (found.anchorType === 'figure' && input.hostContext?.figureId !== found.anchorId) {\n throw new Error(\n context.t('ai-assistant.text.other-figure-history', {\n defaultValue: 'this conversation belongs to another figure. open the conversation of the model you have open.'\n })\n )\n }\n if (found.anchorType === 'application' && (input.hostContext?.app !== found.anchorId || input.hostContext?.figureId)) {\n throw new Error(\n context.t('ai-assistant.text.application-vs-model-history', {\n defaultValue: 'this conversation is about the application, not about a model. open the one you want.'\n })\n )\n }\n /*\n * 이 세션이 **이 대화의** 세션인가 — ADR-0072 결정 3. 다른 대화의 세션에 말을 붙이면 그 이력이\n * 이 대화의 프롬프트로 들어가고, 화면은 줄이 사라져 새 대화처럼 보이므로 보낸 사람은 모른다.\n */\n const verdict = judgeSessionKind((found as any).kind, input.mode.kind)\n if (verdict.action === 'refuse') {\n throw new Error(\n t('ai-assistant.text.session-kind-mismatch', {\n ...verdict.params,\n defaultValue: 'this conversation is {sessionKind}, and you are writing as {requested}. open the conversation you mean.'\n })\n )\n }\n if (verdict.action === 'stamp') {\n /* 칸이 서기 전에 난 세션 — 요청한 대화로 한 번 찍는다. 다음부터는 위 규칙 하나만 남는다. */\n await sessionRepo.update(found.id!, { kind: verdict.kind } as any)\n ;(found as any).kind = verdict.kind\n }\n session = found\n }\n\n assertStationSession(session, input.hostContext, t)\n\n const lastUserMessage = input.messages[input.messages.length - 1]\n let userMessageId: string | undefined\n\n if (session && lastUserMessage?.role === 'user') {\n const previousLast = await messageRepo.findOne({\n where: { session: { id: session.id } as any },\n order: { createdAt: 'DESC' }\n })\n const saved = await messageRepo.save({\n session: { id: session.id } as any,\n role: 'user',\n content: lastUserMessage.content,\n /*\n * This door is the one people call to ask the assistant, so a turn stored here was for the\n * assistant (ADR-0070 A15 decision ①). The value comes from which door was called, never\n * from the request — two sources for one fact can disagree.\n */\n addressee: 'assistant',\n creator: user,\n updater: user,\n parentMessage: previousLast ? ({ id: previousLast.id } as any) : undefined\n })\n userMessageId = saved.id\n\n /*\n * Session activity is denormalised for the list: most-recent ordering and another\n * participant's unread badge read it. Best effort — a conversation must not fail because a\n * list sorts one position late.\n */\n try {\n const patch: any = activityPatch(lastUserMessage.content)\n if (!session.name) patch.name = autoSessionName(lastUserMessage.content)\n await sessionRepo.update(session.id!, patch)\n } catch {\n /* noop */\n }\n\n /*\n * 사람이 `@이름` 으로 부른 사람은 이 대화에 들어온다(ADR-0071 결정 1). 이 문은 **사람의 턴**을\n * 저장하는 문이라 그 일을 한다 — 모델의 답이나 도구 결과는 아무도 초대하지 않는다.\n */\n const invite = await inviteMentionedUsers({\n content: lastUserMessage.content,\n sessionId: session.id!,\n domainId: domain.id,\n inviter: user,\n participants: getRepository(ChatSessionParticipant),\n visibility: (session as any).visibility\n })\n if (invite.invited.length > 0) {\n const payload = buildInviteNotification(invite.invited, undefined, user)\n try {\n if (payload) await (context.state as any)?.notify?.(payload)\n } catch {\n /* 편지가 못 갔다고 방에 들어온 사실이 취소되지는 않는다. */\n }\n }\n\n publishChatMessage({ domainId: domain.id, sessionId: session.id, message: saved } as any)\n publishSessionActivity({\n domainId: domain.id,\n sessionId: session.id!,\n kind: 'message',\n anchorType: (session as any).anchorType,\n anchorId: (session as any).anchorId,\n preview: lastUserMessage.content,\n actorId: user?.id\n })\n }\n\n /*\n * 이번 턴에 상한 밖으로 밀려난 것이 있는가 — 화면이 「대화가 길어졌다」를 알릴 근거다.\n *\n * 이 문은 그 사실을 내지 않았다. 대화창에는 그 안내를 그리는 자리가 있고(`historyFolded`), 보드\n * 문만 그 값을 채웠다. 그래서 앱의 대화는 앞부분이 잘려 나가도 **사람에게는 아무 말도 하지 않았다** —\n * 모델에게는 「N 개 생략」을 적어 보내면서.\n */\n const readRows = session\n ? await messageRepo.find({\n where: { session: { id: session.id } as any },\n order: { createdAt: 'ASC' },\n relations: ['creator']\n })\n : []\n /*\n * 모델이 읽는 이력도 **부른 사람** 기준으로 가린다 — ADR-0073 판정 1. 가리지 않으면, 문을 한 번도\n * 안 지난 사람이 \"아까 그 수 다시 말해 줘\" 한 마디로 값을 가져간다. 모델이 옮겨 적는 순간 그 값은\n * 문 없는 새 답이 된다. 가린 자리를 지우지 않고 한 줄로 남기는 이유도 같다 — 지우면 모델이 그런\n * 말이 없었다고 답하고, 그것은 거짓말이다.\n */\n const storedRows = await readerGate(context)(readRows)\n const capped = splitByCap(\n storedRows.map(row => ({ id: row.id, role: row.role, content: row.content ?? '' })),\n LLM_HISTORY_MAX_TURNS\n )\n const historyFolded =\n capped.dropped.length > 0\n ? { omitted: capped.dropped.length, summarized: Boolean((session as any)?.lastSummary) }\n : undefined\n\n const conversation = session\n ? buildLlmHistory(\n /*\n * The model reads who spoke and who they spoke to. The entity carries the speaker as a\n * relation and the addressee as a column; the history wants them as plain fields, and\n * without this mapping every line arrived unattributed — a room of people read as one\n * voice (ADR-0070 A15 decision ②).\n */\n storedRows.map(message => ({\n id: message.id,\n role: message.role,\n content: message.content,\n senderId: (message as any).creator?.id,\n senderName: (message as any).creator?.name,\n addressee: (message as any).addressee\n })),\n {\n maxTurns: LLM_HISTORY_MAX_TURNS,\n truncateAfterMessageId: input.truncateAfterMessageId,\n /* The message just stored is this turn's question — truncation must never drop it. */\n keepMessageIds: userMessageId ? [userMessageId] : undefined,\n summary: (session as any).lastSummary\n ? { text: (session as any).lastSummary, upToMessageId: (session as any).summaryUpToMessageId }\n : undefined\n }\n )\n : input.messages.map(message => ({ role: message.role as any, content: message.content }))\n\n if (input.hostContext?.image?.data && conversation.length > 0) {\n const last = conversation[conversation.length - 1]\n if (last.role === 'user' && typeof last.content === 'string') {\n const raw = input.hostContext.image\n const rawData = typeof raw.data === 'string' ? raw.data.replace(/^data:image\\/[a-zA-Z]+;base64,/, '') : raw.data\n const buf = typeof rawData === 'string' ? Buffer.from(rawData, 'base64') : Buffer.from(rawData)\n last.content = [\n { type: 'text', text: last.content },\n { type: 'image', source: { kind: 'base64', data: buf, mediaType: raw.mediaType || 'image/png' } }\n ] as any\n }\n }\n\n const result = await registryAssistantChat({\n client,\n messages: conversation as any,\n systemPrompt: settings.systemPrompt,\n toolCategories: settings.toolCategories,\n /* 도구가 「어느 대화에서 불렸나」를 안다 — 서버의 값이다(ADR-0061 결정 3). */\n mode: { kind: input.mode.kind, ...(input.mode.target ? { target: input.mode.target } : {}) },\n toolAccess: toolAccessFor(context),\n /* The stop notice is a sentence a person reads — the server writes it in their language (ADR-0054). */\n t,\n hostContext: input.hostContext,\n /*\n * 근거는 도구 결과만이 아니다 — 시스템 프롬프트 · 이 대화의 말 · 화면이 보낸 스냅숏도 근거다\n * (`ai-client-base/utils/grounding`). 이 문은 그 셋을 넘기지 않아, **사람이 방금 타이핑한\n * 식별자**를 되받아 적은 답이 「근거 없음」으로 표시됐다. 그런 경고가 몇 번 나오면 사람이 경고\n * 자체를 안 보게 된다.\n */\n groundingSources: groundingSourcesFor({\n systemPrompt: settings.systemPrompt,\n messages: conversation as any,\n hostContext: input.hostContext\n }),\n state: context.state,\n model: settings.model,\n temperature: settings.temperature,\n maxTokens: settings.maxTokens,\n requireToolOnFirstTurn: settings.requireToolOnFirstTurn\n })\n\n /*\n * 이 답이 지나온 문 — ADR-0073 판정 1. 행에도, 목록 미리보기에도 같은 값이 간다. 한쪽만 적으면\n * 가려진 답이 목록에는 그대로 뜬다.\n */\n const answerDoors = doorsForAnswer(\n (result.loop.toolUsages ?? []).map((usage: any) => usage?.name).filter(Boolean),\n name => (findToolSpec(name, settings.toolCategories) as any)?.doors\n )\n\n let assistantMessageId: string | undefined\n if (session) {\n const stored = await messageRepo.save({\n session: { id: session.id } as any,\n role: 'assistant',\n content: result.reply,\n /*\n * 이 답이 지나온 문을 함께 적는다 — ADR-0073 판정 1. 방은 도메인 안에서 공개이므로, 이 문장이\n * 실어 나르는 값을 나중에 읽는 사람이 열 수 있는지 판정할 근거가 행에 있어야 한다. 적지 않으면\n * 빈 목록이 되고, 빈 목록은 누구나 읽는다는 뜻이다.\n */\n doors: answerDoors,\n toolUsages: result.loop.toolUsages?.length ? JSON.stringify(result.loop.toolUsages) : undefined,\n groundingWarnings: result.groundingWarnings?.length ? JSON.stringify(result.groundingWarnings) : undefined,\n creator: user,\n updater: user,\n parentMessage: userMessageId ? ({ id: userMessageId } as any) : undefined\n })\n assistantMessageId = stored.id\n\n publishChatMessage({ domainId: domain.id, sessionId: session.id, message: stored } as any)\n /* The reply is session activity too. Leaving it out shows other participants \"nothing new\". */\n publishSessionActivity({\n domainId: domain.id,\n sessionId: session.id!,\n kind: 'message',\n anchorType: (session as any).anchorType,\n anchorId: (session as any).anchorId,\n preview: result.reply,\n actorId: user?.id\n })\n\n await sessionRepo.update(session.id!, {\n aiClientId: client.id,\n updater: user,\n ...activityPatch(result.reply, new Date(), answerDoors)\n } as any)\n\n /*\n * 상한 밖으로 밀려난 앞부분을 요약으로 접는다 — **답을 돌려준 뒤**에 한다(사용자 대기 시간에\n * 요약 호출을 얹지 않는다). 기다리지 않으므로 실패해도 대화에는 영향이 없다: 요약이 없으면 다음\n * 턴이 「N 개 생략」으로 정직하게 알린다.\n *\n * 이 문에는 이 호출이 없었다. 조각은 다 있었는데(상한 · 생략 알림 · `lastSummary` · 순수 규칙)\n * 부르는 쪽이 보드 제품 하나뿐이어서, 앱의 대화는 며칠이 지나도 앞부분을 요약하지 않고 매 턴\n * 「생략했다」고만 말했다 — 협의가 길어질수록 처음에 합의한 것이 먼저 사라진다.\n */\n void foldSessionHistory({\n sessionId: session.id!,\n domainId: domain.id,\n client,\n maxTurns: LLM_HISTORY_MAX_TURNS,\n locale: context.state?.locale\n })\n }\n\n return {\n reply: result.reply,\n clientId: client.id,\n sessionId: session?.id,\n userMessageId,\n assistantMessageId,\n toolUsages: result.loop.toolUsages?.length ? result.loop.toolUsages : null,\n offeredTools: result.offeredTools,\n proposals: result.loop.proposals?.length ? result.loop.proposals : null,\n groundingWarnings: result.groundingWarnings?.length ? result.groundingWarnings : null,\n historyFolded: historyFolded ?? null\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assistant-chat-resolver.js","sourceRoot":"","sources":["../../server/service/assistant-chat-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The neutral door into a conversation.\n *\n * ── Why this exists (2026-09-11) ───────────────────────────────────────────────\n * The chat component moved here from `board-ai`, but the only mutation it could call stayed\n * behind. operato-figure mounted the dock, registered four tools, and got\n * `Cannot query field \"boardAIChat\" on type \"Mutation\"` — it does not load board-ai. The twin has\n * the same shape and it does not show: its space deliberation runs through board-ai's door purely\n * because the board authoring half of the same product already loaded that package.\n *\n * ── Why the board resolver was not moved ───────────────────────────────────────\n * `board-ai-resolver.ts` is 692 lines and the larger part of it is board authoring itself:\n * `currentBoard` as a BoardModel, PatchEntry, board patch broadcasts, refid injection for\n * component mentions. Moving that here would teach this package the board document, which is the\n * dependency we just cut, pointing the other way.\n *\n * So the door moved and the editor did not. `boardAIChat` stays in board-ai for board authoring;\n * this one is for a surface that asks questions and runs registered tools.\n *\n * ── What this is not ───────────────────────────────────────────────────────────\n * It has no document, so it takes no document and returns no patch. Tools that write to a\n * document are not offered here — `registryAssistantChat` enforces that, and it decides what may\n * be dispatched from the same list it offers, so the two cannot drift apart.\n */\nimport { Arg, Ctx, Directive, Field, InputType, Mutation, ObjectType, Resolver } from 'type-graphql'\nimport { GraphQLJSON } from 'graphql-scalars'\nimport '@things-factory/auth-base'\n\nimport { config } from '@things-factory/env'\nimport { getRepository } from '@things-factory/shell'\nimport {\n createAIClient,\n findToolSpec,\n getDefaultAIClient,\n registryAssistantChat,\n type AIClient\n} from '@things-factory/ai-client-base'\n\nimport { ChatSession } from './chat-session/chat-session.js'\nimport { assertStationSession } from './chat-session/station-session-guard.js'\nimport { ChatMessage } from './chat-message/chat-message.js'\nimport { buildLlmHistory } from './chat-message/llm-history.js'\nimport { splitByCap } from './chat-message/history-summary.js'\nimport { foldSessionHistory } from './chat-message/fold-session-history.js'\nimport { groundingSourcesFor } from './grounding-sources.js'\nimport { publishChatMessage } from './chat-message/chat-message-publish.js'\nimport { publishSessionActivity } from './chat-session/session-activity-publish.js'\nimport { activityPatch, autoSessionName } from './chat-session/session-inbox.js'\nimport { toolAccessFor } from './tool-access.js'\nimport { ChatSessionParticipant } from './chat-session-participant/chat-session-participant.js'\nimport { doorsForAnswer } from './chat-message/message-doors.js'\nimport { judgeSessionKind } from './chat-session/session-kind.js'\nimport { readerGate } from './chat-message/reader-gate.js'\nimport { inviteMentionedUsers } from './chat-session-participant/mention-invite.js'\nimport { buildInviteNotification } from './chat-session-participant/invite-notification.js'\nimport { resolveModeSettings } from './assistant-mode/resolve.js'\nimport { requestTranslator } from './request-translator.js'\n\n/**\n * How many stored messages reach the model.\n *\n * A deliberation runs for days. Without a cap the prompt grows without bound and the oldest turns\n * bury the recent ones. Kept the same as the board conversation's cap so the two surfaces of one\n * product do not remember differently.\n */\nconst LLM_HISTORY_MAX_TURNS = 40\n\n@InputType()\nclass AssistantMessageInput {\n @Field({ description: \"Role: 'user' or 'assistant'\" })\n role: string\n\n @Field({ description: 'Message content' })\n content: string\n}\n\n@InputType({ description: 'Which conversation this is, and what it is about.' })\nclass AssistantModeInput {\n @Field({ description: 'A mode kind the app server registered — `<app>.<surface>`, e.g. \"twin.space\".' })\n kind: string\n\n @Field({\n nullable: true,\n description:\n 'What this conversation is about — the space, the station, the model being edited. With `kind` it is the conversation\\'s lifetime: when either changes the screen closes the conversation and starts another.'\n })\n target?: string\n}\n\n@InputType({ description: 'One turn of a conversation that has no document of its own.' })\nclass AssistantChatInput {\n @Field({\n nullable: true,\n description: 'ChatSession id. Omit for a one-off question with no persistence.'\n })\n sessionId?: string\n\n @Field(() => [AssistantMessageInput], {\n description:\n 'Conversation, newest last. For a persisted session only the last user message is appended; the rest is read from storage.'\n })\n messages: AssistantMessageInput[]\n\n @Field(() => AssistantModeInput, {\n description:\n 'Which conversation this is. The persona, the tool categories, the model and the limits come from the mode the app server registered (ADR-0061 decision 7) — a caller choosing its own tool categories would be a caller choosing its own authority.'\n })\n mode: AssistantModeInput\n\n @Field(() => GraphQLJSON, {\n nullable: true,\n description:\n 'What this surface is looking at (e.g. { figureId } while modelling). Tools read it so the model never has to ask the user for identifiers. Client-supplied — never used for authorization; a tool verifies the scope belongs to the caller tenant.'\n })\n hostContext?: any\n\n @Field({\n nullable: true,\n description:\n 'Last message to keep from the stored history. The client keeps its own truncation when a user edits a message, so pass the last kept id to hide the retracted span from the model.'\n })\n truncateAfterMessageId?: string\n\n}\n\n@ObjectType()\nclass AssistantChatOutput {\n @Field({ description: 'The reply, including the notice when the loop stopped early.' })\n reply: string\n\n @Field({ description: 'AI client identifier (provider:model).' })\n clientId: string\n\n @Field({ nullable: true, description: 'Echo of the session id, when persisted.' })\n sessionId?: string\n\n @Field({ nullable: true, description: 'Stored ChatMessage id of the user input.' })\n userMessageId?: string\n\n @Field({ nullable: true, description: 'Stored ChatMessage id of the reply.' })\n assistantMessageId?: string\n\n @Field(() => GraphQLJSON, {\n nullable: true,\n description:\n 'Set when this turn did not fit: { omitted, summarized } — how many earlier messages the model did not see, and whether a summary stood in for them. Without it the person watches the conversation lose its beginning in silence, while the model is told.'\n })\n historyFolded?: any\n\n @Field(() => GraphQLJSON, {\n nullable: true,\n description: 'Tool usages during the turn — {name, arguments, result, kind}. The surface shows them foldably.'\n })\n toolUsages?: any\n\n @Field(() => [String], {\n nullable: true,\n description:\n 'Tool names offered to the model this turn. A surface that expected a category and sees it missing here knows the registration did not happen, rather than reading a vague answer as a refusal.'\n })\n offeredTools?: string[]\n\n @Field(() => GraphQLJSON, {\n nullable: true,\n description:\n 'Actions a tool recorded without executing — each carries what the tool returned alongside `proposed: true`. The surface renders a confirm button; executing is the user\\'s act.'\n })\n proposals?: any\n\n @Field(() => GraphQLJSON, {\n nullable: true,\n description:\n 'Identifiers in the reply that appear in nothing the model received. Hallucination candidates surfaced to the user; the reply itself is not altered. Null when the answer is grounded.'\n })\n groundingWarnings?: any\n}\n\nfunction resolveAIClient(): AIClient | undefined {\n const existing = getDefaultAIClient()\n if (existing) return existing\n const cfg = config.get('aiClient', null) as any\n if (cfg && cfg.provider) return createAIClient(cfg)\n return undefined\n}\n\n@Resolver()\nexport class AssistantChatResolver {\n /*\n * No `@transaction`, for the same reason the board conversation has none: a model call takes\n * seconds to tens of seconds, and holding a write lock for that long collides with everything\n * else on SQLite. Writes are short and around the call. For a conversation, a partly stored\n * history is safer than a lost one.\n */\n @Mutation(() => AssistantChatOutput, {\n description:\n 'Ask the assistant a question on a surface that has no document of its own. Tools come from the registry; with a sessionId the conversation is persisted.'\n })\n /*\n * `ai-assistant:mutation` — the same door as the board conversation's, which moved here on\n * 2026-09-10. No `legacy` alias: this operation is new, so no one was ever passing under an old\n * name, and declaring an alias would open a second name that nothing needs.\n */\n @Directive('@privilege(category: \"ai-assistant\", privilege: \"mutation\", domainOwnerGranted: true)')\n async assistantChat(@Arg('input') input: AssistantChatInput, @Ctx() context: any): Promise<AssistantChatOutput> {\n const client = resolveAIClient()\n if (!client) {\n throw new Error(\n 'No AI client configured. Set config.aiClient = { provider, model, apiKey } or call setDefaultAIClient(...).'\n )\n }\n\n /*\n * Read the declaration before anything is stored. A kind this server does not have is a\n * mismatched deployment, and the person should see that instead of a conversation with no\n * persona and no tools.\n */\n const t = requestTranslator(context)\n const settings = resolveModeSettings(input.mode, t)\n\n const { domain, user } = context.state\n const sessionRepo = getRepository(ChatSession)\n const messageRepo = getRepository(ChatMessage)\n\n let session: ChatSession | undefined\n if (input.sessionId) {\n const found = await sessionRepo.findOneBy({ id: input.sessionId, domain: { id: domain.id } as any })\n if (!found) throw new Error(`ChatSession ${input.sessionId} not found`)\n if (found.anchorType === 'figure' && input.hostContext?.figureId !== found.anchorId) {\n throw new Error(\n context.t('ai-assistant.text.other-figure-history', {\n defaultValue: 'this conversation belongs to another figure. open the conversation of the model you have open.'\n })\n )\n }\n if (found.anchorType === 'application' && (input.hostContext?.app !== found.anchorId || input.hostContext?.figureId)) {\n throw new Error(\n context.t('ai-assistant.text.application-vs-model-history', {\n defaultValue: 'this conversation is about the application, not about a model. open the one you want.'\n })\n )\n }\n /*\n * 이 세션이 **이 대화의** 세션인가 — ADR-0072 결정 3. 다른 대화의 세션에 말을 붙이면 그 이력이\n * 이 대화의 프롬프트로 들어가고, 화면은 줄이 사라져 새 대화처럼 보이므로 보낸 사람은 모른다.\n */\n const verdict = judgeSessionKind((found as any).kind, input.mode.kind)\n if (verdict.action === 'refuse') {\n throw new Error(\n t('ai-assistant.text.session-kind-mismatch', {\n ...verdict.params,\n defaultValue: 'this conversation is {sessionKind}, and you are writing as {requested}. open the conversation you mean.'\n })\n )\n }\n if (verdict.action === 'stamp') {\n /* 칸이 서기 전에 난 세션 — 요청한 대화로 한 번 찍는다. 다음부터는 위 규칙 하나만 남는다. */\n await sessionRepo.update(found.id!, { kind: verdict.kind } as any)\n ;(found as any).kind = verdict.kind\n }\n session = found\n }\n\n assertStationSession(session, input.hostContext, t)\n\n const lastUserMessage = input.messages[input.messages.length - 1]\n let userMessageId: string | undefined\n\n if (session && lastUserMessage?.role === 'user') {\n const previousLast = await messageRepo.findOne({\n where: { session: { id: session.id } as any },\n order: { createdAt: 'DESC' }\n })\n const saved = await messageRepo.save({\n session: { id: session.id } as any,\n role: 'user',\n content: lastUserMessage.content,\n /*\n * This door is the one people call to ask the assistant, so a turn stored here was for the\n * assistant (ADR-0070 A15 decision ①). The value comes from which door was called, never\n * from the request — two sources for one fact can disagree.\n */\n addressee: 'assistant',\n creator: user,\n updater: user,\n parentMessage: previousLast ? ({ id: previousLast.id } as any) : undefined\n })\n userMessageId = saved.id\n\n /*\n * Session activity is denormalised for the list: most-recent ordering and another\n * participant's unread badge read it. Best effort — a conversation must not fail because a\n * list sorts one position late.\n */\n try {\n const patch: any = activityPatch(lastUserMessage.content)\n if (!session.name) patch.name = autoSessionName(lastUserMessage.content)\n await sessionRepo.update(session.id!, patch)\n } catch {\n /* noop */\n }\n\n /*\n * 사람이 `@이름` 으로 부른 사람은 이 대화에 들어온다(ADR-0071 결정 1). 이 문은 **사람의 턴**을\n * 저장하는 문이라 그 일을 한다 — 모델의 답이나 도구 결과는 아무도 초대하지 않는다.\n */\n const invite = await inviteMentionedUsers({\n content: lastUserMessage.content,\n sessionId: session.id!,\n domainId: domain.id,\n inviter: user,\n participants: getRepository(ChatSessionParticipant),\n visibility: (session as any).visibility\n })\n if (invite.invited.length > 0) {\n const payload = buildInviteNotification(invite.invited, undefined, user)\n try {\n if (payload) await (context.state as any)?.notify?.(payload)\n } catch {\n /* 편지가 못 갔다고 방에 들어온 사실이 취소되지는 않는다. */\n }\n }\n\n publishChatMessage({ domainId: domain.id, sessionId: session.id, message: saved } as any)\n publishSessionActivity({\n domainId: domain.id,\n sessionId: session.id!,\n kind: 'message',\n anchorType: (session as any).anchorType,\n anchorId: (session as any).anchorId,\n preview: lastUserMessage.content,\n actorId: user?.id\n })\n }\n\n /*\n * 이번 턴에 상한 밖으로 밀려난 것이 있는가 — 화면이 「대화가 길어졌다」를 알릴 근거다.\n *\n * 이 문은 그 사실을 내지 않았다. 대화창에는 그 안내를 그리는 자리가 있고(`historyFolded`), 보드\n * 문만 그 값을 채웠다. 그래서 앱의 대화는 앞부분이 잘려 나가도 **사람에게는 아무 말도 하지 않았다** —\n * 모델에게는 「N 개 생략」을 적어 보내면서.\n */\n const readRows = session\n ? await messageRepo.find({\n where: { session: { id: session.id } as any },\n order: { createdAt: 'ASC' },\n relations: ['creator']\n })\n : []\n /*\n * 모델이 읽는 이력도 **부른 사람** 기준으로 가린다 — ADR-0073 판정 1. 가리지 않으면, 문을 한 번도\n * 안 지난 사람이 \"아까 그 수 다시 말해 줘\" 한 마디로 값을 가져간다. 모델이 옮겨 적는 순간 그 값은\n * 문 없는 새 답이 된다. 가린 자리를 지우지 않고 한 줄로 남기는 이유도 같다 — 지우면 모델이 그런\n * 말이 없었다고 답하고, 그것은 거짓말이다.\n */\n const storedRows = await readerGate(context)(readRows)\n const capped = splitByCap(\n storedRows.map(row => ({ id: row.id, role: row.role, content: row.content ?? '' })),\n LLM_HISTORY_MAX_TURNS\n )\n const historyFolded =\n capped.dropped.length > 0\n ? { omitted: capped.dropped.length, summarized: Boolean((session as any)?.lastSummary) }\n : undefined\n\n const conversation = session\n ? buildLlmHistory(\n /*\n * The model reads who spoke and who they spoke to. The entity carries the speaker as a\n * relation and the addressee as a column; the history wants them as plain fields, and\n * without this mapping every line arrived unattributed — a room of people read as one\n * voice (ADR-0070 A15 decision ②).\n */\n storedRows.map(message => ({\n id: message.id,\n role: message.role,\n content: message.content,\n senderId: (message as any).creator?.id,\n senderName: (message as any).creator?.name,\n addressee: (message as any).addressee\n })),\n {\n maxTurns: LLM_HISTORY_MAX_TURNS,\n truncateAfterMessageId: input.truncateAfterMessageId,\n /* The message just stored is this turn's question — truncation must never drop it. */\n keepMessageIds: userMessageId ? [userMessageId] : undefined,\n summary: (session as any).lastSummary\n ? { text: (session as any).lastSummary, upToMessageId: (session as any).summaryUpToMessageId }\n : undefined\n }\n )\n : input.messages.map(message => ({ role: message.role as any, content: message.content }))\n\n if (input.hostContext?.image?.data && conversation.length > 0) {\n const last = conversation[conversation.length - 1]\n if (last.role === 'user' && typeof last.content === 'string') {\n const raw = input.hostContext.image\n const rawData = typeof raw.data === 'string' ? raw.data.replace(/^data:image\\/[a-zA-Z]+;base64,/, '') : raw.data\n const buf = typeof rawData === 'string' ? Buffer.from(rawData, 'base64') : Buffer.from(rawData)\n last.content = [\n { type: 'text', text: last.content },\n { type: 'image', source: { kind: 'base64', data: buf, mediaType: raw.mediaType || 'image/png' } }\n ] as any\n }\n }\n\n const result = await registryAssistantChat({\n client,\n messages: conversation as any,\n systemPrompt: settings.systemPrompt,\n toolCategories: settings.toolCategories,\n /* 도구가 「어느 대화에서 불렸나」를 안다 — 서버의 값이다(ADR-0061 결정 3). */\n mode: { kind: input.mode.kind, ...(input.mode.target ? { target: input.mode.target } : {}) },\n toolAccess: toolAccessFor(context),\n /* The stop notice is a sentence a person reads — the server writes it in their language (ADR-0054). */\n t,\n hostContext: input.hostContext,\n /*\n * 근거는 도구 결과만이 아니다 — 시스템 프롬프트 · 이 대화의 말 · 화면이 보낸 스냅숏도 근거다\n * (`ai-client-base/utils/grounding`). 이 문은 그 셋을 넘기지 않아, **사람이 방금 타이핑한\n * 식별자**를 되받아 적은 답이 「근거 없음」으로 표시됐다. 그런 경고가 몇 번 나오면 사람이 경고\n * 자체를 안 보게 된다.\n */\n groundingSources: groundingSourcesFor({\n systemPrompt: settings.systemPrompt,\n messages: conversation as any,\n hostContext: input.hostContext\n }),\n state: context.state,\n model: settings.model,\n temperature: settings.temperature,\n maxTokens: settings.maxTokens,\n requireToolOnFirstTurn: settings.requireToolOnFirstTurn\n })\n\n /*\n * 이 답이 지나온 문 — ADR-0073 판정 1. 행에도, 목록 미리보기에도 같은 값이 간다. 한쪽만 적으면\n * 가려진 답이 목록에는 그대로 뜬다.\n */\n const answerDoors = doorsForAnswer(\n (result.loop.toolUsages ?? []).map((usage: any) => usage?.name).filter(Boolean),\n name => (findToolSpec(name, settings.toolCategories) as any)?.doors\n )\n\n let assistantMessageId: string | undefined\n if (session) {\n const stored = await messageRepo.save({\n session: { id: session.id } as any,\n role: 'assistant',\n content: result.reply,\n /*\n * 이 답이 지나온 문을 함께 적는다 — ADR-0073 판정 1. 방은 도메인 안에서 공개이므로, 이 문장이\n * 실어 나르는 값을 나중에 읽는 사람이 열 수 있는지 판정할 근거가 행에 있어야 한다. 적지 않으면\n * 빈 목록이 되고, 빈 목록은 누구나 읽는다는 뜻이다.\n */\n doors: answerDoors,\n toolUsages: result.loop.toolUsages?.length ? JSON.stringify(result.loop.toolUsages) : undefined,\n groundingWarnings: result.groundingWarnings?.length ? JSON.stringify(result.groundingWarnings) : undefined,\n creator: user,\n updater: user,\n parentMessage: userMessageId ? ({ id: userMessageId } as any) : undefined\n })\n assistantMessageId = stored.id\n\n publishChatMessage({ domainId: domain.id, sessionId: session.id, message: stored } as any)\n /* The reply is session activity too. Leaving it out shows other participants \"nothing new\". */\n publishSessionActivity({\n domainId: domain.id,\n sessionId: session.id!,\n kind: 'message',\n anchorType: (session as any).anchorType,\n anchorId: (session as any).anchorId,\n preview: result.reply,\n actorId: user?.id\n })\n\n await sessionRepo.update(session.id!, {\n aiClientId: client.id,\n updater: user,\n ...activityPatch(result.reply, new Date(), answerDoors)\n } as any)\n\n /*\n * 상한 밖으로 밀려난 앞부분을 요약으로 접는다 — **답을 돌려준 뒤**에 한다(사용자 대기 시간에\n * 요약 호출을 얹지 않는다). 기다리지 않으므로 실패해도 대화에는 영향이 없다: 요약이 없으면 다음\n * 턴이 「N 개 생략」으로 정직하게 알린다.\n *\n * 이 문에는 이 호출이 없었다. 조각은 다 있었는데(상한 · 생략 알림 · `lastSummary` · 순수 규칙)\n * 부르는 쪽이 보드 제품 하나뿐이어서, 앱의 대화는 며칠이 지나도 앞부분을 요약하지 않고 매 턴\n * 「생략했다」고만 말했다 — 협의가 길어질수록 처음에 합의한 것이 먼저 사라진다.\n */\n void foldSessionHistory({\n sessionId: session.id!,\n domainId: domain.id,\n client,\n maxTurns: LLM_HISTORY_MAX_TURNS,\n locale: context.state?.locale\n })\n }\n\n return {\n reply: result.reply,\n clientId: client.id,\n sessionId: session?.id,\n userMessageId,\n assistantMessageId,\n toolUsages: result.loop.toolUsages?.length ? result.loop.toolUsages : null,\n offeredTools: result.offeredTools,\n proposals: result.loop.proposals?.length ? result.loop.proposals : null,\n groundingWarnings: result.groundingWarnings?.length ? result.groundingWarnings : null,\n historyFolded: historyFolded ?? null\n }\n }\n}\n"]}
|
|
@@ -122,7 +122,7 @@ tslib_1.__decorate([
|
|
|
122
122
|
(0, type_graphql_1.Query)(() => [AssistantModeChoice], {
|
|
123
123
|
description: 'Conversations that stand for this kind of subject, the default first. A screen showing one subject opens the head of this list and offers the rest; a single entry means there is nothing to choose.'
|
|
124
124
|
}),
|
|
125
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query")'),
|
|
125
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
126
126
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('subject')),
|
|
127
127
|
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
128
128
|
tslib_1.__metadata("design:type", Function),
|
|
@@ -135,7 +135,7 @@ tslib_1.__decorate([
|
|
|
135
135
|
})
|
|
136
136
|
/* Reading what a conversation offers is reading, and the same door the session list uses. */
|
|
137
137
|
,
|
|
138
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query")'),
|
|
138
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
139
139
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('kind')),
|
|
140
140
|
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
141
141
|
tslib_1.__metadata("design:type", Function),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-mode-resolver.js","sourceRoot":"","sources":["../../../server/service/assistant-mode/assistant-mode-resolver.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"assistant-mode-resolver.js","sourceRoot":"","sources":["../../../server/service/assistant-mode/assistant-mode-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The screen asks what its conversation offers.\n *\n * The `/` sentences, the `@` catalog and the two notices used to travel from the browser into the\n * request, which meant the screen and the door could describe the same conversation differently.\n * They are declared once, in the mode, as i18n keys — and this query hands them over already\n * written in the request's locale (ADR-0054, and the same rule A11 applied to the stop notice).\n * The persona, the tool categories and the limits stay on the server: nothing here returns them.\n */\nimport { Arg, Ctx, Directive, Field, ObjectType, Query, Resolver } from 'type-graphql'\nimport { GraphQLJSON } from 'graphql-scalars'\nimport '@things-factory/auth-base'\n\nimport { assistantModeView } from './resolve.js'\nimport { assistantModesForSubject } from './registry.js'\nimport { requestTranslator } from '../request-translator.js'\n\n@ObjectType({ description: 'One `/` sentence, in the words of this request.' })\nclass AssistantModeSlashView {\n @Field({ description: 'Short name shown in the list.' })\n name: string\n\n @Field({ description: 'One line about what it does.' })\n description: string\n\n @Field({ description: 'The sentence that lands in the input.' })\n template: string\n}\n\n@ObjectType({ description: 'What a conversation offers on screen.' })\nclass AssistantModeView {\n @Field({ description: 'The mode kind this describes.' })\n kind: string\n\n @Field(() => [AssistantModeSlashView], { description: 'What `/` offers.' })\n slashTemplates: AssistantModeSlashView[]\n\n @Field(() => GraphQLJSON, { description: 'What `@` offers.' })\n catalogEntries: any[]\n\n @Field(() => GraphQLJSON, { description: 'Follow-up chips for a proposal that arrived without its own.' })\n proposalChoices: any[]\n\n @Field({ nullable: true, description: 'What the empty input says — what this conversation is for.' })\n placeholder?: string\n\n @Field(() => GraphQLJSON, { nullable: true, description: 'Title and one line for an empty conversation.' })\n intro?: { title: string; subtitle: string }\n\n @Field(() => GraphQLJSON, { nullable: true, description: 'Example sentences this conversation can actually answer.' })\n examples?: Array<{ label: string; items: string[] }>\n\n @Field({ nullable: true, description: 'The line under an empty conversation.' })\n footerNotice?: string\n\n @Field({ nullable: true, description: 'The line shown once the host reports a proposal staged.' })\n stagedNotice?: string\n}\n\n@ObjectType({ description: 'A conversation a screen may open for what it is showing.' })\nclass AssistantModeChoice {\n @Field({ description: 'The mode kind to open.' })\n kind: string\n\n @Field({ description: 'What to call it where a person chooses — already in the words of this request.' })\n label: string\n\n @Field({ description: 'Whether this is the one to open when the person has not chosen.' })\n isDefault: boolean\n}\n\n@Resolver()\nexport class AssistantModeResolver {\n /*\n * 이 화면이 보여 주는 것으로 열 수 있는 대화들 — ADR-0072 A17 ①.\n *\n * 화면은 자기가 무엇을 보여 주는지(`subject`)만 안다. 그 위에 어느 대화가 서는지는 앱 서버가\n * 선언한다. 화면이 종류 목록을 손으로 들고 있으면, 대화를 하나 더 세운 날 그 화면만 모른 채로 남는다.\n *\n * 하나만 오면 고르개를 내지 않는다 — 고를 것이 없는 고르개는 자리만 차지한다.\n */\n @Query(() => [AssistantModeChoice], {\n description:\n 'Conversations that stand for this kind of subject, the default first. A screen showing one subject opens the head of this list and offers the rest; a single entry means there is nothing to choose.'\n })\n @Directive('@privilege(category: \"ai-assistant\", privilege: \"query\", domainOwnerGranted: true)')\n async assistantModes(@Arg('subject') subject: string, @Ctx() context: any): Promise<AssistantModeChoice[]> {\n const t = requestTranslator(context)\n\n return assistantModesForSubject(subject).map(mode => ({\n kind: mode.kind,\n /*\n * 이름이 없는 대화는 종류를 그대로 쓴다. 부팅 보고가 이미 그것을 경고로 말하므로 여기서 숨기지\n * 않는다 — 화면에 `twin.space` 가 뜨면 그것이 빠진 선언을 가리킨다.\n */\n label: mode.labelKey ? t(mode.labelKey) : mode.kind,\n isDefault: (mode.defaultFor ?? []).includes(subject)\n }))\n }\n @Query(() => AssistantModeView, {\n description: 'What this conversation offers on screen — `/` sentences, the `@` catalog and the notices, translated.'\n })\n /* Reading what a conversation offers is reading, and the same door the session list uses. */\n @Directive('@privilege(category: \"ai-assistant\", privilege: \"query\", domainOwnerGranted: true)')\n async assistantMode(@Arg('kind') kind: string, @Ctx() context: any): Promise<AssistantModeView> {\n return assistantModeView(kind, requestTranslator(context)) as AssistantModeView\n }\n}\n"]}
|
|
@@ -696,7 +696,7 @@ let ChatSessionResolver = class ChatSessionResolver {
|
|
|
696
696
|
exports.ChatSessionResolver = ChatSessionResolver;
|
|
697
697
|
tslib_1.__decorate([
|
|
698
698
|
(0, type_graphql_1.Query)(() => chat_session_js_1.ChatSession, { nullable: true, description: 'Get AI chat session by id.' }),
|
|
699
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query")'),
|
|
699
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
700
700
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('id')),
|
|
701
701
|
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
702
702
|
tslib_1.__metadata("design:type", Function),
|
|
@@ -707,7 +707,7 @@ tslib_1.__decorate([
|
|
|
707
707
|
(0, type_graphql_1.Query)(() => [chat_session_js_1.ChatSession], {
|
|
708
708
|
description: 'List AI chat sessions anchored to a target (oldest first). Anchor decides the nature and the history boundary of the conversation: board = authoring, space = the site and the twins running there. Board anchor also matches legacy rows that only have boardId.'
|
|
709
709
|
}),
|
|
710
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query")'),
|
|
710
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
711
711
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('anchorType')),
|
|
712
712
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('anchorId')),
|
|
713
713
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
@@ -719,7 +719,7 @@ tslib_1.__decorate([
|
|
|
719
719
|
(0, type_graphql_1.Query)(() => [ChatSessionInboxEntry], {
|
|
720
720
|
description: "List sessions anchored to a target with this viewer's unread state, most recent activity first. Supports free-text search over name and last preview, a mine-only filter, hidden inclusion, and paging. Uses the denormalized last-activity timestamp compared against the viewer's read point — no per-session aggregation."
|
|
721
721
|
}),
|
|
722
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query")'),
|
|
722
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
723
723
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('anchorType')),
|
|
724
724
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('anchorId')),
|
|
725
725
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
@@ -736,7 +736,7 @@ tslib_1.__decorate([
|
|
|
736
736
|
(0, type_graphql_1.Query)(() => type_graphql_1.Int, {
|
|
737
737
|
description: 'Total number of sessions matching the same filters as chatSessionInbox (before paging). Request it alongside the list so the UI can tell "end of page" from "end of data".'
|
|
738
738
|
}),
|
|
739
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query")'),
|
|
739
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
740
740
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('anchorType')),
|
|
741
741
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('anchorId')),
|
|
742
742
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
@@ -751,7 +751,7 @@ tslib_1.__decorate([
|
|
|
751
751
|
(0, type_graphql_1.Query)(() => type_graphql_1.Int, {
|
|
752
752
|
description: "Number of sessions on this anchor that the caller has hidden and that have had no activity since. Meant to be requested alongside chatSessionInbox in the same operation so the filter can say how many are folded away."
|
|
753
753
|
}),
|
|
754
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query")'),
|
|
754
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
755
755
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('anchorType')),
|
|
756
756
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('anchorId')),
|
|
757
757
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
@@ -763,7 +763,7 @@ tslib_1.__decorate([
|
|
|
763
763
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
764
764
|
description: 'Delete a chat session — creator only, since a shared deliberation cannot be un-deleted from the UI. Participants who are not the creator can hide it from their own list instead (hideAISession). Soft delete; broadcast as meta activity so every list drops it.'
|
|
765
765
|
}),
|
|
766
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
766
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
767
767
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
768
768
|
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
769
769
|
tslib_1.__metadata("design:type", Function),
|
|
@@ -774,7 +774,7 @@ tslib_1.__decorate([
|
|
|
774
774
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
775
775
|
description: 'Hide (or unhide) a session from the calling user\'s own list. Per-user and reversible — the session and other participants are unaffected. Activity newer than the hide makes it reappear; the inbox filter (showHidden) reveals hidden ones.'
|
|
776
776
|
}),
|
|
777
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
777
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
778
778
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
779
779
|
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
780
780
|
tslib_1.__param(2, (0, type_graphql_1.Arg)('hidden', { nullable: true })),
|
|
@@ -786,7 +786,7 @@ tslib_1.__decorate([
|
|
|
786
786
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
787
787
|
description: "Open this conversation to everyone in the domain, or close it to its participants. Only the owner may change it. Opening exposes what is already written — the screen says so before asking. Door checks on assistant answers are unaffected (ADR-0073)."
|
|
788
788
|
}),
|
|
789
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
789
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
790
790
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
791
791
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('visibility')),
|
|
792
792
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
@@ -798,7 +798,7 @@ tslib_1.__decorate([
|
|
|
798
798
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
799
799
|
description: 'Rename a chat session. Any participant may rename it — the name is the topic label of a shared deliberation and the change is reversible. Broadcast as meta activity so every participant sees it without raising unread badges.'
|
|
800
800
|
}),
|
|
801
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
801
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
802
802
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
803
803
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('name')),
|
|
804
804
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
@@ -810,7 +810,7 @@ tslib_1.__decorate([
|
|
|
810
810
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
811
811
|
description: "Mark a session as read up to now for the current user. Idempotent. Distinct from presence (lastSeenAt) — a window can be open without being read."
|
|
812
812
|
}),
|
|
813
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
813
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
814
814
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
815
815
|
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
816
816
|
tslib_1.__metadata("design:type", Function),
|
|
@@ -821,7 +821,7 @@ tslib_1.__decorate([
|
|
|
821
821
|
(0, type_graphql_1.Query)(() => [auth_base_1.User], {
|
|
822
822
|
description: 'List users in the current domain for the `@` mention popup. Anyone with the chat privilege may search here; this is separate from the admin-only users().'
|
|
823
823
|
}),
|
|
824
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query")'),
|
|
824
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
825
825
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('query', { nullable: true, description: 'Substring to match against name/email (case-insensitive). Empty → all.' })),
|
|
826
826
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('limit', () => type_graphql_1.Int, { nullable: true, defaultValue: 50 })),
|
|
827
827
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
@@ -833,7 +833,7 @@ tslib_1.__decorate([
|
|
|
833
833
|
(0, type_graphql_1.Query)(() => [chat_session_participant_js_1.ChatSessionParticipant], {
|
|
834
834
|
description: 'List participants of a ChatSession (members / owner).'
|
|
835
835
|
}),
|
|
836
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query")'),
|
|
836
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
837
837
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
838
838
|
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
839
839
|
tslib_1.__metadata("design:type", Function),
|
|
@@ -844,7 +844,7 @@ tslib_1.__decorate([
|
|
|
844
844
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
845
845
|
description: 'Presence heartbeat — bump current user\'s lastSeenAt in the session (online indicator).'
|
|
846
846
|
}),
|
|
847
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
847
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
848
848
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
849
849
|
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
850
850
|
tslib_1.__metadata("design:type", Function),
|
|
@@ -853,7 +853,7 @@ tslib_1.__decorate([
|
|
|
853
853
|
], ChatSessionResolver.prototype, "touchPresence", null);
|
|
854
854
|
tslib_1.__decorate([
|
|
855
855
|
(0, type_graphql_1.Query)(() => [chat_message_js_1.ChatMessage], { description: 'List chat messages of a session, oldest first.' }),
|
|
856
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query")'),
|
|
856
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "query", domainOwnerGranted: true)'),
|
|
857
857
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
858
858
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('limit', () => type_graphql_1.Int, { nullable: true, defaultValue: 100 })),
|
|
859
859
|
tslib_1.__param(2, (0, type_graphql_1.Arg)('offset', () => type_graphql_1.Int, { nullable: true, defaultValue: 0 })),
|
|
@@ -867,7 +867,7 @@ tslib_1.__decorate([
|
|
|
867
867
|
(0, type_graphql_1.Mutation)(() => chat_session_js_1.ChatSession, {
|
|
868
868
|
description: 'Start (or get existing) AI chat session anchored to a target. Idempotent — returns first existing or creates one. Use anchorType board for board authoring, space for a site (its co-located twins).'
|
|
869
869
|
}),
|
|
870
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
870
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
871
871
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('anchorType')),
|
|
872
872
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('anchorId')),
|
|
873
873
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
@@ -880,7 +880,7 @@ tslib_1.__decorate([
|
|
|
880
880
|
(0, type_graphql_1.Mutation)(() => chat_session_js_1.ChatSession, {
|
|
881
881
|
description: 'Always create a new AI chat session anchored to a target (no idempotent reuse). For multi-session UX — opening another tab.'
|
|
882
882
|
}),
|
|
883
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
883
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
884
884
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('anchorType')),
|
|
885
885
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('anchorId')),
|
|
886
886
|
tslib_1.__param(2, (0, type_graphql_1.Arg)('name', { nullable: true, description: 'Optional name. Defaults to a generated one, numbered within the anchor.' })),
|
|
@@ -894,7 +894,7 @@ tslib_1.__decorate([
|
|
|
894
894
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
895
895
|
description: 'Rename a ChatSession (tab label).'
|
|
896
896
|
}),
|
|
897
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
897
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
898
898
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
899
899
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('name')),
|
|
900
900
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
@@ -907,7 +907,7 @@ tslib_1.__decorate([
|
|
|
907
907
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
908
908
|
description: "What the host did with a proposal the assistant made. The host calls this once per card it was asked to run; the server records it as a system row, so the panel shows it, a refreshed panel still shows it, and the model reads on its next turn that its proposal was refused instead of assuming it landed (ADR-0070)."
|
|
909
909
|
}),
|
|
910
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
910
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
911
911
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
912
912
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('proposalKey')),
|
|
913
913
|
tslib_1.__param(2, (0, type_graphql_1.Arg)('outcome', () => graphql_scalars_1.GraphQLJSON)),
|
|
@@ -922,7 +922,7 @@ tslib_1.__decorate([
|
|
|
922
922
|
nullable: true,
|
|
923
923
|
description: "Say something to the people in this session. The turn is stored as the speaker's, addressed to the room, and the assistant is not called — it reads the line as context the next time someone asks it something. Returns the stored message id, or null when there was nothing to say (ADR-0070)."
|
|
924
924
|
}),
|
|
925
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
925
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
926
926
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
927
927
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('content')),
|
|
928
928
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
@@ -935,7 +935,7 @@ tslib_1.__decorate([
|
|
|
935
935
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
936
936
|
description: 'Append a system note to a session — context that did not come from a participant typing (e.g. a conversation carried over from another surface, an external state change). The AI sees it as part of the shared history.'
|
|
937
937
|
}),
|
|
938
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
938
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
939
939
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
940
940
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('content')),
|
|
941
941
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|
|
@@ -948,7 +948,7 @@ tslib_1.__decorate([
|
|
|
948
948
|
(0, type_graphql_1.Mutation)(() => Boolean, {
|
|
949
949
|
description: "Carry a conversation from another surface into this session as its own turns, preserving order. A person's turns are stored as theirs, addressed to the room. An assistant turn is stored as the assistant's only when it names its original (sourceSessionId, sourceMessageId) and the server can read it; otherwise it is kept as a quotation on a system row — roles are the server's to write (ADR-0070)."
|
|
950
950
|
}),
|
|
951
|
-
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation")'),
|
|
951
|
+
(0, type_graphql_1.Directive)('@privilege(category: "ai-assistant", privilege: "mutation", domainOwnerGranted: true)'),
|
|
952
952
|
tslib_1.__param(0, (0, type_graphql_1.Arg)('sessionId')),
|
|
953
953
|
tslib_1.__param(1, (0, type_graphql_1.Arg)('turns', () => graphql_scalars_1.GraphQLJSON)),
|
|
954
954
|
tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
|