@things-factory/ai-assistant 10.1.207 → 10.1.210
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 +238 -9
- package/client/components/assistant-dock.ts +9 -4
- package/client/components/assistant-invite.ts +138 -0
- package/client/components/assistant-session-manager.ts +53 -12
- package/client/components/proposal-request.test.ts +10 -0
- package/client/components/roster-identity.test.ts +2 -12
- package/client/components/roster-identity.ts +0 -6
- package/client/setup-assistant-dock.ts +1 -0
- package/client/utils/assistant-session-actions.ts +3 -1
- package/client/utils/assistant-session-admin.ts +9 -2
- package/client/utils/assistant-session-controller.ts +1 -1
- package/client/utils/assistant-typing.test.ts +33 -0
- package/client/utils/assistant-typing.ts +53 -0
- package/dist-client/components/assistant-chat.d.ts +28 -0
- package/dist-client/components/assistant-chat.js +259 -8
- package/dist-client/components/assistant-chat.js.map +1 -1
- package/dist-client/components/assistant-dock.js +12 -4
- package/dist-client/components/assistant-dock.js.map +1 -1
- package/dist-client/components/assistant-invite.d.ts +39 -0
- package/dist-client/components/assistant-invite.js +150 -0
- package/dist-client/components/assistant-invite.js.map +1 -0
- package/dist-client/components/assistant-session-manager.d.ts +5 -1
- package/dist-client/components/assistant-session-manager.js +40 -10
- package/dist-client/components/assistant-session-manager.js.map +1 -1
- package/dist-client/components/proposal-request.test.js +8 -0
- package/dist-client/components/proposal-request.test.js.map +1 -1
- package/dist-client/components/roster-identity.d.ts +0 -2
- package/dist-client/components/roster-identity.js +0 -7
- package/dist-client/components/roster-identity.js.map +1 -1
- package/dist-client/components/roster-identity.test.js +2 -13
- package/dist-client/components/roster-identity.test.js.map +1 -1
- package/dist-client/setup-assistant-dock.js +1 -0
- package/dist-client/setup-assistant-dock.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/assistant-session-actions.d.ts +1 -0
- package/dist-client/utils/assistant-session-actions.js +3 -1
- package/dist-client/utils/assistant-session-actions.js.map +1 -1
- package/dist-client/utils/assistant-session-admin.d.ts +5 -2
- package/dist-client/utils/assistant-session-admin.js +3 -0
- package/dist-client/utils/assistant-session-admin.js.map +1 -1
- package/dist-client/utils/assistant-session-controller.d.ts +1 -1
- package/dist-client/utils/assistant-session-controller.js.map +1 -1
- package/dist-client/utils/assistant-typing.d.ts +20 -0
- package/dist-client/utils/assistant-typing.js +48 -0
- package/dist-client/utils/assistant-typing.js.map +1 -0
- package/dist-client/utils/assistant-typing.test.d.ts +1 -0
- package/dist-client/utils/assistant-typing.test.js +30 -0
- package/dist-client/utils/assistant-typing.test.js.map +1 -0
- package/dist-server/service/assistant-chat-resolver.js +5 -12
- package/dist-server/service/assistant-chat-resolver.js.map +1 -1
- package/dist-server/service/assistant-mode/index.d.ts +1 -1
- package/dist-server/service/assistant-mode/index.js +4 -1
- package/dist-server/service/assistant-mode/index.js.map +1 -1
- package/dist-server/service/assistant-mode/mode-categories.js +9 -2
- package/dist-server/service/assistant-mode/mode-categories.js.map +1 -1
- package/dist-server/service/assistant-mode/screen-rules.d.ts +19 -0
- package/dist-server/service/assistant-mode/screen-rules.js +40 -6
- package/dist-server/service/assistant-mode/screen-rules.js.map +1 -1
- package/dist-server/service/chat-message/chat-message-room.js +3 -1
- package/dist-server/service/chat-message/chat-message-room.js.map +1 -1
- package/dist-server/service/chat-message/chat-typing-subscription.d.ts +10 -0
- package/dist-server/service/chat-message/chat-typing-subscription.js +68 -0
- package/dist-server/service/chat-message/chat-typing-subscription.js.map +1 -0
- package/dist-server/service/chat-message/chat-typing.d.ts +28 -0
- package/dist-server/service/chat-message/chat-typing.js +40 -0
- package/dist-server/service/chat-message/chat-typing.js.map +1 -0
- package/dist-server/service/chat-session/attachment-tool-result.d.ts +1 -0
- package/dist-server/service/chat-session/attachment-tool-result.js +6 -0
- package/dist-server/service/chat-session/attachment-tool-result.js.map +1 -1
- package/dist-server/service/chat-session/attachment-tools.js +2 -2
- package/dist-server/service/chat-session/attachment-tools.js.map +1 -1
- package/dist-server/service/chat-session/chat-session.d.ts +2 -1
- package/dist-server/service/chat-session/chat-session.js +1 -1
- package/dist-server/service/chat-session/chat-session.js.map +1 -1
- package/dist-server/service/chat-session/session-activity-broadcast.d.ts +12 -0
- package/dist-server/service/chat-session/session-activity-broadcast.js +15 -0
- package/dist-server/service/chat-session/session-activity-broadcast.js.map +1 -1
- package/dist-server/service/chat-session/session-activity-publish.d.ts +2 -0
- package/dist-server/service/chat-session/session-activity-publish.js +5 -2
- package/dist-server/service/chat-session/session-activity-publish.js.map +1 -1
- package/dist-server/service/chat-session/session-visibility.d.ts +17 -2
- package/dist-server/service/chat-session/session-visibility.js +28 -3
- package/dist-server/service/chat-session/session-visibility.js.map +1 -1
- package/dist-server/service/chat-session/session-write-gate.d.ts +1 -1
- package/dist-server/service/chat-session/session-write-gate.js +1 -1
- package/dist-server/service/chat-session/session-write-gate.js.map +1 -1
- package/dist-server/service/chat-session-participant/index.d.ts +1 -0
- package/dist-server/service/chat-session-participant/index.js +4 -1
- package/dist-server/service/chat-session-participant/index.js.map +1 -1
- package/dist-server/service/chat-session-participant/invite-notification.d.ts +16 -1
- package/dist-server/service/chat-session-participant/invite-notification.js +34 -5
- package/dist-server/service/chat-session-participant/invite-notification.js.map +1 -1
- package/dist-server/service/chat-session-participant/mention-invite.d.ts +30 -4
- package/dist-server/service/chat-session-participant/mention-invite.js +78 -31
- package/dist-server/service/chat-session-participant/mention-invite.js.map +1 -1
- package/dist-server/service/chat-session-participant/read-roster.js +5 -2
- package/dist-server/service/chat-session-participant/read-roster.js.map +1 -1
- package/dist-server/service/chat-session-resolver.d.ts +27 -0
- package/dist-server/service/chat-session-resolver.js +226 -25
- package/dist-server/service/chat-session-resolver.js.map +1 -1
- package/dist-server/service/data-entry/entry-target.d.ts +19 -0
- package/dist-server/service/data-entry/entry-target.js +20 -5
- package/dist-server/service/data-entry/entry-target.js.map +1 -1
- package/dist-server/service/data-entry/entry-tools.js +2 -2
- package/dist-server/service/data-entry/entry-tools.js.map +1 -1
- package/dist-server/service/data-entry/entry-update.d.ts +2 -1
- package/dist-server/service/data-entry/entry-update.js +5 -3
- package/dist-server/service/data-entry/entry-update.js.map +1 -1
- package/dist-server/service/data-read/list-read.d.ts +10 -0
- package/dist-server/service/data-read/list-read.js +12 -0
- package/dist-server/service/data-read/list-read.js.map +1 -1
- package/dist-server/service/data-read/read-target.d.ts +7 -0
- package/dist-server/service/data-read/read-target.js +2 -2
- package/dist-server/service/data-read/read-target.js.map +1 -1
- package/dist-server/service/data-read/read-tools.js +1 -1
- package/dist-server/service/data-read/read-tools.js.map +1 -1
- package/dist-server/service/index.d.ts +4 -2
- package/dist-server/service/index.js +9 -1
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/testing/conversation-script.d.ts +1 -1
- package/dist-server/service/testing/conversation-script.js.map +1 -1
- package/dist-server/service/testing/mode-conformance.js +4 -0
- package/dist-server/service/testing/mode-conformance.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/server/explicit-graphql-types.test.ts +1 -1
- package/server/service/assistant-chat-resolver.ts +7 -12
- package/server/service/assistant-mode/index.ts +1 -1
- package/server/service/assistant-mode/mode-categories.ts +11 -3
- package/server/service/assistant-mode/mode-front.test.ts +53 -1
- package/server/service/assistant-mode/resolve.test.ts +2 -1
- package/server/service/assistant-mode/screen-rules.test.ts +41 -3
- package/server/service/assistant-mode/screen-rules.ts +44 -5
- package/server/service/chat-message/chat-message-room.test.ts +7 -2
- package/server/service/chat-message/chat-message-room.ts +3 -1
- package/server/service/chat-message/chat-typing-subscription.ts +52 -0
- package/server/service/chat-message/chat-typing.test.ts +45 -0
- package/server/service/chat-message/chat-typing.ts +48 -0
- package/server/service/chat-message/fold-history-window.test.ts +1 -1
- package/server/service/chat-session/attachment-tool-result.test.ts +9 -1
- package/server/service/chat-session/attachment-tool-result.ts +7 -0
- package/server/service/chat-session/attachment-tools.ts +3 -3
- package/server/service/chat-session/chat-session.ts +3 -2
- package/server/service/chat-session/group-room.test.ts +233 -0
- package/server/service/chat-session/session-activity-broadcast.ts +20 -0
- package/server/service/chat-session/session-activity-publish.ts +6 -1
- package/server/service/chat-session/session-activity-receivers.test.ts +26 -0
- package/server/service/chat-session/session-visibility.test.ts +32 -1
- package/server/service/chat-session/session-visibility.ts +33 -4
- package/server/service/chat-session/session-write-gate.test.ts +7 -1
- package/server/service/chat-session/session-write-gate.ts +2 -2
- package/server/service/chat-session-participant/index.ts +2 -0
- package/server/service/chat-session-participant/invite-notification.test.ts +36 -1
- package/server/service/chat-session-participant/invite-notification.ts +43 -5
- package/server/service/chat-session-participant/mention-invite.test.ts +49 -1
- package/server/service/chat-session-participant/mention-invite.ts +82 -28
- package/server/service/chat-session-participant/read-roster.test.ts +13 -4
- package/server/service/chat-session-participant/read-roster.ts +6 -3
- package/server/service/chat-session-resolver.ts +204 -27
- package/server/service/data-entry/data-entry-update.test.ts +19 -0
- package/server/service/data-entry/data-entry.test.ts +65 -1
- package/server/service/data-entry/entry-target.ts +36 -5
- package/server/service/data-entry/entry-tools.ts +2 -2
- package/server/service/data-entry/entry-update.ts +6 -3
- package/server/service/data-read/list-missing.test.ts +49 -0
- package/server/service/data-read/list-read.ts +18 -0
- package/server/service/data-read/read-target.ts +10 -3
- package/server/service/data-read/read-tools.ts +1 -1
- package/server/service/data-read/record-keys.test.ts +36 -0
- package/server/service/index.ts +6 -1
- package/server/service/testing/conversation-script.ts +1 -1
- package/server/service/testing/mode-conformance.ts +2 -0
- package/test/assistant-dock.test.ts +5 -1
- package/test/assistant-session-manager.test.ts +4 -2
- package/test/open-doors.test.ts +10 -5
- package/translations/en.json +29 -4
- package/translations/es.json +29 -4
- package/translations/ja.json +29 -4
- package/translations/ko.json +30 -5
- package/translations/ms.json +29 -4
- package/translations/zh.json +29 -4
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* hide/unhide anyone, for their own list only (reversible)
|
|
7
7
|
* delete the creator (`mine`) (not reversible from a screen)
|
|
8
8
|
* visibility the owner (`owner`) (opening shows what is already written)
|
|
9
|
+
* leave a participant who is not the owner (the owner deletes or makes it private instead)
|
|
9
10
|
*/
|
|
10
11
|
export function sessionActions(entry) {
|
|
11
12
|
return {
|
|
@@ -13,7 +14,8 @@ export function sessionActions(entry) {
|
|
|
13
14
|
hide: !entry.hidden,
|
|
14
15
|
unhide: !!entry.hidden,
|
|
15
16
|
remove: !!entry.mine,
|
|
16
|
-
setVisibility: !!entry.owner
|
|
17
|
+
setVisibility: !!entry.owner,
|
|
18
|
+
leave: !!entry.participant && !entry.owner
|
|
17
19
|
};
|
|
18
20
|
}
|
|
19
21
|
//# sourceMappingURL=assistant-session-actions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-session-actions.js","sourceRoot":"","sources":["../../client/utils/assistant-session-actions.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"assistant-session-actions.js","sourceRoot":"","sources":["../../client/utils/assistant-session-actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,KAAmF;IAChH,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,WAAW,KAAK,KAAK;QACnC,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM;QACnB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM;QACtB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI;QACpB,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK;QAC5B,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,KAAK;KAC3C,CAAA;AACH,CAAC","sourcesContent":["/*\n * Which actions a row of the conversation list offers this viewer - pure, so the rule is tested alone.\n *\n * The same hints the server gives on each inbox entry; the doors decide again:\n * rename any participant (the name is the topic label; reversible)\n * hide/unhide anyone, for their own list only (reversible)\n * delete the creator (`mine`) (not reversible from a screen)\n * visibility the owner (`owner`) (opening shows what is already written)\n * leave a participant who is not the owner (the owner deletes or makes it private instead)\n */\nexport function sessionActions(entry: { participant?: boolean; hidden?: boolean; mine?: boolean; owner?: boolean }) {\n return {\n rename: entry.participant !== false,\n hide: !entry.hidden,\n unhide: !!entry.hidden,\n remove: !!entry.mine,\n setVisibility: !!entry.owner,\n leave: !!entry.participant && !entry.owner\n }\n}\n"]}
|
|
@@ -12,8 +12,10 @@ export { sessionActions } from './assistant-session-actions.js';
|
|
|
12
12
|
* hide/unhide anyone, for their own list only (reversible)
|
|
13
13
|
* delete the creator (`mine`) (not reversible from a screen)
|
|
14
14
|
* visibility the owner (`owner`) (opening shows what is already written)
|
|
15
|
+
* leave a member, never the owner (in a group, leaving ends reading here)
|
|
15
16
|
*/
|
|
16
|
-
|
|
17
|
+
/** Who reads it: everyone in the domain, the people the owner brought in, or the owner alone (ADR-0073 decision 3). */
|
|
18
|
+
export type SessionVisibility = 'open' | 'group' | 'private';
|
|
17
19
|
/** One row of `chatSessionInbox`, as the list draws it. */
|
|
18
20
|
export interface AssistantSessionEntry {
|
|
19
21
|
id: string;
|
|
@@ -39,7 +41,7 @@ export interface AssistantSessionFilters {
|
|
|
39
41
|
/** A kind (mode) to list; empty lists every kind on the anchor. */
|
|
40
42
|
kind?: string;
|
|
41
43
|
whose?: 'all' | 'mine' | 'participant';
|
|
42
|
-
visibility?: 'all' |
|
|
44
|
+
visibility?: 'all' | SessionVisibility;
|
|
43
45
|
includeHidden?: boolean;
|
|
44
46
|
unreadOnly?: boolean;
|
|
45
47
|
}
|
|
@@ -65,6 +67,7 @@ export interface AssistantSessionAdmin {
|
|
|
65
67
|
hide(sessionId: string, hidden: boolean): Promise<boolean>;
|
|
66
68
|
remove(sessionId: string): Promise<boolean>;
|
|
67
69
|
setVisibility(sessionId: string, visibility: SessionVisibility): Promise<boolean>;
|
|
70
|
+
leave(sessionId: string): Promise<boolean>;
|
|
68
71
|
}
|
|
69
72
|
/** The page size the server allows at most (`chatSessionInbox` clamps to 200). */
|
|
70
73
|
export declare const SESSION_PAGE_MAX = 200;
|
|
@@ -76,6 +76,9 @@ export const assistantSessionAdmin = {
|
|
|
76
76
|
},
|
|
77
77
|
setVisibility(sessionId, visibility) {
|
|
78
78
|
return mutate(gql `mutation ($id: String!, $v: String!) { setSessionVisibility(sessionId: $id, visibility: $v) }`, { id: sessionId, v: visibility }, 'setSessionVisibility');
|
|
79
|
+
},
|
|
80
|
+
leave(sessionId) {
|
|
81
|
+
return mutate(gql `mutation ($id: String!) { leaveAISession(sessionId: $id) }`, { id: sessionId }, 'leaveAISession');
|
|
79
82
|
}
|
|
80
83
|
};
|
|
81
84
|
//# sourceMappingURL=assistant-session-admin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-session-admin.js","sourceRoot":"","sources":["../../client/utils/assistant-session-admin.ts"],"names":[],"mappings":"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","sourcesContent":["import { client } from '@operato/graphql'\nimport { i18next } from '@operato/i18n'\nimport gql from 'graphql-tag'\n\nexport { sessionActions } from './assistant-session-actions.js'\n\n/**\n * Managing the conversations on one anchor - list, rename, hide, delete, and who may read them.\n *\n * These were written once, inside operato-twin's dock (things-factory `b3d1081f4c`, `b44c986c07`,\n * 2026-07-30), and every other dock (plant, figure, warehouse, ecs) had only the tabs. They are the\n * framework's doors (`chatSessionInbox` · `renameAISession` · `hideAISession` · `deleteAISession` ·\n * `setSessionVisibility`), so the screen for them belongs here too - one list every dock opens.\n *\n * Who may do what is the server's to decide; the entry carries hints only:\n * rename any participant (the name is the topic label; reversible)\n * hide/unhide anyone, for their own list only (reversible)\n * delete the creator (`mine`) (not reversible from a screen)\n * visibility the owner (`owner`) (opening shows what is already written)\n */\n\nexport type SessionVisibility = 'open' | 'private'\n\n/** One row of `chatSessionInbox`, as the list draws it. */\nexport interface AssistantSessionEntry {\n id: string\n name?: string\n kind?: string\n lastMessageAt?: string\n lastMessagePreview?: string\n previewRedacted?: boolean\n unread?: boolean\n participant?: boolean\n /** I created it - only then may I delete it. */\n mine?: boolean\n /** I own it - only then may I change who reads it. */\n owner?: boolean\n /** Hidden from my own list (returned only with the hidden filter on). */\n hidden?: boolean\n visibility?: SessionVisibility | string\n}\n\n/** Legacy single filter - kept for callers written before the filter set. */\nexport type AssistantSessionFilter = 'all' | 'mine' | 'hidden'\n\n/** What the manager narrows by (design/ai/shared-dock.md §2-6). Defaults: this kind, everyone's, any visibility, hidden out. */\nexport interface AssistantSessionFilters {\n /** A kind (mode) to list; empty lists every kind on the anchor. */\n kind?: string\n whose?: 'all' | 'mine' | 'participant'\n visibility?: 'all' | 'open' | 'private'\n includeHidden?: boolean\n unreadOnly?: boolean\n}\n\nexport interface AssistantSessionQuery {\n anchorType: string\n anchorId: string\n search?: string\n filter?: AssistantSessionFilter\n filters?: AssistantSessionFilters\n limit?: number\n offset?: number\n}\n\nexport interface AssistantSessionPage {\n entries: AssistantSessionEntry[]\n /** How many pass the filter - so \"load more\" knows whether the end of the list is the end of the data. */\n total: number\n /** How many I hid - so a hidden conversation is never forgotten. */\n hiddenCount: number\n}\n\nexport interface AssistantSessionAdmin {\n inbox(query: AssistantSessionQuery): Promise<AssistantSessionPage>\n rename(sessionId: string, name: string): Promise<boolean>\n hide(sessionId: string, hidden: boolean): Promise<boolean>\n remove(sessionId: string): Promise<boolean>\n setVisibility(sessionId: string, visibility: SessionVisibility): Promise<boolean>\n}\n\n/** The page size the server allows at most (`chatSessionInbox` clamps to 200). */\nexport const SESSION_PAGE_MAX = 200\n\nasync function mutate(mutation: any, variables: Record<string, unknown>, field: string): Promise<boolean> {\n const result = await client.mutate({ mutation, variables })\n if (result.errors?.length) throw new Error(result.errors[0].message)\n return result.data?.[field] === true\n}\n\n/** The framework's session doors, as they are. */\nexport const assistantSessionAdmin: AssistantSessionAdmin = {\n async inbox({ anchorType, anchorId, search, filter = 'all', filters = {}, limit = 50, offset = 0 }) {\n const hidden = filters.includeHidden ?? filter === 'hidden'\n const mine = filters.whose ? filters.whose === 'mine' : filter === 'mine'\n const result = await client.query({\n query: gql`\n query ($t: String!, $i: String!, $h: Boolean, $q: String, $m: Boolean, $l: Int, $o: Int, $k: String, $p: Boolean, $v: String, $u: Boolean) {\n hiddenSessionCount(anchorType: $t, anchorId: $i)\n chatSessionInboxTotal(anchorType: $t, anchorId: $i, showHidden: $h, search: $q, mineOnly: $m, kind: $k, participantOnly: $p, visibility: $v, unreadOnly: $u)\n chatSessionInbox(anchorType: $t, anchorId: $i, showHidden: $h, search: $q, mineOnly: $m, kind: $k, participantOnly: $p, visibility: $v, unreadOnly: $u, limit: $l, offset: $o) {\n id\n name\n kind\n lastMessageAt\n lastMessagePreview\n previewRedacted\n unread\n participant\n mine\n owner\n hidden\n visibility\n }\n }\n `,\n variables: {\n t: anchorType,\n i: anchorId,\n /* Hidden ones only when asked - elsewhere they stay out of the list. */\n h: hidden,\n q: search?.trim() || null,\n m: mine,\n k: filters.kind || null,\n p: filters.whose === 'participant' || null,\n v: filters.visibility && filters.visibility !== 'all' ? filters.visibility : null,\n u: filters.unreadOnly || null,\n l: Math.min(SESSION_PAGE_MAX, Math.max(1, limit)),\n o: Math.max(0, offset)\n },\n fetchPolicy: 'no-cache'\n })\n\n if (result.errors?.length) throw new Error(result.errors[0].message)\n const entries = result.data?.chatSessionInbox\n if (!Array.isArray(entries)) {\n throw new Error(i18next.t('ai-assistant.text.sessions-unreadable', { defaultValue: 'could not load the conversation list.' }))\n }\n\n return {\n /* With the hidden filter the server returns hidden ones alongside the rest, marked - the total counts both. */\n entries,\n total: result.data?.chatSessionInboxTotal ?? entries.length,\n hiddenCount: result.data?.hiddenSessionCount ?? 0\n }\n },\n\n rename(sessionId, name) {\n return mutate(gql`mutation ($id: String!, $n: String!) { renameAISession(sessionId: $id, name: $n) }`, { id: sessionId, n: name }, 'renameAISession')\n },\n\n hide(sessionId, hidden) {\n return mutate(gql`mutation ($id: String!, $h: Boolean) { hideAISession(sessionId: $id, hidden: $h) }`, { id: sessionId, h: hidden }, 'hideAISession')\n },\n\n remove(sessionId) {\n return mutate(gql`mutation ($id: String!) { deleteAISession(sessionId: $id) }`, { id: sessionId }, 'deleteAISession')\n },\n\n setVisibility(sessionId, visibility) {\n return mutate(\n gql`mutation ($id: String!, $v: String!) { setSessionVisibility(sessionId: $id, visibility: $v) }`,\n { id: sessionId, v: visibility },\n 'setSessionVisibility'\n )\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assistant-session-admin.js","sourceRoot":"","sources":["../../client/utils/assistant-session-admin.ts"],"names":[],"mappings":"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","sourcesContent":["import { client } from '@operato/graphql'\nimport { i18next } from '@operato/i18n'\nimport gql from 'graphql-tag'\n\nexport { sessionActions } from './assistant-session-actions.js'\n\n/**\n * Managing the conversations on one anchor - list, rename, hide, delete, and who may read them.\n *\n * These were written once, inside operato-twin's dock (things-factory `b3d1081f4c`, `b44c986c07`,\n * 2026-07-30), and every other dock (plant, figure, warehouse, ecs) had only the tabs. They are the\n * framework's doors (`chatSessionInbox` · `renameAISession` · `hideAISession` · `deleteAISession` ·\n * `setSessionVisibility`), so the screen for them belongs here too - one list every dock opens.\n *\n * Who may do what is the server's to decide; the entry carries hints only:\n * rename any participant (the name is the topic label; reversible)\n * hide/unhide anyone, for their own list only (reversible)\n * delete the creator (`mine`) (not reversible from a screen)\n * visibility the owner (`owner`) (opening shows what is already written)\n * leave a member, never the owner (in a group, leaving ends reading here)\n */\n\n/** Who reads it: everyone in the domain, the people the owner brought in, or the owner alone (ADR-0073 decision 3). */\nexport type SessionVisibility = 'open' | 'group' | 'private'\n\n/** One row of `chatSessionInbox`, as the list draws it. */\nexport interface AssistantSessionEntry {\n id: string\n name?: string\n kind?: string\n lastMessageAt?: string\n lastMessagePreview?: string\n previewRedacted?: boolean\n unread?: boolean\n participant?: boolean\n /** I created it - only then may I delete it. */\n mine?: boolean\n /** I own it - only then may I change who reads it. */\n owner?: boolean\n /** Hidden from my own list (returned only with the hidden filter on). */\n hidden?: boolean\n visibility?: SessionVisibility | string\n}\n\n/** Legacy single filter - kept for callers written before the filter set. */\nexport type AssistantSessionFilter = 'all' | 'mine' | 'hidden'\n\n/** What the manager narrows by (design/ai/shared-dock.md §2-6). Defaults: this kind, everyone's, any visibility, hidden out. */\nexport interface AssistantSessionFilters {\n /** A kind (mode) to list; empty lists every kind on the anchor. */\n kind?: string\n whose?: 'all' | 'mine' | 'participant'\n visibility?: 'all' | SessionVisibility\n includeHidden?: boolean\n unreadOnly?: boolean\n}\n\nexport interface AssistantSessionQuery {\n anchorType: string\n anchorId: string\n search?: string\n filter?: AssistantSessionFilter\n filters?: AssistantSessionFilters\n limit?: number\n offset?: number\n}\n\nexport interface AssistantSessionPage {\n entries: AssistantSessionEntry[]\n /** How many pass the filter - so \"load more\" knows whether the end of the list is the end of the data. */\n total: number\n /** How many I hid - so a hidden conversation is never forgotten. */\n hiddenCount: number\n}\n\nexport interface AssistantSessionAdmin {\n inbox(query: AssistantSessionQuery): Promise<AssistantSessionPage>\n rename(sessionId: string, name: string): Promise<boolean>\n hide(sessionId: string, hidden: boolean): Promise<boolean>\n remove(sessionId: string): Promise<boolean>\n setVisibility(sessionId: string, visibility: SessionVisibility): Promise<boolean>\n leave(sessionId: string): Promise<boolean>\n}\n\n/** The page size the server allows at most (`chatSessionInbox` clamps to 200). */\nexport const SESSION_PAGE_MAX = 200\n\nasync function mutate(mutation: any, variables: Record<string, unknown>, field: string): Promise<boolean> {\n const result = await client.mutate({ mutation, variables })\n if (result.errors?.length) throw new Error(result.errors[0].message)\n return result.data?.[field] === true\n}\n\n/** The framework's session doors, as they are. */\nexport const assistantSessionAdmin: AssistantSessionAdmin = {\n async inbox({ anchorType, anchorId, search, filter = 'all', filters = {}, limit = 50, offset = 0 }) {\n const hidden = filters.includeHidden ?? filter === 'hidden'\n const mine = filters.whose ? filters.whose === 'mine' : filter === 'mine'\n const result = await client.query({\n query: gql`\n query ($t: String!, $i: String!, $h: Boolean, $q: String, $m: Boolean, $l: Int, $o: Int, $k: String, $p: Boolean, $v: String, $u: Boolean) {\n hiddenSessionCount(anchorType: $t, anchorId: $i)\n chatSessionInboxTotal(anchorType: $t, anchorId: $i, showHidden: $h, search: $q, mineOnly: $m, kind: $k, participantOnly: $p, visibility: $v, unreadOnly: $u)\n chatSessionInbox(anchorType: $t, anchorId: $i, showHidden: $h, search: $q, mineOnly: $m, kind: $k, participantOnly: $p, visibility: $v, unreadOnly: $u, limit: $l, offset: $o) {\n id\n name\n kind\n lastMessageAt\n lastMessagePreview\n previewRedacted\n unread\n participant\n mine\n owner\n hidden\n visibility\n }\n }\n `,\n variables: {\n t: anchorType,\n i: anchorId,\n /* Hidden ones only when asked - elsewhere they stay out of the list. */\n h: hidden,\n q: search?.trim() || null,\n m: mine,\n k: filters.kind || null,\n p: filters.whose === 'participant' || null,\n v: filters.visibility && filters.visibility !== 'all' ? filters.visibility : null,\n u: filters.unreadOnly || null,\n l: Math.min(SESSION_PAGE_MAX, Math.max(1, limit)),\n o: Math.max(0, offset)\n },\n fetchPolicy: 'no-cache'\n })\n\n if (result.errors?.length) throw new Error(result.errors[0].message)\n const entries = result.data?.chatSessionInbox\n if (!Array.isArray(entries)) {\n throw new Error(i18next.t('ai-assistant.text.sessions-unreadable', { defaultValue: 'could not load the conversation list.' }))\n }\n\n return {\n /* With the hidden filter the server returns hidden ones alongside the rest, marked - the total counts both. */\n entries,\n total: result.data?.chatSessionInboxTotal ?? entries.length,\n hiddenCount: result.data?.hiddenSessionCount ?? 0\n }\n },\n\n rename(sessionId, name) {\n return mutate(gql`mutation ($id: String!, $n: String!) { renameAISession(sessionId: $id, name: $n) }`, { id: sessionId, n: name }, 'renameAISession')\n },\n\n hide(sessionId, hidden) {\n return mutate(gql`mutation ($id: String!, $h: Boolean) { hideAISession(sessionId: $id, hidden: $h) }`, { id: sessionId, h: hidden }, 'hideAISession')\n },\n\n remove(sessionId) {\n return mutate(gql`mutation ($id: String!) { deleteAISession(sessionId: $id) }`, { id: sessionId }, 'deleteAISession')\n },\n\n setVisibility(sessionId, visibility) {\n return mutate(\n gql`mutation ($id: String!, $v: String!) { setSessionVisibility(sessionId: $id, visibility: $v) }`,\n { id: sessionId, v: visibility },\n 'setSessionVisibility'\n )\n },\n\n leave(sessionId) {\n return mutate(gql`mutation ($id: String!) { leaveAISession(sessionId: $id) }`, { id: sessionId }, 'leaveAISession')\n }\n}\n"]}
|
|
@@ -11,7 +11,7 @@ export interface AssistantSession {
|
|
|
11
11
|
* 칸이 서기 전에 난 세션에는 값이 없다. 그때는 지금 대화로 두고, 서버가 다음 턴에 찍는다.
|
|
12
12
|
*/
|
|
13
13
|
kind?: string;
|
|
14
|
-
/** Who reads it: 'open' (everyone in the domain) or 'private' (
|
|
14
|
+
/** Who reads it: 'open' (everyone in the domain), 'group' (the people the owner brought in) or 'private' (the owner alone). The dock draws an open one apart. */
|
|
15
15
|
visibility?: string;
|
|
16
16
|
}
|
|
17
17
|
export interface AssistantSessionTarget {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-session-controller.js","sourceRoot":"","sources":["../../client/utils/assistant-session-controller.ts"],"names":[],"mappings":"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","sourcesContent":["export interface AssistantSession {\n id: string\n name?: string\n /**\n * 어느 대화인가 — 등록된 모드의 kind (ADR-0072 결정 3).\n *\n * 목록은 한 대상의 대화를 **모두** 보여 준다. 그 대상 위에 대화가 둘 설 수 있으므로(A17), 고른 것이\n * 지금 열려 있는 대화와 다를 수 있다. 그때 화면은 그 대화로 따라가야 한다 — 안 그러면 문이 거절하고\n * (`session-kind-mismatch`), 목록은 눌리는데 안 열리는 목록이 된다.\n *\n * 칸이 서기 전에 난 세션에는 값이 없다. 그때는 지금 대화로 두고, 서버가 다음 턴에 찍는다.\n */\n kind?: string\n /** Who reads it: 'open' (everyone in the domain) or 'private' (participants). The dock draws an open one apart. */\n visibility?: string\n}\nexport interface AssistantSessionTarget {\n /** Account/domain boundary supplied by the authenticated host. */\n scope: string\n anchorType: string\n anchorId: string\n /** The conversation kind (mode) the tabs list - all readable conversations of it, not only mine (shared dock §2-6). */\n kind?: string\n}\nexport interface AssistantSessionTransport {\n list(target: AssistantSessionTarget): Promise<AssistantSession[]>\n create(target: AssistantSessionTarget): Promise<AssistantSession>\n isEmpty?(sessionId: string): Promise<boolean>\n /**\n * 이 대화를 여기까지 읽었다 — 열린 대화가 바뀔 때 한 번.\n *\n * 읽은 지점은 **다른 참여자에게 보이는 사실**이다(안 읽음 배지 · 목록의 굵은 글씨). 도크마다 손으로\n * 부르던 것을 여기로 올렸다 — twin 만 부르고 있었고, 멘션으로 사람이 들어올 수 있게 된 뒤에는\n * 세 화면이 같은 사실을 달리 말하게 된다.\n */\n markRead?(sessionId: string): Promise<void> | void\n}\nexport interface AssistantSessionState {\n target?: AssistantSessionTarget\n sessions: AssistantSession[]\n sessionId: string\n loading: boolean\n error: string\n /** Changes on reset, including temporary conversations without a server ID. */\n revision: number\n}\nconst keyOf = (target?: AssistantSessionTarget) => target\n ? JSON.stringify([target.scope, target.anchorType, target.anchorId]) : ''\n\n/** Domain-neutral session lifecycle. Never deletes history or edits host documents. */\nexport class AssistantSessionController {\n state: AssistantSessionState = { sessions: [], sessionId: '', loading: false, error: '', revision: 0 }\n private generation = 0\n private preferred = new Map<string, string>()\n private pending = new Map<string, Promise<AssistantSession[]>>()\n\n private transport: AssistantSessionTransport\n private changed: (state: AssistantSessionState) => void\n private storage?: Pick<Storage, 'getItem' | 'setItem'>\n private lazy: boolean\n private preparing?: Promise<string | undefined>\n constructor(transport: AssistantSessionTransport, changed: (state: AssistantSessionState) => void,\n storage?: Pick<Storage, 'getItem' | 'setItem'>, options: { lazy?: boolean } = {}) {\n this.transport = transport\n this.changed = changed\n this.storage = storage\n this.lazy = !!options.lazy\n }\n\n private remember(key: string, id: string) {\n this.preferred.set(key, id)\n try { this.storage?.setItem('assistant-session:' + key, id) } catch { /* Storage is optional. */ }\n }\n\n private preference(key: string): string | undefined {\n try { return this.preferred.get(key) || this.storage?.getItem('assistant-session:' + key) || undefined }\n catch { return this.preferred.get(key) }\n }\n\n private publish(patch: Partial<AssistantSessionState>) {\n const before = this.state.sessionId\n this.state = { ...this.state, ...patch }\n /*\n * 열린 대화가 바뀌면 읽음 지점을 옮긴다. 한 자리에서 하는 이유는 여는 길이 넷이기 때문이다\n * (열기 · 고르기 · 새로 시작 · 보내며 만들기) — 갈래마다 부르면 한 곳이 빠진다.\n */\n if (this.state.sessionId && this.state.sessionId !== before) void this.markRead(this.state.sessionId)\n this.changed(this.state)\n }\n\n /** 실패는 삼킨다 — 읽음 표시가 못 가는 것이 대화를 막을 이유는 아니다. */\n private async markRead(sessionId: string): Promise<void> {\n try {\n await this.transport.markRead?.(sessionId)\n } catch {\n /* noop */\n }\n }\n\n async open(target?: AssistantSessionTarget, force = false): Promise<void> {\n const key = keyOf(target)\n if (!force && key === keyOf(this.state.target) && (this.state.loading || this.state.sessionId || (this.lazy && this.state.target && !this.state.error))) return\n const generation = ++this.generation\n const snapshot = target ? { ...target } : undefined\n this.publish({ target: snapshot, sessions: [], sessionId: '', loading: !!target, error: '' })\n if (!snapshot) return\n try {\n let pending = this.pending.get(key)\n if (!pending) {\n pending = (async () => {\n const sessions = await this.transport.list(snapshot)\n // Failed/malformed reads must not create replacement sessions.\n if (!Array.isArray(sessions) || sessions.some(session => !session.id)) throw new Error('Invalid session list')\n if (sessions.length || this.lazy) return [...sessions]\n // Do not issue a mutation for a target abandoned while the inbox was loading.\n if (keyOf(this.state.target) !== key || this.pending.get(key) !== pending) throw new Error('Session target changed')\n const created = await this.transport.create(snapshot)\n if (!created?.id) throw new Error('Invalid new session')\n return [created]\n })()\n this.pending.set(key, pending)\n void pending.finally(() => {\n if (this.pending.get(key) === pending) this.pending.delete(key)\n }).catch(() => {})\n }\n const sessions = await pending\n if (generation !== this.generation) return\n const preference = this.preference(key)\n const sessionId = this.lazy && preference === '__draft__' ? '' : sessions.find(session => session.id === preference)?.id || sessions[0]?.id || ''\n this.remember(key, sessionId || '__draft__')\n this.publish({ sessions, sessionId, loading: false })\n } catch (error) {\n if (generation === this.generation) this.publish({ loading: false, error: error instanceof Error ? error.message : String(error) })\n }\n }\n\n select(id: string): boolean {\n if (this.state.loading || !this.state.sessions.some(session => session.id === id)) return false\n this.remember(keyOf(this.state.target), id)\n this.publish({ sessionId: id, error: '' })\n return true\n }\n\n async reset(): Promise<void> {\n if (this.state.loading || this.preparing) return\n if (this.lazy) {\n if (!this.state.sessionId) return\n ++this.generation\n this.remember(keyOf(this.state.target), '__draft__')\n this.publish({ sessionId: '', error: '', revision: this.state.revision + 1 })\n return\n }\n const target = this.state.target\n if (!target) {\n this.publish({ sessions: [], sessionId: '', error: '', revision: this.state.revision + 1 })\n return\n }\n const generation = ++this.generation\n const key = keyOf(target)\n this.publish({ loading: true, error: '' })\n try {\n if (this.state.sessionId && this.transport.isEmpty) {\n const empty = await this.transport.isEmpty(this.state.sessionId)\n if (generation !== this.generation) return\n if (empty) { this.publish({ loading: false }); return }\n }\n const session = await this.transport.create({ ...target })\n if (!session?.id) throw new Error('Invalid new session')\n this.remember(key, session.id)\n if (generation !== this.generation) return\n this.publish({ sessions: [session, ...this.state.sessions.filter(existing => existing.id !== session.id)], sessionId: session.id,\n loading: false, revision: this.state.revision + 1 })\n } catch (error) {\n if (generation === this.generation) this.publish({ loading: false, error: error instanceof Error ? error.message : String(error) })\n }\n }\n\n /** Called only for a real send. Adoption happens before hosts render the new ID. */\n prepareSession(adopt: (id: string) => void = () => {}): Promise<string | undefined> {\n if (this.state.sessionId) return Promise.resolve(this.state.sessionId)\n if (!this.state.target) return Promise.resolve(undefined)\n if (this.state.loading || this.state.error) return Promise.reject(new Error('Session history is not ready'))\n if (this.preparing) return this.preparing\n const target = { ...this.state.target }, generation = this.generation\n const pending = (async () => {\n const session = await this.transport.create(target)\n if (generation !== this.generation) throw new Error('Session target changed')\n if (!session?.id) throw new Error('Invalid new session')\n adopt(session.id)\n this.remember(keyOf(target), session.id)\n this.publish({ sessionId: session.id, sessions: [session, ...this.state.sessions] })\n return session.id\n })()\n this.preparing = pending\n void pending.finally(() => { if (this.preparing === pending) this.preparing = undefined }).catch(() => {})\n return pending\n }\n\n /** Invalidate pending UI updates at logout/disposal. Server authorization remains authoritative. */\n clear() {\n ++this.generation\n this.preferred.clear()\n this.pending.clear()\n this.publish({ target: undefined, sessions: [], sessionId: '', loading: false, error: '', revision: this.state.revision + 1 })\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assistant-session-controller.js","sourceRoot":"","sources":["../../client/utils/assistant-session-controller.ts"],"names":[],"mappings":"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","sourcesContent":["export interface AssistantSession {\n id: string\n name?: string\n /**\n * 어느 대화인가 — 등록된 모드의 kind (ADR-0072 결정 3).\n *\n * 목록은 한 대상의 대화를 **모두** 보여 준다. 그 대상 위에 대화가 둘 설 수 있으므로(A17), 고른 것이\n * 지금 열려 있는 대화와 다를 수 있다. 그때 화면은 그 대화로 따라가야 한다 — 안 그러면 문이 거절하고\n * (`session-kind-mismatch`), 목록은 눌리는데 안 열리는 목록이 된다.\n *\n * 칸이 서기 전에 난 세션에는 값이 없다. 그때는 지금 대화로 두고, 서버가 다음 턴에 찍는다.\n */\n kind?: string\n /** Who reads it: 'open' (everyone in the domain), 'group' (the people the owner brought in) or 'private' (the owner alone). The dock draws an open one apart. */\n visibility?: string\n}\nexport interface AssistantSessionTarget {\n /** Account/domain boundary supplied by the authenticated host. */\n scope: string\n anchorType: string\n anchorId: string\n /** The conversation kind (mode) the tabs list - all readable conversations of it, not only mine (shared dock §2-6). */\n kind?: string\n}\nexport interface AssistantSessionTransport {\n list(target: AssistantSessionTarget): Promise<AssistantSession[]>\n create(target: AssistantSessionTarget): Promise<AssistantSession>\n isEmpty?(sessionId: string): Promise<boolean>\n /**\n * 이 대화를 여기까지 읽었다 — 열린 대화가 바뀔 때 한 번.\n *\n * 읽은 지점은 **다른 참여자에게 보이는 사실**이다(안 읽음 배지 · 목록의 굵은 글씨). 도크마다 손으로\n * 부르던 것을 여기로 올렸다 — twin 만 부르고 있었고, 멘션으로 사람이 들어올 수 있게 된 뒤에는\n * 세 화면이 같은 사실을 달리 말하게 된다.\n */\n markRead?(sessionId: string): Promise<void> | void\n}\nexport interface AssistantSessionState {\n target?: AssistantSessionTarget\n sessions: AssistantSession[]\n sessionId: string\n loading: boolean\n error: string\n /** Changes on reset, including temporary conversations without a server ID. */\n revision: number\n}\nconst keyOf = (target?: AssistantSessionTarget) => target\n ? JSON.stringify([target.scope, target.anchorType, target.anchorId]) : ''\n\n/** Domain-neutral session lifecycle. Never deletes history or edits host documents. */\nexport class AssistantSessionController {\n state: AssistantSessionState = { sessions: [], sessionId: '', loading: false, error: '', revision: 0 }\n private generation = 0\n private preferred = new Map<string, string>()\n private pending = new Map<string, Promise<AssistantSession[]>>()\n\n private transport: AssistantSessionTransport\n private changed: (state: AssistantSessionState) => void\n private storage?: Pick<Storage, 'getItem' | 'setItem'>\n private lazy: boolean\n private preparing?: Promise<string | undefined>\n constructor(transport: AssistantSessionTransport, changed: (state: AssistantSessionState) => void,\n storage?: Pick<Storage, 'getItem' | 'setItem'>, options: { lazy?: boolean } = {}) {\n this.transport = transport\n this.changed = changed\n this.storage = storage\n this.lazy = !!options.lazy\n }\n\n private remember(key: string, id: string) {\n this.preferred.set(key, id)\n try { this.storage?.setItem('assistant-session:' + key, id) } catch { /* Storage is optional. */ }\n }\n\n private preference(key: string): string | undefined {\n try { return this.preferred.get(key) || this.storage?.getItem('assistant-session:' + key) || undefined }\n catch { return this.preferred.get(key) }\n }\n\n private publish(patch: Partial<AssistantSessionState>) {\n const before = this.state.sessionId\n this.state = { ...this.state, ...patch }\n /*\n * 열린 대화가 바뀌면 읽음 지점을 옮긴다. 한 자리에서 하는 이유는 여는 길이 넷이기 때문이다\n * (열기 · 고르기 · 새로 시작 · 보내며 만들기) — 갈래마다 부르면 한 곳이 빠진다.\n */\n if (this.state.sessionId && this.state.sessionId !== before) void this.markRead(this.state.sessionId)\n this.changed(this.state)\n }\n\n /** 실패는 삼킨다 — 읽음 표시가 못 가는 것이 대화를 막을 이유는 아니다. */\n private async markRead(sessionId: string): Promise<void> {\n try {\n await this.transport.markRead?.(sessionId)\n } catch {\n /* noop */\n }\n }\n\n async open(target?: AssistantSessionTarget, force = false): Promise<void> {\n const key = keyOf(target)\n if (!force && key === keyOf(this.state.target) && (this.state.loading || this.state.sessionId || (this.lazy && this.state.target && !this.state.error))) return\n const generation = ++this.generation\n const snapshot = target ? { ...target } : undefined\n this.publish({ target: snapshot, sessions: [], sessionId: '', loading: !!target, error: '' })\n if (!snapshot) return\n try {\n let pending = this.pending.get(key)\n if (!pending) {\n pending = (async () => {\n const sessions = await this.transport.list(snapshot)\n // Failed/malformed reads must not create replacement sessions.\n if (!Array.isArray(sessions) || sessions.some(session => !session.id)) throw new Error('Invalid session list')\n if (sessions.length || this.lazy) return [...sessions]\n // Do not issue a mutation for a target abandoned while the inbox was loading.\n if (keyOf(this.state.target) !== key || this.pending.get(key) !== pending) throw new Error('Session target changed')\n const created = await this.transport.create(snapshot)\n if (!created?.id) throw new Error('Invalid new session')\n return [created]\n })()\n this.pending.set(key, pending)\n void pending.finally(() => {\n if (this.pending.get(key) === pending) this.pending.delete(key)\n }).catch(() => {})\n }\n const sessions = await pending\n if (generation !== this.generation) return\n const preference = this.preference(key)\n const sessionId = this.lazy && preference === '__draft__' ? '' : sessions.find(session => session.id === preference)?.id || sessions[0]?.id || ''\n this.remember(key, sessionId || '__draft__')\n this.publish({ sessions, sessionId, loading: false })\n } catch (error) {\n if (generation === this.generation) this.publish({ loading: false, error: error instanceof Error ? error.message : String(error) })\n }\n }\n\n select(id: string): boolean {\n if (this.state.loading || !this.state.sessions.some(session => session.id === id)) return false\n this.remember(keyOf(this.state.target), id)\n this.publish({ sessionId: id, error: '' })\n return true\n }\n\n async reset(): Promise<void> {\n if (this.state.loading || this.preparing) return\n if (this.lazy) {\n if (!this.state.sessionId) return\n ++this.generation\n this.remember(keyOf(this.state.target), '__draft__')\n this.publish({ sessionId: '', error: '', revision: this.state.revision + 1 })\n return\n }\n const target = this.state.target\n if (!target) {\n this.publish({ sessions: [], sessionId: '', error: '', revision: this.state.revision + 1 })\n return\n }\n const generation = ++this.generation\n const key = keyOf(target)\n this.publish({ loading: true, error: '' })\n try {\n if (this.state.sessionId && this.transport.isEmpty) {\n const empty = await this.transport.isEmpty(this.state.sessionId)\n if (generation !== this.generation) return\n if (empty) { this.publish({ loading: false }); return }\n }\n const session = await this.transport.create({ ...target })\n if (!session?.id) throw new Error('Invalid new session')\n this.remember(key, session.id)\n if (generation !== this.generation) return\n this.publish({ sessions: [session, ...this.state.sessions.filter(existing => existing.id !== session.id)], sessionId: session.id,\n loading: false, revision: this.state.revision + 1 })\n } catch (error) {\n if (generation === this.generation) this.publish({ loading: false, error: error instanceof Error ? error.message : String(error) })\n }\n }\n\n /** Called only for a real send. Adoption happens before hosts render the new ID. */\n prepareSession(adopt: (id: string) => void = () => {}): Promise<string | undefined> {\n if (this.state.sessionId) return Promise.resolve(this.state.sessionId)\n if (!this.state.target) return Promise.resolve(undefined)\n if (this.state.loading || this.state.error) return Promise.reject(new Error('Session history is not ready'))\n if (this.preparing) return this.preparing\n const target = { ...this.state.target }, generation = this.generation\n const pending = (async () => {\n const session = await this.transport.create(target)\n if (generation !== this.generation) throw new Error('Session target changed')\n if (!session?.id) throw new Error('Invalid new session')\n adopt(session.id)\n this.remember(keyOf(target), session.id)\n this.publish({ sessionId: session.id, sessions: [session, ...this.state.sessions] })\n return session.id\n })()\n this.preparing = pending\n void pending.finally(() => { if (this.preparing === pending) this.preparing = undefined }).catch(() => {})\n return pending\n }\n\n /** Invalidate pending UI updates at logout/disposal. Server authorization remains authoritative. */\n clear() {\n ++this.generation\n this.preferred.clear()\n this.pending.clear()\n this.publish({ target: undefined, sessions: [], sessionId: '', loading: false, error: '', revision: this.state.revision + 1 })\n }\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** How often a typist says it while typing. */
|
|
2
|
+
export declare const TYPING_SAY_EVERY_MS = 3000;
|
|
3
|
+
/** How long a notice stands without the next - the server's `TYPING_NOTICE_MS`. */
|
|
4
|
+
export declare const TYPING_SHOW_MS = 6000;
|
|
5
|
+
export interface TypingNotice {
|
|
6
|
+
userId: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class TypingRoom {
|
|
10
|
+
private saidAt;
|
|
11
|
+
private heardFrom;
|
|
12
|
+
/** Should the typist say it now? Only with words in the box, and not more often than every few seconds. */
|
|
13
|
+
shouldSay(text: string, now?: number): boolean;
|
|
14
|
+
heard(notice: TypingNotice, now?: number): void;
|
|
15
|
+
/** That person's message arrived - they are not typing it any more. */
|
|
16
|
+
spoke(userId: string | undefined): boolean;
|
|
17
|
+
/** The names typing now, in the order they started; faded ones are dropped. */
|
|
18
|
+
typing(now?: number): string[];
|
|
19
|
+
clear(): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* "Someone is typing" on the client - when to say it, and whose to show. Pure, so it is tested alone
|
|
3
|
+
* (`assistant-typing.test.ts`); the chat sends and receives, this keeps the times.
|
|
4
|
+
*
|
|
5
|
+
* Nothing is stored on the server, so a notice stands only until the next would have come: the typist says it
|
|
6
|
+
* every few seconds while the words change, the room lets it fade a little after the last one, and a message
|
|
7
|
+
* from that person ends it at once - the words arrived.
|
|
8
|
+
*/
|
|
9
|
+
/** How often a typist says it while typing. */
|
|
10
|
+
export const TYPING_SAY_EVERY_MS = 3_000;
|
|
11
|
+
/** How long a notice stands without the next - the server's `TYPING_NOTICE_MS`. */
|
|
12
|
+
export const TYPING_SHOW_MS = 6_000;
|
|
13
|
+
export class TypingRoom {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.saidAt = 0;
|
|
16
|
+
this.heardFrom = new Map();
|
|
17
|
+
}
|
|
18
|
+
/** Should the typist say it now? Only with words in the box, and not more often than every few seconds. */
|
|
19
|
+
shouldSay(text, now = Date.now()) {
|
|
20
|
+
if (!text.trim())
|
|
21
|
+
return false;
|
|
22
|
+
if (now - this.saidAt < TYPING_SAY_EVERY_MS)
|
|
23
|
+
return false;
|
|
24
|
+
this.saidAt = now;
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
heard(notice, now = Date.now()) {
|
|
28
|
+
if (!notice?.userId)
|
|
29
|
+
return;
|
|
30
|
+
this.heardFrom.set(notice.userId, { name: notice.name, until: now + TYPING_SHOW_MS });
|
|
31
|
+
}
|
|
32
|
+
/** That person's message arrived - they are not typing it any more. */
|
|
33
|
+
spoke(userId) {
|
|
34
|
+
return !!userId && this.heardFrom.delete(userId);
|
|
35
|
+
}
|
|
36
|
+
/** The names typing now, in the order they started; faded ones are dropped. */
|
|
37
|
+
typing(now = Date.now()) {
|
|
38
|
+
for (const [id, one] of this.heardFrom)
|
|
39
|
+
if (one.until <= now)
|
|
40
|
+
this.heardFrom.delete(id);
|
|
41
|
+
return [...this.heardFrom.values()].map(one => one.name);
|
|
42
|
+
}
|
|
43
|
+
clear() {
|
|
44
|
+
this.saidAt = 0;
|
|
45
|
+
this.heardFrom.clear();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=assistant-typing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistant-typing.js","sourceRoot":"","sources":["../../client/utils/assistant-typing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,+CAA+C;AAC/C,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,CAAA;AAExC,mFAAmF;AACnF,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAA;AAOnC,MAAM,OAAO,UAAU;IAAvB;QACU,WAAM,GAAG,CAAC,CAAA;QACV,cAAS,GAAG,IAAI,GAAG,EAA2C,CAAA;IA8BxE,CAAC;IA5BC,2GAA2G;IAC3G,SAAS,CAAC,IAAY,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACtC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,KAAK,CAAA;QAC9B,IAAI,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,mBAAmB;YAAE,OAAO,KAAK,CAAA;QACzD,IAAI,CAAC,MAAM,GAAG,GAAG,CAAA;QACjB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,KAAK,CAAC,MAAoB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QAC1C,IAAI,CAAC,MAAM,EAAE,MAAM;YAAE,OAAM;QAC3B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,cAAc,EAAE,CAAC,CAAA;IACvF,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,MAA0B;QAC9B,OAAO,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAClD,CAAC;IAED,+EAA+E;IAC/E,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACrB,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,SAAS;YAAE,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG;gBAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QACvF,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC1D,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;QACf,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;IACxB,CAAC;CACF","sourcesContent":["/*\n * \"Someone is typing\" on the client - when to say it, and whose to show. Pure, so it is tested alone\n * (`assistant-typing.test.ts`); the chat sends and receives, this keeps the times.\n *\n * Nothing is stored on the server, so a notice stands only until the next would have come: the typist says it\n * every few seconds while the words change, the room lets it fade a little after the last one, and a message\n * from that person ends it at once - the words arrived.\n */\n\n/** How often a typist says it while typing. */\nexport const TYPING_SAY_EVERY_MS = 3_000\n\n/** How long a notice stands without the next - the server's `TYPING_NOTICE_MS`. */\nexport const TYPING_SHOW_MS = 6_000\n\nexport interface TypingNotice {\n userId: string\n name: string\n}\n\nexport class TypingRoom {\n private saidAt = 0\n private heardFrom = new Map<string, { name: string; until: number }>()\n\n /** Should the typist say it now? Only with words in the box, and not more often than every few seconds. */\n shouldSay(text: string, now = Date.now()): boolean {\n if (!text.trim()) return false\n if (now - this.saidAt < TYPING_SAY_EVERY_MS) return false\n this.saidAt = now\n return true\n }\n\n heard(notice: TypingNotice, now = Date.now()): void {\n if (!notice?.userId) return\n this.heardFrom.set(notice.userId, { name: notice.name, until: now + TYPING_SHOW_MS })\n }\n\n /** That person's message arrived - they are not typing it any more. */\n spoke(userId: string | undefined): boolean {\n return !!userId && this.heardFrom.delete(userId)\n }\n\n /** The names typing now, in the order they started; faded ones are dropped. */\n typing(now = Date.now()): string[] {\n for (const [id, one] of this.heardFrom) if (one.until <= now) this.heardFrom.delete(id)\n return [...this.heardFrom.values()].map(one => one.name)\n }\n\n clear(): void {\n this.saidAt = 0\n this.heardFrom.clear()\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { TYPING_SAY_EVERY_MS, TYPING_SHOW_MS, TypingRoom } from './assistant-typing';
|
|
2
|
+
describe('typing in a conversation', () => {
|
|
3
|
+
it('says it with words in the box, not on every key', () => {
|
|
4
|
+
const room = new TypingRoom();
|
|
5
|
+
expect(room.shouldSay('', 0)).toBe(false);
|
|
6
|
+
expect(room.shouldSay('h', 10_000)).toBe(true);
|
|
7
|
+
expect(room.shouldSay('he', 10_000 + TYPING_SAY_EVERY_MS - 1)).toBe(false);
|
|
8
|
+
expect(room.shouldSay('hel', 10_000 + TYPING_SAY_EVERY_MS)).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
it('★ a notice fades without the next one', () => {
|
|
11
|
+
const room = new TypingRoom();
|
|
12
|
+
room.heard({ userId: 'u2', name: 'Kim' }, 0);
|
|
13
|
+
expect(room.typing(TYPING_SHOW_MS - 1)).toEqual(['Kim']);
|
|
14
|
+
expect(room.typing(TYPING_SHOW_MS)).toEqual([]);
|
|
15
|
+
});
|
|
16
|
+
it('★ the message ends it at once', () => {
|
|
17
|
+
const room = new TypingRoom();
|
|
18
|
+
room.heard({ userId: 'u2', name: 'Kim' }, 0);
|
|
19
|
+
room.heard({ userId: 'u3', name: 'Lee' }, 0);
|
|
20
|
+
expect(room.spoke('u2')).toBe(true);
|
|
21
|
+
expect(room.typing(1)).toEqual(['Lee']);
|
|
22
|
+
});
|
|
23
|
+
it('a new notice from the same person keeps one line for them', () => {
|
|
24
|
+
const room = new TypingRoom();
|
|
25
|
+
room.heard({ userId: 'u2', name: 'Kim' }, 0);
|
|
26
|
+
room.heard({ userId: 'u2', name: 'Kim' }, TYPING_SHOW_MS - 1);
|
|
27
|
+
expect(room.typing(TYPING_SHOW_MS + 1)).toEqual(['Kim']);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=assistant-typing.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistant-typing.test.js","sourceRoot":"","sources":["../../client/utils/assistant-typing.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAEpF,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,IAAI,GAAG,IAAI,UAAU,EAAE,CAAA;QAC7B,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACzC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC9C,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC1E,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACxE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,IAAI,GAAG,IAAI,UAAU,EAAE,CAAA;QAC7B,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAA;QAC5C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;QACxD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,IAAI,GAAG,IAAI,UAAU,EAAE,CAAA;QAC7B,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAA;QAC5C,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAA;QAC5C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACnC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;IACzC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,IAAI,GAAG,IAAI,UAAU,EAAE,CAAA;QAC7B,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAA;QAC5C,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,cAAc,GAAG,CAAC,CAAC,CAAA;QAC7D,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;IAC1D,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import { TYPING_SAY_EVERY_MS, TYPING_SHOW_MS, TypingRoom } from './assistant-typing'\n\ndescribe('typing in a conversation', () => {\n it('says it with words in the box, not on every key', () => {\n const room = new TypingRoom()\n expect(room.shouldSay('', 0)).toBe(false)\n expect(room.shouldSay('h', 10_000)).toBe(true)\n expect(room.shouldSay('he', 10_000 + TYPING_SAY_EVERY_MS - 1)).toBe(false)\n expect(room.shouldSay('hel', 10_000 + TYPING_SAY_EVERY_MS)).toBe(true)\n })\n\n it('★ a notice fades without the next one', () => {\n const room = new TypingRoom()\n room.heard({ userId: 'u2', name: 'Kim' }, 0)\n expect(room.typing(TYPING_SHOW_MS - 1)).toEqual(['Kim'])\n expect(room.typing(TYPING_SHOW_MS)).toEqual([])\n })\n\n it('★ the message ends it at once', () => {\n const room = new TypingRoom()\n room.heard({ userId: 'u2', name: 'Kim' }, 0)\n room.heard({ userId: 'u3', name: 'Lee' }, 0)\n expect(room.spoke('u2')).toBe(true)\n expect(room.typing(1)).toEqual(['Lee'])\n })\n\n it('a new notice from the same person keeps one line for them', () => {\n const room = new TypingRoom()\n room.heard({ userId: 'u2', name: 'Kim' }, 0)\n room.heard({ userId: 'u2', name: 'Kim' }, TYPING_SHOW_MS - 1)\n expect(room.typing(TYPING_SHOW_MS + 1)).toEqual(['Kim'])\n })\n})\n"]}
|
|
@@ -379,9 +379,10 @@ let AssistantChatResolver = class AssistantChatResolver {
|
|
|
379
379
|
}
|
|
380
380
|
/*
|
|
381
381
|
* 사람이 `@이름` 으로 부른 사람은 이 대화에 들어온다(ADR-0071 결정 1). 이 문은 **사람의 턴**을
|
|
382
|
-
* 저장하는 문이라 그 일을 한다 — 모델의 답이나 도구 결과는 아무도 초대하지 않는다.
|
|
382
|
+
* 저장하는 문이라 그 일을 한다 — 모델의 답이나 도구 결과는 아무도 초대하지 않는다. 공개 방에서 말한 사람도
|
|
383
|
+
* 이때 자리에 앉는다(`seatTurn`) — 참여자 줄 · 접속 표시 · 안 읽음이 실제로 말한 사람과 맞도록.
|
|
383
384
|
*/
|
|
384
|
-
const invite = await (0, mention_invite_js_1.
|
|
385
|
+
const invite = await (0, mention_invite_js_1.seatTurn)({
|
|
385
386
|
content: lastUserMessage.content,
|
|
386
387
|
sessionId: session.id,
|
|
387
388
|
domainId: domain.id,
|
|
@@ -389,16 +390,8 @@ let AssistantChatResolver = class AssistantChatResolver {
|
|
|
389
390
|
participants: (0, shell_1.getRepository)(chat_session_participant_js_1.ChatSessionParticipant),
|
|
390
391
|
visibility: session.visibility
|
|
391
392
|
});
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
try {
|
|
395
|
-
if (payload)
|
|
396
|
-
await context.state?.notify?.(payload);
|
|
397
|
-
}
|
|
398
|
-
catch {
|
|
399
|
-
/* 편지가 못 갔다고 방에 들어온 사실이 취소되지는 않는다. */
|
|
400
|
-
}
|
|
401
|
-
}
|
|
393
|
+
/* 편지가 못 갔다고 방에 들어온 사실이 취소되지는 않는다(`sendInviteNotification`). */
|
|
394
|
+
await (0, invite_notification_js_1.sendInviteNotification)(context.state?.notify, invite.invited, user, session?.name, undefined, (0, request_translator_js_1.requestTranslator)(context));
|
|
402
395
|
(0, chat_message_publish_js_1.publishChatMessage)({ domainId: domain.id, sessionId: session.id, message: saved });
|
|
403
396
|
(0, session_activity_publish_js_1.publishSessionActivity)({
|
|
404
397
|
domainId: domain.id,
|