@things-factory/ai-assistant 10.1.8 → 10.1.15
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/{board-ai-chat.ts → assistant-chat.ts} +597 -66
- package/client/components/assistant-session-toolbar.ts +70 -0
- package/client/components/chat-input-builder.ts +1 -1
- package/client/index.ts +6 -3
- package/client/utils/assistant-request-context.ts +21 -0
- package/client/utils/assistant-session-controller.ts +166 -0
- package/client/utils/assistant-session-transport.ts +34 -0
- package/dist-client/components/{board-ai-chat.d.ts → assistant-chat.d.ts} +30 -3
- package/dist-client/components/{board-ai-chat.js → assistant-chat.js} +616 -109
- package/dist-client/components/assistant-chat.js.map +1 -0
- package/dist-client/components/{board-ai-chat.test.js → assistant-chat.test.js} +1 -1
- package/dist-client/components/assistant-chat.test.js.map +1 -0
- package/dist-client/components/assistant-session-toolbar.d.ts +18 -0
- package/dist-client/components/assistant-session-toolbar.js +109 -0
- package/dist-client/components/assistant-session-toolbar.js.map +1 -0
- package/dist-client/components/chat-input-builder.js +1 -1
- package/dist-client/components/chat-input-builder.js.map +1 -1
- package/dist-client/index.d.ts +6 -3
- package/dist-client/index.js +6 -3
- package/dist-client/index.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/assistant-request-context.d.ts +10 -0
- package/dist-client/utils/assistant-request-context.js +20 -0
- package/dist-client/utils/assistant-request-context.js.map +1 -0
- package/dist-client/utils/assistant-session-controller.d.ts +49 -0
- package/dist-client/utils/assistant-session-controller.js +165 -0
- package/dist-client/utils/assistant-session-controller.js.map +1 -0
- package/dist-client/utils/assistant-session-transport.d.ts +3 -0
- package/dist-client/utils/assistant-session-transport.js +39 -0
- package/dist-client/utils/assistant-session-transport.js.map +1 -0
- package/dist-server/service/assistant-chat-resolver.js +20 -0
- package/dist-server/service/assistant-chat-resolver.js.map +1 -1
- package/dist-server/service/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-anchor.d.ts +1 -1
- package/dist-server/service/chat-session/session-anchor.js +3 -1
- package/dist-server/service/chat-session/session-anchor.js.map +1 -1
- package/dist-server/service/chat-session/station-session-guard.d.ts +12 -0
- package/dist-server/service/chat-session/station-session-guard.js +20 -0
- package/dist-server/service/chat-session/station-session-guard.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/docs/session-controller.md +52 -0
- package/package.json +2 -2
- package/server/service/assistant-chat-resolver.ts +22 -0
- package/server/service/chat-session/chat-session.ts +1 -1
- package/server/service/chat-session/session-anchor.ts +3 -1
- package/server/service/chat-session/station-session-guard.ts +17 -0
- package/server/service/session-anchor.test.ts +11 -0
- package/server/service/station-session-guard.test.ts +24 -0
- package/test/assistant-request-context.test.ts +30 -0
- package/test/assistant-session-controller.test.ts +88 -0
- package/test/assistant-session-toolbar.test.ts +29 -0
- package/translations/en.json +12 -1
- package/translations/ko.json +12 -1
- package/dist-client/components/board-ai-chat.js.map +0 -1
- package/dist-client/components/board-ai-chat.test.js.map +0 -1
- /package/client/components/{board-ai-chat.test.ts → assistant-chat.test.ts} +0 -0
- /package/dist-client/components/{board-ai-chat.test.d.ts → assistant-chat.test.d.ts} +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import { LitElement, css, html, nothing } from 'lit';
|
|
3
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
4
|
+
import { i18next } from '@operato/i18n';
|
|
5
|
+
import { assistantRecentSessions } from '../utils/assistant-request-context.js';
|
|
6
|
+
/** Product-neutral controls. The host/controller owns effects; this view never mutates sessions. */
|
|
7
|
+
let AssistantSessionToolbar = class AssistantSessionToolbar extends LitElement {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.sessions = [];
|
|
11
|
+
this.sessionId = '';
|
|
12
|
+
this.loading = false;
|
|
13
|
+
this.disabled = false;
|
|
14
|
+
this.temporary = false;
|
|
15
|
+
this.error = '';
|
|
16
|
+
this.historyEnabled = false;
|
|
17
|
+
this.maxTabs = 5;
|
|
18
|
+
/** 좁은 도크의 문맥 헤더 바로 아래에서 쓰는 밀도. 기능은 줄이지 않는다. */
|
|
19
|
+
this.compact = false;
|
|
20
|
+
}
|
|
21
|
+
static { this.styles = css `
|
|
22
|
+
:host { display: block; min-width: 0; }
|
|
23
|
+
.controls { display: flex; align-items: center; gap: 6px; }
|
|
24
|
+
button { box-sizing: border-box; height: 28px; font: inherit; font-size: 12px;
|
|
25
|
+
color: var(--secondary-color, #68747c); border: 0; border-radius: 4px;
|
|
26
|
+
background: transparent; cursor: pointer; }
|
|
27
|
+
.tabs { display:flex; flex:1; min-width:0; overflow-x:auto; gap:1px; scrollbar-width:thin; }
|
|
28
|
+
[role=tab] { max-width:160px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
|
|
29
|
+
padding: 0 8px; border-bottom: 2px solid transparent; border-radius: 3px 3px 0 0; }
|
|
30
|
+
[role=tab]:hover { background: color-mix(in srgb, var(--primary-color, #007f87) 7%, transparent); color: var(--primary-color, #007f87); }
|
|
31
|
+
[aria-selected=true] { color:var(--primary-color, #007f87); border-bottom-color: currentColor; font-weight:600; }
|
|
32
|
+
.session-new { flex: none; padding: 0 7px; font-weight: 500; white-space: nowrap; }
|
|
33
|
+
.session-new:hover, .history:hover { color: var(--primary-color, #007f87); background: color-mix(in srgb, var(--primary-color, #007f87) 7%, transparent); }
|
|
34
|
+
.history { height: 24px; margin: 1px 0 0 4px; padding: 0 5px; font-size: 11px; color: var(--tertiary-color, #88939a); }
|
|
35
|
+
:host([compact]) .controls { gap: 3px; min-height: 24px; }
|
|
36
|
+
:host([compact]) button { height: 24px; font-size: 11px; }
|
|
37
|
+
:host([compact]) [role=tab] { max-width: 132px; padding: 0 6px; border-bottom-width: 1px; }
|
|
38
|
+
:host([compact]) .session-new { padding: 0 5px; }
|
|
39
|
+
:disabled { opacity: .5; cursor: default; }
|
|
40
|
+
:focus-visible { outline: 2px solid var(--primary-color, #007f87); outline-offset: 2px; }
|
|
41
|
+
p { margin: 6px 0 0; font-size: 12px; overflow-wrap: anywhere; }
|
|
42
|
+
[role=alert] { color: var(--error-color, #b42318); }
|
|
43
|
+
`; }
|
|
44
|
+
emit(name, detail) {
|
|
45
|
+
this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
|
|
46
|
+
}
|
|
47
|
+
render() {
|
|
48
|
+
const label = i18next.t('ai-assistant.session.history', { defaultValue: '대화 이력' });
|
|
49
|
+
return html `
|
|
50
|
+
<div class="controls">
|
|
51
|
+
${this.temporary ? nothing : html `<div class="tabs" role="tablist" aria-label=${label}>
|
|
52
|
+
${!this.sessionId ? html `<button role="tab" aria-selected="true">${i18next.t('ai-assistant.session.new', { defaultValue: '새 대화' })}</button>` : nothing}
|
|
53
|
+
${assistantRecentSessions(this.sessions, this.sessionId, this.historyEnabled ? this.maxTabs : this.sessions.length).map((session, index) => html `<button role="tab" aria-selected=${session.id === this.sessionId ? 'true' : 'false'}
|
|
54
|
+
?disabled=${this.disabled || this.loading} title=${session.name || `${label} ${index + 1}`}
|
|
55
|
+
@click=${() => this.emit('assistant-session-select', { sessionId: session.id })}>${session.name || `${label} ${index + 1}`}</button>`)}
|
|
56
|
+
</div>`}
|
|
57
|
+
<button class="session-new" ?disabled=${this.disabled || this.loading || !!this.error}
|
|
58
|
+
title=${i18next.t('ai-assistant.session.reset-help', { defaultValue: '이전 이력과 작업 내용은 보존하고 새 대화를 시작합니다. 이전 대화는 다음 요청에 포함하지 않습니다.' })}
|
|
59
|
+
@click=${() => this.emit('assistant-session-reset')}>+ ${i18next.t('ai-assistant.session.new', { defaultValue: '새 대화' })}</button>
|
|
60
|
+
</div>
|
|
61
|
+
${this.historyEnabled ? html `<button class="history" ?disabled=${this.loading || this.disabled}
|
|
62
|
+
@click=${() => this.emit('assistant-session-history')}>${i18next.t('ai-assistant.session.all', { defaultValue: '전체 이력' })}</button>` : nothing}
|
|
63
|
+
${this.loading ? html `<p role="status">${i18next.t('ai-assistant.session.loading', { defaultValue: '대화 이력을 불러오는 중…' })}</p>` : nothing}
|
|
64
|
+
${this.error ? html `<p role="alert">${this.error} <button ?disabled=${this.loading || this.disabled}
|
|
65
|
+
@click=${() => this.emit('assistant-session-retry')}>${i18next.t('ai-assistant.session.retry', { defaultValue: '다시 시도' })}</button></p>` : nothing}
|
|
66
|
+
`;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
__decorate([
|
|
70
|
+
property({ attribute: false }),
|
|
71
|
+
__metadata("design:type", Array)
|
|
72
|
+
], AssistantSessionToolbar.prototype, "sessions", void 0);
|
|
73
|
+
__decorate([
|
|
74
|
+
property(),
|
|
75
|
+
__metadata("design:type", Object)
|
|
76
|
+
], AssistantSessionToolbar.prototype, "sessionId", void 0);
|
|
77
|
+
__decorate([
|
|
78
|
+
property({ type: Boolean }),
|
|
79
|
+
__metadata("design:type", Object)
|
|
80
|
+
], AssistantSessionToolbar.prototype, "loading", void 0);
|
|
81
|
+
__decorate([
|
|
82
|
+
property({ type: Boolean }),
|
|
83
|
+
__metadata("design:type", Object)
|
|
84
|
+
], AssistantSessionToolbar.prototype, "disabled", void 0);
|
|
85
|
+
__decorate([
|
|
86
|
+
property({ type: Boolean }),
|
|
87
|
+
__metadata("design:type", Object)
|
|
88
|
+
], AssistantSessionToolbar.prototype, "temporary", void 0);
|
|
89
|
+
__decorate([
|
|
90
|
+
property(),
|
|
91
|
+
__metadata("design:type", Object)
|
|
92
|
+
], AssistantSessionToolbar.prototype, "error", void 0);
|
|
93
|
+
__decorate([
|
|
94
|
+
property({ type: Boolean }),
|
|
95
|
+
__metadata("design:type", Object)
|
|
96
|
+
], AssistantSessionToolbar.prototype, "historyEnabled", void 0);
|
|
97
|
+
__decorate([
|
|
98
|
+
property({ type: Number }),
|
|
99
|
+
__metadata("design:type", Object)
|
|
100
|
+
], AssistantSessionToolbar.prototype, "maxTabs", void 0);
|
|
101
|
+
__decorate([
|
|
102
|
+
property({ type: Boolean, reflect: true }),
|
|
103
|
+
__metadata("design:type", Object)
|
|
104
|
+
], AssistantSessionToolbar.prototype, "compact", void 0);
|
|
105
|
+
AssistantSessionToolbar = __decorate([
|
|
106
|
+
customElement('ox-assistant-session-toolbar')
|
|
107
|
+
], AssistantSessionToolbar);
|
|
108
|
+
export { AssistantSessionToolbar };
|
|
109
|
+
//# sourceMappingURL=assistant-session-toolbar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistant-session-toolbar.js","sourceRoot":"","sources":["../../client/components/assistant-session-toolbar.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAE/E,oGAAoG;AAE7F,IAAM,uBAAuB,GAA7B,MAAM,uBAAwB,SAAQ,UAAU;IAAhD;;QAC2B,aAAQ,GAAuB,EAAE,CAAA;QACrD,cAAS,GAAG,EAAE,CAAA;QACG,YAAO,GAAG,KAAK,CAAA;QACf,aAAQ,GAAG,KAAK,CAAA;QAChB,cAAS,GAAG,KAAK,CAAA;QAClC,UAAK,GAAG,EAAE,CAAA;QACO,mBAAc,GAAG,KAAK,CAAA;QACvB,YAAO,GAAG,CAAC,CAAA;QACvC,+CAA+C;QACH,YAAO,GAAG,KAAK,CAAA;IAmD7D,CAAC;aAjDQ,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;GAsBlB,AAtBY,CAsBZ;IAEO,IAAI,CAAC,IAAY,EAAE,MAAgB;QACzC,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACtF,CAAC;IAED,MAAM;QACJ,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,8BAA8B,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAA;QAClF,OAAO,IAAI,CAAA;;UAEL,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,+CAA+C,KAAK;YACjF,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA,2CAA2C,OAAO,CAAC,CAAC,CAAC,0BAA0B,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO;YACrJ,uBAAuB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAA,oCAAoC,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;wBACtN,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,UAAU,OAAO,CAAC,IAAI,IAAI,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE;qBACjF,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,IAAI,IAAI,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,WAAW,CAAC;eACnI;gDACiC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK;kBAC3E,OAAO,CAAC,CAAC,CAAC,iCAAiC,EAAE,EAAE,YAAY,EAAE,0DAA0D,EAAE,CAAC;mBACzH,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,0BAA0B,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;;QAE1H,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAA,qCAAqC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ;iBACnF,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,2BAA2B,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,0BAA0B,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO;QAC9I,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,oBAAoB,OAAO,CAAC,CAAC,CAAC,8BAA8B,EAAE,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;QACpI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA,mBAAmB,IAAI,CAAC,KAAK,sBAAsB,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ;iBACxF,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,4BAA4B,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO;KACrJ,CAAA;IACH,CAAC;;AA3D+B;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;yDAAkC;AACrD;IAAX,QAAQ,EAAE;;0DAAe;AACG;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;wDAAgB;AACf;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;yDAAiB;AAChB;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;0DAAkB;AAClC;IAAX,QAAQ,EAAE;;sDAAW;AACO;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;+DAAuB;AACvB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;wDAAY;AAEK;IAA3C,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;wDAAgB;AAVhD,uBAAuB;IADnC,aAAa,CAAC,8BAA8B,CAAC;GACjC,uBAAuB,CA6DnC","sourcesContent":["import { LitElement, css, html, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { i18next } from '@operato/i18n'\nimport type { AssistantSession } from '../utils/assistant-session-controller'\nimport { assistantRecentSessions } from '../utils/assistant-request-context.js'\n\n/** Product-neutral controls. The host/controller owns effects; this view never mutates sessions. */\n@customElement('ox-assistant-session-toolbar')\nexport class AssistantSessionToolbar extends LitElement {\n @property({ attribute: false }) sessions: AssistantSession[] = []\n @property() sessionId = ''\n @property({ type: Boolean }) loading = false\n @property({ type: Boolean }) disabled = false\n @property({ type: Boolean }) temporary = false\n @property() error = ''\n @property({ type: Boolean }) historyEnabled = false\n @property({ type: Number }) maxTabs = 5\n /** 좁은 도크의 문맥 헤더 바로 아래에서 쓰는 밀도. 기능은 줄이지 않는다. */\n @property({ type: Boolean, reflect: true }) compact = false\n\n static styles = css`\n :host { display: block; min-width: 0; }\n .controls { display: flex; align-items: center; gap: 6px; }\n button { box-sizing: border-box; height: 28px; font: inherit; font-size: 12px;\n color: var(--secondary-color, #68747c); border: 0; border-radius: 4px;\n background: transparent; cursor: pointer; }\n .tabs { display:flex; flex:1; min-width:0; overflow-x:auto; gap:1px; scrollbar-width:thin; }\n [role=tab] { max-width:160px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;\n padding: 0 8px; border-bottom: 2px solid transparent; border-radius: 3px 3px 0 0; }\n [role=tab]:hover { background: color-mix(in srgb, var(--primary-color, #007f87) 7%, transparent); color: var(--primary-color, #007f87); }\n [aria-selected=true] { color:var(--primary-color, #007f87); border-bottom-color: currentColor; font-weight:600; }\n .session-new { flex: none; padding: 0 7px; font-weight: 500; white-space: nowrap; }\n .session-new:hover, .history:hover { color: var(--primary-color, #007f87); background: color-mix(in srgb, var(--primary-color, #007f87) 7%, transparent); }\n .history { height: 24px; margin: 1px 0 0 4px; padding: 0 5px; font-size: 11px; color: var(--tertiary-color, #88939a); }\n :host([compact]) .controls { gap: 3px; min-height: 24px; }\n :host([compact]) button { height: 24px; font-size: 11px; }\n :host([compact]) [role=tab] { max-width: 132px; padding: 0 6px; border-bottom-width: 1px; }\n :host([compact]) .session-new { padding: 0 5px; }\n :disabled { opacity: .5; cursor: default; }\n :focus-visible { outline: 2px solid var(--primary-color, #007f87); outline-offset: 2px; }\n p { margin: 6px 0 0; font-size: 12px; overflow-wrap: anywhere; }\n [role=alert] { color: var(--error-color, #b42318); }\n `\n\n private emit(name: string, detail?: unknown) {\n this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }))\n }\n\n render() {\n const label = i18next.t('ai-assistant.session.history', { defaultValue: '대화 이력' })\n return html`\n <div class=\"controls\">\n ${this.temporary ? nothing : html`<div class=\"tabs\" role=\"tablist\" aria-label=${label}>\n ${!this.sessionId ? html`<button role=\"tab\" aria-selected=\"true\">${i18next.t('ai-assistant.session.new', { defaultValue: '새 대화' })}</button>` : nothing}\n ${assistantRecentSessions(this.sessions, this.sessionId, this.historyEnabled ? this.maxTabs : this.sessions.length).map((session, index) => html`<button role=\"tab\" aria-selected=${session.id === this.sessionId ? 'true' : 'false'}\n ?disabled=${this.disabled || this.loading} title=${session.name || `${label} ${index + 1}`}\n @click=${() => this.emit('assistant-session-select', { sessionId: session.id })}>${session.name || `${label} ${index + 1}`}</button>`)}\n </div>`}\n <button class=\"session-new\" ?disabled=${this.disabled || this.loading || !!this.error}\n title=${i18next.t('ai-assistant.session.reset-help', { defaultValue: '이전 이력과 작업 내용은 보존하고 새 대화를 시작합니다. 이전 대화는 다음 요청에 포함하지 않습니다.' })}\n @click=${() => this.emit('assistant-session-reset')}>+ ${i18next.t('ai-assistant.session.new', { defaultValue: '새 대화' })}</button>\n </div>\n ${this.historyEnabled ? html`<button class=\"history\" ?disabled=${this.loading || this.disabled}\n @click=${() => this.emit('assistant-session-history')}>${i18next.t('ai-assistant.session.all', { defaultValue: '전체 이력' })}</button>` : nothing}\n ${this.loading ? html`<p role=\"status\">${i18next.t('ai-assistant.session.loading', { defaultValue: '대화 이력을 불러오는 중…' })}</p>` : nothing}\n ${this.error ? html`<p role=\"alert\">${this.error} <button ?disabled=${this.loading || this.disabled}\n @click=${() => this.emit('assistant-session-retry')}>${i18next.t('ai-assistant.session.retry', { defaultValue: '다시 시도' })}</button></p>` : nothing}\n `\n }\n}\n"]}
|
|
@@ -38,7 +38,7 @@ export function buildChatMutationInput(args) {
|
|
|
38
38
|
*/
|
|
39
39
|
export function buildAssistantChatInput(args) {
|
|
40
40
|
if (!args.systemPrompt?.trim()) {
|
|
41
|
-
throw new Error('[ox-
|
|
41
|
+
throw new Error('[ox-assistant-chat] systemPrompt is required when the chat talks to assistantChat');
|
|
42
42
|
}
|
|
43
43
|
return {
|
|
44
44
|
sessionId: args.sessionId ?? null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-input-builder.js","sourceRoot":"","sources":["../../client/components/chat-input-builder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuCH;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAA2B;IAChE,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACjC,QAAQ,EAAE,IAAI,CAAC,OAAO;QACtB,YAAY,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACpC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,qDAAqD;QACrD,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;KAC9E,CAAA;AACH,CAAC;AAqBD;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAA4B;IAClE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"chat-input-builder.js","sourceRoot":"","sources":["../../client/components/chat-input-builder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuCH;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAA2B;IAChE,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACjC,QAAQ,EAAE,IAAI,CAAC,OAAO;QACtB,YAAY,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACpC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,qDAAqD;QACrD,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;KAC9E,CAAA;AACH,CAAC;AAqBD;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAA4B;IAClE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,mFAAmF,CAAC,CAAA;IACtG,CAAC;IAED,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACjC,QAAQ,EAAE,IAAI,CAAC,OAAO;QACtB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,4GAA4G;QAC5G,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvE,CAAA;AACH,CAAC","sourcesContent":["/**\n * boardAIChat GraphQL mutation 의 input 객체 빌더.\n *\n * board-ai-chat.ts (Lit / Material Web 의존) 와 분리 — pure 함수만 두면 jest 의 node\n * 환경에서도 import 가능. 회귀 테스트 (selectedRefids 누락 등) 가 단위 테스트로 잠긴다.\n */\n\nexport interface ChatMutationInputArgs {\n sessionId?: string\n history: Array<{ role: string; content: string }>\n liveBoard: any\n scopes?: string[]\n /** 편집으로 접은 지점(마지막으로 남긴 서버 메시지 id) — 서버가 그 뒤 이력을 에이전트 문맥에서 뺀다. */\n truncateAfterMessageId?: string\n /** 이 대화면이 무엇을 보고 있는지(예: { spaceId }) — 도구가 대상을 알 수 있게. 없으면 모델이 식별자를 되묻는다. */\n hostContext?: any\n /**\n * 이 대화면이 쓸 수 있는 외부 도구 카테고리(registerToolCategory 이름).\n * 미지정 = 등록된 전부(현행 동작). 지정하면 정의와 **실행** 모두 그 범위로 제한된다.\n */\n toolCategories?: string[]\n /** 코어 보드 편집 도구(addComponent·스타일링·씬 조작) 사용 여부. 기본 true. */\n boardTools?: boolean\n /**\n * 첫 턴에 도구 호출을 강제할지 — 라이브 상태를 다루는 대화면에서 켠다.\n * 근거 없이 상태를 단언하는 답(도구를 아예 부르지 않은 답)을 원천에서 막는다.\n */\n requireGroundingTools?: boolean\n knownTypes?: string[]\n categories?: string[]\n componentSchemas?: any\n /**\n * 선택된 컴포넌트의 `refid` 배열 — things-scene universal numeric handle.\n * id (데이터 바인딩 이름) 와는 다른 개념.\n */\n selectedRefids: number[]\n /**\n * popup 에서 명시 선택한 #mention 매핑 — {token, refid}.\n * 사용자 textarea 에는 #token 만 보이고, 정확한 refid 는 이 별도 채널로 server 에 전달.\n * server 가 mention map 으로 LLM 에 정확한 refid 정보 첨부 (사용자 노출 0).\n */\n mentions?: Array<{ token: string; refid: number }>\n}\n\n/**\n * GraphQL mutation 의 input 객체를 빌드.\n *\n * 핵심 보호 — `selectedRefids` 가 항상 포함되어야 한다. 누락이 사용자에게 보고된\n * \"AI 가 선택된 컴포넌트를 모른다\" 의 root cause 였다.\n */\nexport function buildChatMutationInput(args: ChatMutationInputArgs): Record<string, any> {\n return {\n sessionId: args.sessionId ?? null,\n messages: args.history,\n currentBoard: args.liveBoard ?? null,\n scopes: args.scopes,\n ...(args.truncateAfterMessageId ? { truncateAfterMessageId: args.truncateAfterMessageId } : {}),\n ...(args.hostContext ? { hostContext: args.hostContext } : {}),\n /* 미지정은 서버 기본(전부 허용)에 맡긴다 — null 을 보내 굳이 덮어쓰지 않는다. */\n ...(args.toolCategories ? { toolCategories: args.toolCategories } : {}),\n ...(args.boardTools === false ? { boardTools: false } : {}),\n ...(args.requireGroundingTools ? { requireGroundingTools: true } : {}),\n knownTypes: args.knownTypes,\n categories: args.categories,\n componentSchemas: args.componentSchemas,\n selectedRefids: args.selectedRefids,\n ...(args.mentions && args.mentions.length > 0 && { mentions: args.mentions })\n }\n}\n\n/**\n * Input for `assistantChat` — the door for a surface that has no document of its own.\n *\n * Deliberately a separate builder rather than a flag on the one above. The two mutations take\n * different inputs, and folding them together would mean a board field reaching a surface that\n * has no board, or a required field going missing with nothing to notice it. The one thing they\n * share is the conversation, and that is passed the same way.\n */\nexport interface AssistantChatInputArgs {\n sessionId?: string\n history: Array<{ role: string; content: string }>\n /** Who the assistant is on this surface. The host owns this prose; the server appends tool rules. */\n systemPrompt: string\n truncateAfterMessageId?: string\n hostContext?: any\n toolCategories?: string[]\n requireGroundingTools?: boolean\n}\n\n/**\n * Build the neutral input.\n *\n * Refuses an empty system prompt instead of sending one. A conversation with no persona still\n * answers — it just answers as nobody, on a surface that never said what it is — and that reads\n * as a bad model rather than a missing argument.\n */\nexport function buildAssistantChatInput(args: AssistantChatInputArgs): Record<string, any> {\n if (!args.systemPrompt?.trim()) {\n throw new Error('[ox-assistant-chat] systemPrompt is required when the chat talks to assistantChat')\n }\n\n return {\n sessionId: args.sessionId ?? null,\n messages: args.history,\n systemPrompt: args.systemPrompt,\n ...(args.truncateAfterMessageId ? { truncateAfterMessageId: args.truncateAfterMessageId } : {}),\n ...(args.hostContext ? { hostContext: args.hostContext } : {}),\n /* Absent means every registered category; an empty array means none. Both are meant, so pass it through. */\n ...(args.toolCategories ? { toolCategories: args.toolCategories } : {}),\n ...(args.requireGroundingTools ? { requireGroundingTools: true } : {})\n }\n}\n"]}
|
package/dist-client/index.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@things-factory/ai-assistant` — 대화면.
|
|
3
3
|
*
|
|
4
|
-
* `<ox-
|
|
5
|
-
* 박혀 있고, 그것을 같은 커밋에서 함께 바꾸면 무엇이 깨졌는지 못 가린다. 이름은 별도로 옮긴다.
|
|
4
|
+
* 표준 컴포넌트는 `<ox-assistant-chat>` 이다.
|
|
6
5
|
*/
|
|
7
|
-
export * from './components/
|
|
6
|
+
export * from './components/assistant-chat.js';
|
|
8
7
|
export * from './components/chat-defaults.js';
|
|
9
8
|
export * from './components/markdown.js';
|
|
10
9
|
export * from './components/chat-echo-dedup.js';
|
|
@@ -12,3 +11,7 @@ export * from './components/chat-input-builder.js';
|
|
|
12
11
|
export * from './components/mention-popup.js';
|
|
13
12
|
export * from './components/mention-popup-helpers.js';
|
|
14
13
|
export * from './utils/board-edit-patch.js';
|
|
14
|
+
export * from './utils/assistant-session-controller.js';
|
|
15
|
+
export * from './utils/assistant-session-transport.js';
|
|
16
|
+
export * from './components/assistant-session-toolbar.js';
|
|
17
|
+
export * from './utils/assistant-request-context.js';
|
package/dist-client/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@things-factory/ai-assistant` — 대화면.
|
|
3
3
|
*
|
|
4
|
-
* `<ox-
|
|
5
|
-
* 박혀 있고, 그것을 같은 커밋에서 함께 바꾸면 무엇이 깨졌는지 못 가린다. 이름은 별도로 옮긴다.
|
|
4
|
+
* 표준 컴포넌트는 `<ox-assistant-chat>` 이다.
|
|
6
5
|
*/
|
|
7
|
-
export * from './components/
|
|
6
|
+
export * from './components/assistant-chat.js';
|
|
8
7
|
export * from './components/chat-defaults.js';
|
|
9
8
|
export * from './components/markdown.js';
|
|
10
9
|
export * from './components/chat-echo-dedup.js';
|
|
@@ -12,4 +11,8 @@ export * from './components/chat-input-builder.js';
|
|
|
12
11
|
export * from './components/mention-popup.js';
|
|
13
12
|
export * from './components/mention-popup-helpers.js';
|
|
14
13
|
export * from './utils/board-edit-patch.js';
|
|
14
|
+
export * from './utils/assistant-session-controller.js';
|
|
15
|
+
export * from './utils/assistant-session-transport.js';
|
|
16
|
+
export * from './components/assistant-session-toolbar.js';
|
|
17
|
+
export * from './utils/assistant-request-context.js';
|
|
15
18
|
//# sourceMappingURL=index.js.map
|
package/dist-client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,gCAAgC,CAAA;AAC9C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,0BAA0B,CAAA;AACxC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,oCAAoC,CAAA;AAClD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,uCAAuC,CAAA;AACrD,cAAc,6BAA6B,CAAA;AAC3C,cAAc,yCAAyC,CAAA;AACvD,cAAc,wCAAwC,CAAA;AACtD,cAAc,2CAA2C,CAAA;AACzD,cAAc,sCAAsC,CAAA","sourcesContent":["/**\n * `@things-factory/ai-assistant` — 대화면.\n *\n * 표준 컴포넌트는 `<ox-assistant-chat>` 이다.\n */\nexport * from './components/assistant-chat.js'\nexport * from './components/chat-defaults.js'\nexport * from './components/markdown.js'\nexport * from './components/chat-echo-dedup.js'\nexport * from './components/chat-input-builder.js'\nexport * from './components/mention-popup.js'\nexport * from './components/mention-popup-helpers.js'\nexport * from './utils/board-edit-patch.js'\nexport * from './utils/assistant-session-controller.js'\nexport * from './utils/assistant-session-transport.js'\nexport * from './components/assistant-session-toolbar.js'\nexport * from './utils/assistant-request-context.js'\n"]}
|