@things-factory/ai-assistant 10.1.79 → 10.1.87
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 +102 -13
- package/client/components/assistant-dock.ts +206 -18
- package/client/components/assistant-mode.test.ts +16 -2
- package/client/components/assistant-session-manager.ts +58 -26
- package/client/components/assistant-session-toolbar.ts +4 -2
- package/client/components/chat-echo-dedup.ts +4 -0
- package/client/components/proposal-outcome-line.ts +36 -0
- package/client/components/proposal-request.test.ts +52 -0
- package/client/utils/assistant-navigation.test.ts +38 -0
- package/client/utils/assistant-navigation.ts +6 -0
- package/client/utils/assistant-session-controller.ts +2 -0
- package/client/utils/assistant-session-transport.ts +2 -2
- package/client/utils/assistant-surface.ts +21 -0
- package/dist-client/components/assistant-chat.d.ts +15 -0
- package/dist-client/components/assistant-chat.js +106 -13
- package/dist-client/components/assistant-chat.js.map +1 -1
- package/dist-client/components/assistant-dock.d.ts +16 -0
- package/dist-client/components/assistant-dock.js +221 -20
- package/dist-client/components/assistant-dock.js.map +1 -1
- package/dist-client/components/assistant-mode.test.js +14 -2
- package/dist-client/components/assistant-mode.test.js.map +1 -1
- package/dist-client/components/assistant-session-manager.d.ts +4 -0
- package/dist-client/components/assistant-session-manager.js +74 -27
- package/dist-client/components/assistant-session-manager.js.map +1 -1
- package/dist-client/components/assistant-session-toolbar.d.ts +1 -0
- package/dist-client/components/assistant-session-toolbar.js +8 -2
- package/dist-client/components/assistant-session-toolbar.js.map +1 -1
- package/dist-client/components/chat-echo-dedup.d.ts +2 -0
- package/dist-client/components/chat-echo-dedup.js +2 -0
- package/dist-client/components/chat-echo-dedup.js.map +1 -1
- package/dist-client/components/proposal-outcome-line.d.ts +29 -0
- package/dist-client/components/proposal-outcome-line.js +15 -0
- package/dist-client/components/proposal-outcome-line.js.map +1 -1
- package/dist-client/components/proposal-request.test.d.ts +1 -0
- package/dist-client/components/proposal-request.test.js +45 -0
- package/dist-client/components/proposal-request.test.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/assistant-navigation.d.ts +6 -0
- package/dist-client/utils/assistant-navigation.js +9 -0
- package/dist-client/utils/assistant-navigation.js.map +1 -0
- package/dist-client/utils/assistant-navigation.test.d.ts +1 -0
- package/dist-client/utils/assistant-navigation.test.js +37 -0
- package/dist-client/utils/assistant-navigation.test.js.map +1 -0
- package/dist-client/utils/assistant-session-controller.d.ts +2 -0
- package/dist-client/utils/assistant-session-controller.js.map +1 -1
- package/dist-client/utils/assistant-session-transport.js +2 -2
- package/dist-client/utils/assistant-session-transport.js.map +1 -1
- package/dist-client/utils/assistant-surface.d.ts +19 -0
- package/dist-client/utils/assistant-surface.js.map +1 -1
- package/dist-server/service/assistant-chat-resolver.js +1 -0
- package/dist-server/service/assistant-chat-resolver.js.map +1 -1
- package/dist-server/service/chat-message/chat-message.d.ts +10 -0
- package/dist-server/service/chat-message/chat-message.js +29 -0
- package/dist-server/service/chat-message/chat-message.js.map +1 -1
- package/dist-server/service/chat-message/proposal-outcome.d.ts +8 -0
- package/dist-server/service/chat-message/proposal-outcome.js +35 -1
- package/dist-server/service/chat-message/proposal-outcome.js.map +1 -1
- package/dist-server/service/chat-session/chat-session.d.ts +5 -5
- package/dist-server/service/chat-session/chat-session.js.map +1 -1
- package/dist-server/service/chat-session/session-inbox.d.ts +7 -0
- package/dist-server/service/chat-session/session-inbox.js +7 -0
- package/dist-server/service/chat-session/session-inbox.js.map +1 -1
- package/dist-server/service/chat-session/session-visibility.d.ts +8 -5
- package/dist-server/service/chat-session/session-visibility.js +9 -15
- package/dist-server/service/chat-session/session-visibility.js.map +1 -1
- package/dist-server/service/chat-session-resolver.d.ts +1 -0
- package/dist-server/service/chat-session-resolver.js +64 -10
- package/dist-server/service/chat-session-resolver.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/server/service/assistant-chat-resolver.ts +1 -0
- package/server/service/chat-message/chat-message.ts +32 -0
- package/server/service/chat-message/proposal-outcome.test.ts +53 -2
- package/server/service/chat-message/proposal-outcome.ts +33 -0
- package/server/service/chat-session/chat-session.ts +5 -5
- package/server/service/chat-session/session-inbox.ts +7 -0
- package/server/service/chat-session/session-visibility.test.ts +6 -6
- package/server/service/chat-session/session-visibility.ts +9 -15
- package/server/service/chat-session-resolver.ts +72 -13
- package/server/service/session-anchor.test.ts +2 -2
- package/test/assistant-dock.test.ts +8 -3
- package/test/assistant-session-toolbar.test.ts +7 -1
- package/test/open-doors.test.ts +2 -1
- package/test/session-discovery.test.ts +42 -0
- package/translations/en.json +28 -2
- package/translations/ja.json +28 -2
- package/translations/ko.json +28 -2
- package/translations/ms.json +28 -2
- package/translations/zh.json +28 -2
|
@@ -45,7 +45,7 @@ import { buildMentionCandidates, buildCatalogCandidates, buildSlashCandidates, b
|
|
|
45
45
|
*/
|
|
46
46
|
import { chatDefaults } from './chat-defaults.js';
|
|
47
47
|
import { isDifferentConversation, resolveConversation, pickInitialMode } from './assistant-mode.js';
|
|
48
|
-
import { parseProposalOutcomeLine } from './proposal-outcome-line.js';
|
|
48
|
+
import { parseProposalOutcomeLine, parseProposalRequestLine, proposalCardState } from './proposal-outcome-line.js';
|
|
49
49
|
import { availableTriggers } from './chat-triggers.js';
|
|
50
50
|
function formatFileSize(bytes) {
|
|
51
51
|
if (!bytes || bytes < 1024)
|
|
@@ -184,6 +184,7 @@ const CHAT_MESSAGES_QUERY = gql `
|
|
|
184
184
|
content
|
|
185
185
|
relatedPatchId
|
|
186
186
|
toolUsagesJson
|
|
187
|
+
proposalsJson
|
|
187
188
|
groundingWarningsJson
|
|
188
189
|
attachmentsJson
|
|
189
190
|
createdAt
|
|
@@ -205,6 +206,7 @@ const CHAT_MESSAGE_SUBSCRIPTION = gql `
|
|
|
205
206
|
content
|
|
206
207
|
relatedPatchId
|
|
207
208
|
toolUsagesJson
|
|
209
|
+
proposalsJson
|
|
208
210
|
groundingWarningsJson
|
|
209
211
|
attachmentsJson
|
|
210
212
|
createdAt
|
|
@@ -298,6 +300,8 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
298
300
|
/** 안내를 닫았는가 — 한 번 닫으면 이 세션에서는 다시 띄우지 않는다(잔소리 금지). */
|
|
299
301
|
this.foldNoticeDismissed = false;
|
|
300
302
|
this.busy = false;
|
|
303
|
+
/** Host navigation may retain a conversation for reading while its subject is off screen. */
|
|
304
|
+
this.sendBlocked = '';
|
|
301
305
|
/** C4 presence/roster — 세션 참여자 목록. lastSeenAt 으로 online 판정. */
|
|
302
306
|
this.participants = [];
|
|
303
307
|
/** roster online 재평가 트리거 — heartbeat tick 마다 갱신(시간 경과 반영). */
|
|
@@ -559,6 +563,10 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
559
563
|
}
|
|
560
564
|
/** Once per page: the miss is a wiring mistake, and repeating it per candidate buries it. */
|
|
561
565
|
static { this.warnedMissingStagedNotice = false; }
|
|
566
|
+
/** The host may inspect navigation risk without reading or copying the person's input. */
|
|
567
|
+
get navigationState() {
|
|
568
|
+
return { busy: this.busy, draft: !!this.input.trim() || this.attachments.length > 0 };
|
|
569
|
+
}
|
|
562
570
|
/** 현재 로그인 사용자 email — 그룹챗에서 내 메시지 vs 타인 메시지 구분.
|
|
563
571
|
* email 을 쓰는 이유: /auth/profile 이 항상 내려주는 로그인 식별자이고(비어 있지 않다),
|
|
564
572
|
* 내부 관리키(User.id)를 클라이언트 판정에 의존하지 않기 위함. */
|
|
@@ -2555,6 +2563,9 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2555
2563
|
const prevSameSender = !!prev && prev.role === 'user' && prev.senderEmail === line.senderEmail;
|
|
2556
2564
|
const showSenderLabel = isOther && !prevSameSender;
|
|
2557
2565
|
const content = (line.content ?? '').trim();
|
|
2566
|
+
/* A "requested" row is the card's state, shown on the card - as a line it would only be the machine's key. */
|
|
2567
|
+
if (line.role === 'system' && parseProposalRequestLine(content))
|
|
2568
|
+
return nothing;
|
|
2558
2569
|
/*
|
|
2559
2570
|
* 가려진 줄은 어시스턴트의 말이 아니다 — 마크다운으로 그리지 않고, 복사·재실행 같은 손잡이도
|
|
2560
2571
|
* 달지 않는다(ADR-0073 판정 1). 그 손잡이들은 있지도 않은 답을 다룬다.
|
|
@@ -2797,9 +2808,9 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2797
2808
|
`
|
|
2798
2809
|
: nothing}
|
|
2799
2810
|
<button
|
|
2800
|
-
?disabled=${this.busy || (!this.input.trim() && this.attachments.length === 0)}
|
|
2811
|
+
?disabled=${this.busy || !!this.sendBlocked || (!this.input.trim() && this.attachments.length === 0)}
|
|
2801
2812
|
@click=${this.send}
|
|
2802
|
-
title=${i18next.t('ai-assistant.text.send-tooltip')}>
|
|
2813
|
+
title=${this.sendBlocked || i18next.t('ai-assistant.text.send-tooltip')}>
|
|
2803
2814
|
${this.busy
|
|
2804
2815
|
? html `<span class="spinner"></span>`
|
|
2805
2816
|
: html `<md-icon>arrow_upward</md-icon>`}
|
|
@@ -3486,11 +3497,15 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
3486
3497
|
const items = line.proposals ?? [];
|
|
3487
3498
|
if (items.length === 0)
|
|
3488
3499
|
return nothing;
|
|
3500
|
+
/* The card's state is the conversation's rows - read once for every card of this line. */
|
|
3501
|
+
const outcomes = this.proposalOutcomes;
|
|
3502
|
+
const requested = this.proposalRequests;
|
|
3489
3503
|
return html `
|
|
3490
3504
|
<div class="proposals">
|
|
3491
3505
|
${items.map(p => {
|
|
3492
3506
|
const key = this.proposalKey(p);
|
|
3493
|
-
const
|
|
3507
|
+
const card = proposalCardState(key, { outcomes, requested, sentHere: this.sentProposals.has(key) });
|
|
3508
|
+
const sent = card.state !== 'ready';
|
|
3494
3509
|
/*
|
|
3495
3510
|
Follow-up chips are the host's words, never this component's. The fallback that used
|
|
3496
3511
|
to live here named a 3D modeller's operations in hardcoded Korean, and it reached the
|
|
@@ -3511,14 +3526,23 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
3511
3526
|
the one action the card exists for became unreachable while the model kept
|
|
3512
3527
|
telling people to press it.
|
|
3513
3528
|
*/''}
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3529
|
+
${card.state === 'unconfirmed'
|
|
3530
|
+
? html `
|
|
3531
|
+
<span class="preason">${i18next.t('ai-assistant.text.proposal-unconfirmed', { defaultValue: 'sent earlier - no answer received' })}</span>
|
|
3532
|
+
<button class="run" @click=${() => this.checkProposal(p, key)}>
|
|
3533
|
+
${i18next.t('ai-assistant.button.check-proposal', { defaultValue: 'check result' })}
|
|
3534
|
+
</button>
|
|
3535
|
+
`
|
|
3536
|
+
: html `
|
|
3537
|
+
<button
|
|
3538
|
+
class="run"
|
|
3539
|
+
?disabled=${sent}
|
|
3540
|
+
@click=${() => this.executeProposal(p, key)}>
|
|
3541
|
+
${sent
|
|
3542
|
+
? this.proposalStateText(key)
|
|
3543
|
+
: i18next.t('ai-assistant.button.run-proposal', { defaultValue: 'run' })}
|
|
3544
|
+
</button>
|
|
3545
|
+
`}
|
|
3522
3546
|
${ /*
|
|
3523
3547
|
Drawn only where the host has said it staged this candidate, in the host's own
|
|
3524
3548
|
words. Not knowing is not a claim: with no word from the host, nothing is drawn.
|
|
@@ -3646,10 +3670,34 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
3646
3670
|
return `${p?.tool ?? ''}|${p?.command ?? ''}|${p?.instanceId ?? ''}`;
|
|
3647
3671
|
}
|
|
3648
3672
|
}
|
|
3649
|
-
executeProposal(p, key) {
|
|
3673
|
+
async executeProposal(p, key) {
|
|
3674
|
+
if (this.sentProposals.has(key))
|
|
3675
|
+
return;
|
|
3650
3676
|
this.sentProposals = new Set(this.sentProposals).add(key);
|
|
3651
3677
|
this.proposalSentAt.set(key, Date.now());
|
|
3652
3678
|
this._startElapsedTicker();
|
|
3679
|
+
/*
|
|
3680
|
+
* The server records the request first and answers whether this conversation has taken the proposal
|
|
3681
|
+
* already (ADR-0097 Q-D4 ④). Only a granted request reaches the host. A refused one - taken in another
|
|
3682
|
+
* window, or before a refresh - leaves the card to the rows; a failed one leaves it as it was, and the
|
|
3683
|
+
* error is said by the client's own error line. Either way the host is not asked.
|
|
3684
|
+
*/
|
|
3685
|
+
if (this.sessionId) {
|
|
3686
|
+
let granted = false;
|
|
3687
|
+
try {
|
|
3688
|
+
granted = await this.requestProposal(key);
|
|
3689
|
+
}
|
|
3690
|
+
catch {
|
|
3691
|
+
granted = false;
|
|
3692
|
+
}
|
|
3693
|
+
if (!granted) {
|
|
3694
|
+
const left = new Set(this.sentProposals);
|
|
3695
|
+
left.delete(key);
|
|
3696
|
+
this.sentProposals = left;
|
|
3697
|
+
this.proposalSentAt.delete(key);
|
|
3698
|
+
return;
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3653
3701
|
this.dispatchEvent(new CustomEvent('assistant-proposal-execute', {
|
|
3654
3702
|
/*
|
|
3655
3703
|
* `proposalKey` 를 함께 준다 — 호스트가 `reportProposalOutcome` 으로 답할 때 쓰는 이름이다.
|
|
@@ -3660,6 +3708,42 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
3660
3708
|
composed: true
|
|
3661
3709
|
}));
|
|
3662
3710
|
}
|
|
3711
|
+
async requestProposal(key) {
|
|
3712
|
+
const result = await client.mutate({
|
|
3713
|
+
mutation: gql `
|
|
3714
|
+
mutation ($id: String!, $key: String!) {
|
|
3715
|
+
requestProposal(sessionId: $id, proposalKey: $key)
|
|
3716
|
+
}
|
|
3717
|
+
`,
|
|
3718
|
+
variables: { id: this.sessionId, key }
|
|
3719
|
+
});
|
|
3720
|
+
if (result.errors?.length)
|
|
3721
|
+
throw new Error(result.errors[0].message);
|
|
3722
|
+
return result.data?.requestProposal === true;
|
|
3723
|
+
}
|
|
3724
|
+
/**
|
|
3725
|
+
* "Check result" on a card that was requested and never answered: the host is asked what happened
|
|
3726
|
+
* (`AssistantSurface.proposalOutcome`), never to run it again. The dock reports what it learns.
|
|
3727
|
+
*/
|
|
3728
|
+
checkProposal(p, key) {
|
|
3729
|
+
this.dispatchEvent(new CustomEvent('assistant-proposal-check', {
|
|
3730
|
+
detail: { proposal: p, sessionId: this.sessionId, proposalKey: key },
|
|
3731
|
+
bubbles: true,
|
|
3732
|
+
composed: true
|
|
3733
|
+
}));
|
|
3734
|
+
}
|
|
3735
|
+
/** Keys whose run was requested - from the conversation's rows, like the outcomes. */
|
|
3736
|
+
get proposalRequests() {
|
|
3737
|
+
const requested = new Set();
|
|
3738
|
+
for (const line of this.lines) {
|
|
3739
|
+
if (line.role !== 'system')
|
|
3740
|
+
continue;
|
|
3741
|
+
const key = parseProposalRequestLine(line.content ?? '');
|
|
3742
|
+
if (key)
|
|
3743
|
+
requested.add(key);
|
|
3744
|
+
}
|
|
3745
|
+
return requested;
|
|
3746
|
+
}
|
|
3663
3747
|
/**
|
|
3664
3748
|
* 호스트가 무엇을 했는지 — 대화에 남은 `system` 행에서 읽는다(ADR-0070 A12-a 결정 ①).
|
|
3665
3749
|
*
|
|
@@ -3959,6 +4043,10 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
3959
4043
|
}));
|
|
3960
4044
|
}
|
|
3961
4045
|
async send() {
|
|
4046
|
+
if (this.sendBlocked) {
|
|
4047
|
+
this.errorMessage = this.sendBlocked;
|
|
4048
|
+
return;
|
|
4049
|
+
}
|
|
3962
4050
|
const text = this.input.trim();
|
|
3963
4051
|
const attachmentsToSend = [...this.attachments];
|
|
3964
4052
|
if ((!text && attachmentsToSend.length === 0) || this.busy)
|
|
@@ -4022,6 +4110,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
4022
4110
|
}
|
|
4023
4111
|
catch (error) {
|
|
4024
4112
|
this.errorMessage = error instanceof Error ? error.message : String(error);
|
|
4113
|
+
this.attachments = attachmentsToSend;
|
|
4025
4114
|
return;
|
|
4026
4115
|
}
|
|
4027
4116
|
// pickedMentions / pickedUserMentions 에서 현재 텍스트에 실제 #/@ token 으로
|
|
@@ -4375,6 +4464,10 @@ __decorate([
|
|
|
4375
4464
|
state(),
|
|
4376
4465
|
__metadata("design:type", Object)
|
|
4377
4466
|
], OxAssistantChat.prototype, "busy", void 0);
|
|
4467
|
+
__decorate([
|
|
4468
|
+
property(),
|
|
4469
|
+
__metadata("design:type", Object)
|
|
4470
|
+
], OxAssistantChat.prototype, "sendBlocked", void 0);
|
|
4378
4471
|
__decorate([
|
|
4379
4472
|
state(),
|
|
4380
4473
|
__metadata("design:type", String)
|