@yeaft/webchat-agent 1.0.316 → 1.0.317
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/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +13 -13
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/yeaft/work-center/coordinator.js +15 -2
- package/yeaft/work-center/projection.js +10 -3
- package/yeaft/work-center/store.js +64 -5
|
Binary file
|
package/package.json
CHANGED
|
@@ -654,6 +654,10 @@ export class WorkItemCoordinator {
|
|
|
654
654
|
text, recovery, addedAttachments, options, abortController,
|
|
655
655
|
}) {
|
|
656
656
|
let providerTurn = null;
|
|
657
|
+
let candidateSpeaker = null;
|
|
658
|
+
let speaker = (started.detail.messages || []).find(message => (
|
|
659
|
+
message?.turnId === started.turnId && message.role === 'assistant'
|
|
660
|
+
))?.speaker || null;
|
|
657
661
|
try {
|
|
658
662
|
let normalized = null;
|
|
659
663
|
let mutation = null;
|
|
@@ -697,6 +701,10 @@ export class WorkItemCoordinator {
|
|
|
697
701
|
vps,
|
|
698
702
|
priorRuns: started.detail.runs || [],
|
|
699
703
|
});
|
|
704
|
+
candidateSpeaker = {
|
|
705
|
+
id: assignment.vp.id,
|
|
706
|
+
name: assignment.vp.name || assignment.vp.id,
|
|
707
|
+
};
|
|
700
708
|
const coordinatorPolicy = settings?.coordinatorModelPolicy || {
|
|
701
709
|
...(settings?.modelPolicy || {}),
|
|
702
710
|
effort: settings?.actionModelPolicies?.triage?.effort || settings?.modelPolicy?.effort || 'high',
|
|
@@ -736,9 +744,10 @@ export class WorkItemCoordinator {
|
|
|
736
744
|
};
|
|
737
745
|
const claim = started.fence.claim;
|
|
738
746
|
providerTurn = this.store.prepareCoordinatorProviderTurn(
|
|
739
|
-
started.detail.id, started.turnId, attemptCount, requestBody, claim,
|
|
747
|
+
started.detail.id, started.turnId, attemptCount, requestBody, claim, candidateSpeaker,
|
|
740
748
|
);
|
|
741
749
|
if (!providerTurn) return started.detail;
|
|
750
|
+
speaker = providerTurn.speaker;
|
|
742
751
|
if (providerTurn.status === 'unknown') {
|
|
743
752
|
throw new Error('Coordinator provider dispatch outcome is unknown and requires review');
|
|
744
753
|
}
|
|
@@ -844,6 +853,7 @@ export class WorkItemCoordinator {
|
|
|
844
853
|
}
|
|
845
854
|
const detail = this.store.completeCoordinatorTurn(started.turnId, {
|
|
846
855
|
reply: normalized.reply,
|
|
856
|
+
speaker,
|
|
847
857
|
decision: normalized.decision,
|
|
848
858
|
mutation,
|
|
849
859
|
attemptCount,
|
|
@@ -855,7 +865,10 @@ export class WorkItemCoordinator {
|
|
|
855
865
|
if (providerTurn?.status === 'responded') {
|
|
856
866
|
this.store.rejectCoordinatorProviderTurn(providerTurn.id, error, started.fence.claim);
|
|
857
867
|
}
|
|
858
|
-
const detail = this.store.failCoordinatorTurn(started.turnId, error,
|
|
868
|
+
const detail = this.store.failCoordinatorTurn(started.turnId, error, {
|
|
869
|
+
...started.fence,
|
|
870
|
+
speaker,
|
|
871
|
+
});
|
|
859
872
|
if (detail) {
|
|
860
873
|
options.onUpdate?.('coordinator.turn_failed', detail);
|
|
861
874
|
return detail;
|
|
@@ -19,6 +19,8 @@ const MAX_ACTION_REQUEST_NAME_BYTES = 512;
|
|
|
19
19
|
const MAX_ACTION_REQUEST_MODEL_BYTES = 1_024;
|
|
20
20
|
const MAX_ACTION_REQUEST_STOP_REASON_BYTES = 256;
|
|
21
21
|
const MAX_ACTION_REQUEST_METADATA_BYTES = 4 * 1_024;
|
|
22
|
+
const MAX_SPEAKER_ID_BYTES = 256;
|
|
23
|
+
const MAX_SPEAKER_NAME_BYTES = 512;
|
|
22
24
|
const MAX_HISTORICAL_BRIEF_CHARS = 256;
|
|
23
25
|
const MAX_CURRENT_BRIEF_BYTES = 8 * 1024;
|
|
24
26
|
export const MAX_WORK_ITEM_BROWSER_DTO_BYTES = 512 * 1024;
|
|
@@ -207,9 +209,12 @@ function conversationRuns(action, runs) {
|
|
|
207
209
|
|
|
208
210
|
function projectVpSpeaker(snapshot) {
|
|
209
211
|
if (!snapshot || typeof snapshot !== 'object') return null;
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
212
|
+
const sourceId = typeof snapshot.id === 'string' ? snapshot.id.trim() : '';
|
|
213
|
+
if (!sourceId) return null;
|
|
214
|
+
const id = truncateUtf8(sourceId, MAX_SPEAKER_ID_BYTES);
|
|
215
|
+
const sourceName = typeof snapshot.name === 'string' ? snapshot.name.trim() : '';
|
|
216
|
+
const name = truncateUtf8(sourceName || id, MAX_SPEAKER_NAME_BYTES) || id;
|
|
217
|
+
return { id, name };
|
|
213
218
|
}
|
|
214
219
|
|
|
215
220
|
function compareEventIds(leftId, rightId) {
|
|
@@ -896,6 +901,8 @@ export function projectWorkItemDetail(detail, options = {}) {
|
|
|
896
901
|
id: String(message.id || ''),
|
|
897
902
|
turnId: String(message.turnId || message.id || ''),
|
|
898
903
|
role: message.role === 'assistant' ? 'assistant' : message.role === 'legacy_instruction' ? 'legacy_instruction' : 'user',
|
|
904
|
+
...(message.role === 'assistant' && projectVpSpeaker(message.speaker)
|
|
905
|
+
? { speaker: projectVpSpeaker(message.speaker) } : {}),
|
|
899
906
|
text: truncateUtf8(message.text || '', MAX_ACTION_MESSAGE_CHARS),
|
|
900
907
|
attachments: projectAttachments(message.attachments),
|
|
901
908
|
status: ['thinking', 'completed', 'failed'].includes(message.status) ? message.status : 'completed',
|
|
@@ -1361,7 +1361,14 @@ export class WorkItemStore {
|
|
|
1361
1361
|
WHERE status = 'claimed' AND lease_expires_at <= ?`).run(now, now).changes);
|
|
1362
1362
|
}
|
|
1363
1363
|
|
|
1364
|
-
prepareCoordinatorProviderTurn(
|
|
1364
|
+
prepareCoordinatorProviderTurn(
|
|
1365
|
+
workItemId,
|
|
1366
|
+
coordinatorTurnId,
|
|
1367
|
+
attemptNumber,
|
|
1368
|
+
requestBody,
|
|
1369
|
+
claim = {},
|
|
1370
|
+
speaker = null,
|
|
1371
|
+
) {
|
|
1365
1372
|
return withTransaction(this.db, () => {
|
|
1366
1373
|
const mailbox = this.#activeCoordinatorMailboxClaim(coordinatorTurnId, claim);
|
|
1367
1374
|
if (!mailbox || mailbox.work_item_id !== workItemId) return null;
|
|
@@ -1374,8 +1381,14 @@ export class WorkItemStore {
|
|
|
1374
1381
|
}
|
|
1375
1382
|
if (existing.claim_owner !== claim.ownerBootId
|
|
1376
1383
|
|| Number(existing.claim_epoch) !== Number(claim.claimEpoch)) return null;
|
|
1377
|
-
|
|
1384
|
+
const persistedSpeaker = this.#persistCoordinatorSpeaker(
|
|
1385
|
+
workItemId, coordinatorTurnId, null,
|
|
1386
|
+
);
|
|
1387
|
+
return { ...this.#mapCoordinatorProviderTurn(existing), speaker: persistedSpeaker };
|
|
1378
1388
|
}
|
|
1389
|
+
const persistedSpeaker = this.#persistCoordinatorSpeaker(
|
|
1390
|
+
workItemId, coordinatorTurnId, speaker,
|
|
1391
|
+
);
|
|
1379
1392
|
const now = this.now();
|
|
1380
1393
|
const id = durableId('coordinator-provider-turn');
|
|
1381
1394
|
this.db.prepare(`INSERT INTO coordinator_provider_turns
|
|
@@ -1385,10 +1398,49 @@ export class WorkItemStore {
|
|
|
1385
1398
|
id, workItemId, coordinatorTurnId, attemptNumber, stringify(requestBody), requestHash,
|
|
1386
1399
|
claim.ownerBootId, claim.claimEpoch, now, now,
|
|
1387
1400
|
);
|
|
1388
|
-
return this.getCoordinatorProviderTurn(id);
|
|
1401
|
+
return { ...this.getCoordinatorProviderTurn(id), speaker: persistedSpeaker };
|
|
1389
1402
|
});
|
|
1390
1403
|
}
|
|
1391
1404
|
|
|
1405
|
+
#persistCoordinatorSpeaker(workItemId, coordinatorTurnId, speaker) {
|
|
1406
|
+
const workItem = this.getWorkItem(workItemId);
|
|
1407
|
+
if (!workItem) return null;
|
|
1408
|
+
const messages = [...(workItem.messages || [])];
|
|
1409
|
+
const index = messages.findIndex(message => (
|
|
1410
|
+
message?.turnId === coordinatorTurnId
|
|
1411
|
+
&& message.role === 'assistant'
|
|
1412
|
+
&& message.status === 'thinking'
|
|
1413
|
+
));
|
|
1414
|
+
if (index < 0) return null;
|
|
1415
|
+
const persistedSpeaker = messages[index].speaker;
|
|
1416
|
+
const persistedId = typeof persistedSpeaker?.id === 'string' ? persistedSpeaker.id.trim() : '';
|
|
1417
|
+
const persistedName = typeof persistedSpeaker?.name === 'string'
|
|
1418
|
+
? persistedSpeaker.name.trim()
|
|
1419
|
+
: '';
|
|
1420
|
+
if (persistedId || persistedName) {
|
|
1421
|
+
return { id: persistedId || null, name: persistedName || persistedId };
|
|
1422
|
+
}
|
|
1423
|
+
const id = typeof speaker?.id === 'string' ? speaker.id.trim() : '';
|
|
1424
|
+
const name = typeof speaker?.name === 'string' ? speaker.name.trim() : '';
|
|
1425
|
+
if (!id && !name) return null;
|
|
1426
|
+
if (!id || !name) throw new Error('Coordinator speaker identity is incomplete');
|
|
1427
|
+
const persisted = { id, name };
|
|
1428
|
+
messages[index] = { ...messages[index], speaker: persisted };
|
|
1429
|
+
const changed = this.db.prepare(`UPDATE work_items SET messages = ?
|
|
1430
|
+
WHERE id = ? AND coordinator_revision = ?`).run(
|
|
1431
|
+
stringify(messages), workItemId, workItem.coordinatorRevision,
|
|
1432
|
+
);
|
|
1433
|
+
if (Number(changed.changes) !== 1) {
|
|
1434
|
+
throw new Error('Coordinator speaker lost its revision fence');
|
|
1435
|
+
}
|
|
1436
|
+
this.#appendConversationEntry(
|
|
1437
|
+
workItemId,
|
|
1438
|
+
messages[index],
|
|
1439
|
+
`coordinator:turn:${coordinatorTurnId}:assistant`,
|
|
1440
|
+
);
|
|
1441
|
+
return persisted;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1392
1444
|
#activeCoordinatorMailboxClaim(coordinatorTurnId, claim = {}) {
|
|
1393
1445
|
if (!claim.mailboxId || !claim.ownerBootId || !Number.isInteger(Number(claim.claimEpoch))) return null;
|
|
1394
1446
|
return this.db.prepare(`SELECT * FROM coordinator_mailbox_entries WHERE id = ? AND status = 'claimed'
|
|
@@ -3363,7 +3415,11 @@ export class WorkItemStore {
|
|
|
3363
3415
|
|
|
3364
3416
|
const graphState = ['answer'].includes(decision.kind) ? null : this.#graphWorkItemState(workItem.id);
|
|
3365
3417
|
messages[assistantIndex] = {
|
|
3366
|
-
...messages[assistantIndex],
|
|
3418
|
+
...messages[assistantIndex],
|
|
3419
|
+
text: result.reply,
|
|
3420
|
+
status: 'completed',
|
|
3421
|
+
updatedAt: now,
|
|
3422
|
+
...(result.speaker ? { speaker: result.speaker } : {}),
|
|
3367
3423
|
decision: {
|
|
3368
3424
|
kind: decision.kind,
|
|
3369
3425
|
reason: decision.reason,
|
|
@@ -3423,7 +3479,10 @@ export class WorkItemStore {
|
|
|
3423
3479
|
if (index < 0) return null;
|
|
3424
3480
|
const now = this.now();
|
|
3425
3481
|
messages[index] = {
|
|
3426
|
-
...messages[index],
|
|
3482
|
+
...messages[index],
|
|
3483
|
+
status: 'failed',
|
|
3484
|
+
updatedAt: now,
|
|
3485
|
+
...(expected.speaker ? { speaker: expected.speaker } : {}),
|
|
3427
3486
|
error: String(error?.message || error || 'Coordinator failed').slice(0, 8_000),
|
|
3428
3487
|
};
|
|
3429
3488
|
this.#appendConversationEntry(
|