@markusylisiurunen/tau 0.3.42 → 0.3.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -11
- package/dist/core/agent/agent_runtime.js +55 -52
- package/dist/core/agent/agent_runtime.js.map +1 -1
- package/dist/core/config/builtin_themes.js +62 -155
- package/dist/core/config/builtin_themes.js.map +1 -1
- package/dist/core/config/content_loader.js +1 -63
- package/dist/core/config/content_loader.js.map +1 -1
- package/dist/core/runtime/chat_runtime.js +4 -4
- package/dist/core/session/runner.js +21 -14
- package/dist/core/session/runner.js.map +1 -1
- package/dist/core/static/code_mode/capabilities.mjs +25 -0
- package/dist/core/static/code_mode/history/documentation.md +2 -0
- package/dist/core/static/code_mode/history/sandbox_runner.mjs +4 -1
- package/dist/core/static/code_mode/nook/documentation.md +2 -0
- package/dist/core/static/code_mode/nook/sandbox_runner.mjs +4 -1
- package/dist/core/static/code_mode/web/documentation.md +2 -0
- package/dist/core/static/code_mode/web/sandbox_runner.mjs +4 -1
- package/dist/core/subagents/agent_supervisor.js +4 -4
- package/dist/core/subagents/agent_supervisor.js.map +1 -1
- package/dist/core/subagents/registry.js +5 -9
- package/dist/core/subagents/registry.js.map +1 -1
- package/dist/core/subagents/types.js.map +1 -1
- package/dist/core/telegram/adapter.js +0 -23
- package/dist/core/telegram/adapter.js.map +1 -1
- package/dist/core/telegram/session_manager.js +34 -7
- package/dist/core/telegram/session_manager.js.map +1 -1
- package/dist/core/tools/bash.js +9 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/catalog.js +2 -2
- package/dist/core/tools/code_mode.js +1 -0
- package/dist/core/tools/code_mode.js.map +1 -1
- package/dist/core/tools/spawn_agent.js +30 -40
- package/dist/core/tools/spawn_agent.js.map +1 -1
- package/dist/core/utils/auto_retry.js +1 -1
- package/dist/core/utils/auto_retry.js.map +1 -1
- package/dist/core/version.js +1 -1
- package/dist/host/execution_runtime.js +8 -18
- package/dist/host/execution_runtime.js.map +1 -1
- package/dist/host/local_session_host.js +446 -216
- package/dist/host/local_session_host.js.map +1 -1
- package/dist/host/session_host.js.map +1 -1
- package/dist/host/session_protocol_handler.js +3 -8
- package/dist/host/session_protocol_handler.js.map +1 -1
- package/dist/protocol/index.d.ts +2 -2
- package/dist/protocol/index.js +1 -1
- package/dist/protocol/index.js.map +1 -1
- package/dist/protocol/session_protocol.d.ts +127 -40
- package/dist/protocol/session_protocol.js +431 -98
- package/dist/protocol/session_protocol.js.map +1 -1
- package/dist/sdk/index.d.ts +4 -3
- package/dist/sdk/index.js +9 -1
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/types.d.ts +2 -1
- package/dist/store/session_snapshot_migrations.js +331 -4
- package/dist/store/session_snapshot_migrations.js.map +1 -1
- package/dist/tui/chat_controller/assistant_clipboard.js +12 -8
- package/dist/tui/chat_controller/assistant_clipboard.js.map +1 -1
- package/dist/tui/chat_controller/diff_review_service.js +13 -9
- package/dist/tui/chat_controller/diff_review_service.js.map +1 -1
- package/dist/tui/chat_controller/history_message_model.js +3 -3
- package/dist/tui/chat_controller/history_message_model.js.map +1 -1
- package/dist/tui/chat_view.js +35 -35
- package/dist/tui/chat_view.js.map +1 -1
- package/dist/tui/session_chat_app.js +1 -3
- package/dist/tui/session_chat_app.js.map +1 -1
- package/dist/tui/session_chat_controller.js +373 -239
- package/dist/tui/session_chat_controller.js.map +1 -1
- package/dist/tui/speech_playback.js +4 -4
- package/dist/tui/speech_playback.js.map +1 -1
- package/dist/tui/ui/assistant_message.js +1 -15
- package/dist/tui/ui/assistant_message.js.map +1 -1
- package/dist/tui/ui/chat_message_model.js +26 -7
- package/dist/tui/ui/chat_message_model.js.map +1 -1
- package/dist/tui/ui/footer.js +100 -66
- package/dist/tui/ui/footer.js.map +1 -1
- package/dist/tui/ui/theme/palette.js +10 -16
- package/dist/tui/ui/theme/palette.js.map +1 -1
- package/dist/tui/ui/theme/theme.js.map +1 -1
- package/dist/tui/ui/tool_card.js +1 -2
- package/dist/tui/ui/tool_card.js.map +1 -1
- package/dist/tui/ui/transcript_notice.js +44 -0
- package/dist/tui/ui/transcript_notice.js.map +1 -0
- package/dist/tui/ui/transcript_text.js +14 -0
- package/dist/tui/ui/transcript_text.js.map +1 -0
- package/package.json +5 -7
- package/dist/sdk/turn_outcome.d.ts +0 -2
- package/dist/sdk/turn_outcome.js +0 -4
- package/dist/sdk/turn_outcome.js.map +0 -1
- package/dist/tui/ui/system_message.js +0 -22
- package/dist/tui/ui/system_message.js.map +0 -1
|
@@ -9,11 +9,11 @@ import { SUBAGENT_ACTIVITY_FACET_KIND } from "../core/subagents/types.js";
|
|
|
9
9
|
import { buildToolRunPresentation, TOOL_UI_FACET_VERSION } from "../core/tools/presentation.js";
|
|
10
10
|
import { appendUsageLogEntry, getUsageCostTotal, getUsageTotals, } from "../core/usage/logs.js";
|
|
11
11
|
import { filterProjectPathAutocompleteEntries, loadProjectPathAutocompleteEntriesWithBackend, } from "../core/utils/project_files.js";
|
|
12
|
-
import {
|
|
13
|
-
import { applySessionProtocolDelta, createSessionProtocolDeltaMessage, createSessionProtocolEphemeralMessage, } from "../protocol/session_protocol.js";
|
|
14
|
-
import {
|
|
12
|
+
import { hasGoalTurnMetadata } from "../core/utils/user_metadata.js";
|
|
13
|
+
import { applySessionProtocolDelta, createSessionProtocolDeltaMessage, createSessionProtocolEphemeralMessage, projectSessionProtocolNoticePresentation, } from "../protocol/session_protocol.js";
|
|
14
|
+
import { LEGACY_SESSION_MODEL_CONTEXT_KEY } from "../store/session_snapshot_migrations.js";
|
|
15
15
|
import { ClientToolBroker } from "./client_tool_broker.js";
|
|
16
|
-
import {
|
|
16
|
+
import { createExecutionEnvironmentSubagentPromptResolver } from "./execution_runtime.js";
|
|
17
17
|
import { HostedEphemeralAgentSession } from "./hosted_ephemeral_agent_session.js";
|
|
18
18
|
import { SessionExecBusyError, SessionRetryUnavailableError, } from "./session_host.js";
|
|
19
19
|
const PATH_AUTOCOMPLETE_CACHE_TTL_MS = 5_000;
|
|
@@ -76,7 +76,7 @@ export class LocalSessionHost {
|
|
|
76
76
|
const agentRecovery = hostedSession.runtime.restoreState({
|
|
77
77
|
agentId: recovered.snapshot.sessionId,
|
|
78
78
|
revision: recovered.snapshot.agentState.revision,
|
|
79
|
-
|
|
79
|
+
modelContextKey: recovered.snapshot.agentState.modelContextKey,
|
|
80
80
|
historyEntries: recovered.snapshot.messages.flatMap((entry) => entry.modelVisible && isCoreMessage(entry.message)
|
|
81
81
|
? [{ id: entry.id, message: entry.message }]
|
|
82
82
|
: []),
|
|
@@ -124,7 +124,7 @@ export class LocalSessionHost {
|
|
|
124
124
|
backend: executionEnvironment.getToolExecutionBackend(),
|
|
125
125
|
clientTools: (sessionId) => this.clientToolBroker.getToolDefinitions(sessionId),
|
|
126
126
|
modelResolver: bootstrap.modelResolver,
|
|
127
|
-
|
|
127
|
+
resolveSubagentPrompts: createExecutionEnvironmentSubagentPromptResolver({
|
|
128
128
|
executionEnvironment,
|
|
129
129
|
includeAgentContext: this.sessionOptions.includeAgentContext,
|
|
130
130
|
now: this.sessionOptions.environment.now,
|
|
@@ -344,6 +344,9 @@ export class LocalSessionHost {
|
|
|
344
344
|
return false;
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
|
+
function normalizeDeltaCause(cause) {
|
|
348
|
+
return typeof cause === "string" ? { type: cause } : cause;
|
|
349
|
+
}
|
|
347
350
|
class LocalHostedSessionHandle {
|
|
348
351
|
runtime;
|
|
349
352
|
promptBootstrap;
|
|
@@ -364,11 +367,11 @@ class LocalHostedSessionHandle {
|
|
|
364
367
|
persistedSnapshot;
|
|
365
368
|
draftAssistantMessage;
|
|
366
369
|
messageStates = new Map();
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
timelineExtras = [];
|
|
370
|
+
timeline = { epoch: 1, sequence: 0, items: [] };
|
|
371
|
+
turns = new Map();
|
|
372
|
+
pendingAcceptedTurnHistoryEntryIds = new Set();
|
|
371
373
|
tools = new Map();
|
|
374
|
+
operations = new Map();
|
|
372
375
|
agents = new Map();
|
|
373
376
|
agentCostTotals = new Map();
|
|
374
377
|
facets = new Map();
|
|
@@ -484,19 +487,14 @@ class LocalHostedSessionHandle {
|
|
|
484
487
|
this.pendingGoalCommit = goal;
|
|
485
488
|
});
|
|
486
489
|
try {
|
|
487
|
-
const { userHistoryEntryId } = await this.
|
|
490
|
+
const { userHistoryEntryId } = await this.acceptTurn({
|
|
488
491
|
text: prependGoalPolicy(goal.objective, goal),
|
|
489
492
|
});
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
return {
|
|
497
|
-
userHistoryEntryId,
|
|
498
|
-
turn: await this.runTurnNow(logicalTurn, userHistoryEntryId),
|
|
499
|
-
};
|
|
493
|
+
const turn = logicalTurn.cancellationRequested
|
|
494
|
+
? await this.cancelLogicalTurn()
|
|
495
|
+
: await this.runTurnNow(logicalTurn, userHistoryEntryId);
|
|
496
|
+
await this.settleTurn(userHistoryEntryId, turn);
|
|
497
|
+
return { userHistoryEntryId, turn };
|
|
500
498
|
}
|
|
501
499
|
catch (error) {
|
|
502
500
|
if (this.pendingGoalCommit === goal) {
|
|
@@ -525,13 +523,14 @@ class LocalHostedSessionHandle {
|
|
|
525
523
|
await this.blockActiveGoal();
|
|
526
524
|
return { turn: abortedTurnOutcome() };
|
|
527
525
|
}
|
|
528
|
-
const { userHistoryEntryId } = await this.
|
|
526
|
+
const { userHistoryEntryId } = await this.acceptTurn({
|
|
529
527
|
text: buildGoalContinuationText(goal),
|
|
530
528
|
});
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
529
|
+
const turn = logicalTurn.cancellationRequested
|
|
530
|
+
? await this.cancelLogicalTurn()
|
|
531
|
+
: await this.runTurnNow(logicalTurn, userHistoryEntryId);
|
|
532
|
+
await this.settleTurn(userHistoryEntryId, turn);
|
|
533
|
+
return { turn };
|
|
535
534
|
}
|
|
536
535
|
catch (error) {
|
|
537
536
|
await this.blockActiveGoal().catch(() => undefined);
|
|
@@ -549,15 +548,83 @@ class LocalHostedSessionHandle {
|
|
|
549
548
|
}
|
|
550
549
|
async record(options) {
|
|
551
550
|
this.assertActive();
|
|
552
|
-
const
|
|
551
|
+
const historyEntryId = options.historyEntryId === undefined
|
|
552
|
+
? undefined
|
|
553
|
+
: normalizeExplicitHistoryEntryId(options.historyEntryId);
|
|
554
|
+
if (historyEntryId !== undefined &&
|
|
555
|
+
(this.turns.has(historyEntryId) ||
|
|
556
|
+
this.pendingAcceptedTurnHistoryEntryIds.has(historyEntryId))) {
|
|
557
|
+
throw new Error(`history entry id '${historyEntryId}' belongs to an accepted turn`);
|
|
558
|
+
}
|
|
559
|
+
const userHistoryEntryId = await this.session.commitUserText(options.text, historyEntryId === undefined ? undefined : { historyEntryId });
|
|
553
560
|
return {
|
|
554
561
|
snapshot: await this.snapshot(),
|
|
555
562
|
userHistoryEntryId,
|
|
556
563
|
};
|
|
557
564
|
}
|
|
565
|
+
async acceptTurn(options) {
|
|
566
|
+
this.assertActive();
|
|
567
|
+
const userHistoryEntryId = this.createTurnHistoryEntryId(options.historyEntryId);
|
|
568
|
+
this.pendingAcceptedTurnHistoryEntryIds.add(userHistoryEntryId);
|
|
569
|
+
try {
|
|
570
|
+
await this.session.commitUserText(options.text, { historyEntryId: userHistoryEntryId });
|
|
571
|
+
return { snapshot: await this.snapshot(), userHistoryEntryId };
|
|
572
|
+
}
|
|
573
|
+
finally {
|
|
574
|
+
this.pendingAcceptedTurnHistoryEntryIds.delete(userHistoryEntryId);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
createTurnHistoryEntryId(preferredId) {
|
|
578
|
+
if (preferredId !== undefined) {
|
|
579
|
+
const normalizedId = normalizeExplicitHistoryEntryId(preferredId);
|
|
580
|
+
if (this.turns.has(normalizedId) ||
|
|
581
|
+
this.pendingAcceptedTurnHistoryEntryIds.has(normalizedId)) {
|
|
582
|
+
throw new Error(`turn '${normalizedId}' was already accepted`);
|
|
583
|
+
}
|
|
584
|
+
return normalizedId;
|
|
585
|
+
}
|
|
586
|
+
let id = `history-${randomUUID()}`;
|
|
587
|
+
const historyEntryIds = new Set(this.session.rawHistoryEntries.map((entry) => entry.id));
|
|
588
|
+
while (this.turns.has(id) ||
|
|
589
|
+
this.pendingAcceptedTurnHistoryEntryIds.has(id) ||
|
|
590
|
+
historyEntryIds.has(id)) {
|
|
591
|
+
id = `history-${randomUUID()}`;
|
|
592
|
+
}
|
|
593
|
+
return id;
|
|
594
|
+
}
|
|
595
|
+
async runAcceptedTurn(userHistoryEntryId) {
|
|
596
|
+
const acceptedTurn = this.turns.get(userHistoryEntryId);
|
|
597
|
+
if (!acceptedTurn)
|
|
598
|
+
throw new Error(`turn '${userHistoryEntryId}' was not accepted`);
|
|
599
|
+
if (acceptedTurn.state === "settled") {
|
|
600
|
+
throw new Error(`turn '${userHistoryEntryId}' was already settled`);
|
|
601
|
+
}
|
|
602
|
+
const turn = await this.runLogicalTurn((logicalTurn) => this.runTurnNow(logicalTurn, userHistoryEntryId));
|
|
603
|
+
await this.settleTurn(userHistoryEntryId, turn);
|
|
604
|
+
return { userHistoryEntryId, turn };
|
|
605
|
+
}
|
|
558
606
|
async runTurn() {
|
|
559
607
|
return await this.runLogicalTurn((logicalTurn) => this.runTurnNow(logicalTurn));
|
|
560
608
|
}
|
|
609
|
+
async settleTurn(userHistoryEntryId, outcome) {
|
|
610
|
+
await this.enqueueMutation(async () => {
|
|
611
|
+
const existing = this.turns.get(userHistoryEntryId);
|
|
612
|
+
if (!existing) {
|
|
613
|
+
throw new Error(`turn '${userHistoryEntryId}' was not accepted`);
|
|
614
|
+
}
|
|
615
|
+
if (existing.state === "settled") {
|
|
616
|
+
if (isDeepStrictEqual(existing.outcome, outcome))
|
|
617
|
+
return;
|
|
618
|
+
throw new Error(`turn '${userHistoryEntryId}' was already settled`);
|
|
619
|
+
}
|
|
620
|
+
const turn = { userHistoryEntryId, state: "settled", outcome };
|
|
621
|
+
this.turns.set(userHistoryEntryId, turn);
|
|
622
|
+
const delta = await this.commitSnapshotPatch("assistant-message", [
|
|
623
|
+
{ type: "turn.set", turn },
|
|
624
|
+
]);
|
|
625
|
+
this.emitDelta(delta);
|
|
626
|
+
});
|
|
627
|
+
}
|
|
561
628
|
async retryTurn() {
|
|
562
629
|
const userMessage = this.session.rawHistoryEntries.findLast((entry) => entry.message.role === "user");
|
|
563
630
|
if (userMessage && hasGoalTurnMetadata(userMessage.message)) {
|
|
@@ -602,7 +669,7 @@ class LocalHostedSessionHandle {
|
|
|
602
669
|
let goalRootHistoryEntryId = this.goal?.status === "active" ? rootUserMessage.id : undefined;
|
|
603
670
|
while (true) {
|
|
604
671
|
if (logicalTurn.cancellationRequested) {
|
|
605
|
-
return await this.cancelLogicalTurn(
|
|
672
|
+
return await this.cancelLogicalTurn();
|
|
606
673
|
}
|
|
607
674
|
const committedSteering = [];
|
|
608
675
|
try {
|
|
@@ -610,11 +677,7 @@ class LocalHostedSessionHandle {
|
|
|
610
677
|
committedSteering.push(await this.commitBufferedLogicalSteering(logicalTurn));
|
|
611
678
|
}
|
|
612
679
|
if (logicalTurn.cancellationRequested) {
|
|
613
|
-
return await this.cancelLogicalTurn(
|
|
614
|
-
}
|
|
615
|
-
const userMessage = this.session.rawHistoryEntries.findLast((entry) => entry.message.role === "user");
|
|
616
|
-
if (!userMessage) {
|
|
617
|
-
throw new Error("cannot run a turn without a user message");
|
|
680
|
+
return await this.cancelLogicalTurn(committedSteering);
|
|
618
681
|
}
|
|
619
682
|
const result = await this.runtime.runTurn();
|
|
620
683
|
const terminalResult = result.terminalResult;
|
|
@@ -624,24 +687,17 @@ class LocalHostedSessionHandle {
|
|
|
624
687
|
const initialOutcome = turnOutcomeFromResult(result, result.finalMessage);
|
|
625
688
|
const terminalOutcome = turnOutcomeFromResult(terminalResult, terminalResult.finalMessage);
|
|
626
689
|
await this.enqueueMutation(async () => {
|
|
627
|
-
this.turnOutcomes.set(userMessage.id, initialOutcome);
|
|
628
|
-
for (const steering of committedSteering) {
|
|
629
|
-
this.turnOutcomes.set(steering.historyEntryId, initialOutcome);
|
|
630
|
-
}
|
|
631
690
|
if (this.goal?.status === "active") {
|
|
632
691
|
goalRootHistoryEntryId ??= rootUserMessage.id;
|
|
633
692
|
}
|
|
634
|
-
if (goalRootHistoryEntryId) {
|
|
635
|
-
this.turnOutcomes.set(goalRootHistoryEntryId, terminalOutcome);
|
|
636
|
-
}
|
|
637
693
|
if (terminalOutcome.status !== "completed" && this.goal?.status === "active") {
|
|
638
694
|
this.goal = { ...this.goal, status: "blocked" };
|
|
639
695
|
}
|
|
640
696
|
await this.emitSnapshotResetIfChanged("assistant-message");
|
|
641
697
|
});
|
|
642
|
-
this.resolveCommittedLogicalSteering(committedSteering, initialOutcome);
|
|
698
|
+
await this.resolveCommittedLogicalSteering(committedSteering, initialOutcome);
|
|
643
699
|
if (logicalTurn.cancellationRequested) {
|
|
644
|
-
return await this.cancelLogicalTurn(
|
|
700
|
+
return await this.cancelLogicalTurn();
|
|
645
701
|
}
|
|
646
702
|
if (terminalOutcome.status !== "completed" || this.goal?.status !== "active") {
|
|
647
703
|
return goalRootHistoryEntryId ? terminalOutcome : initialOutcome;
|
|
@@ -652,9 +708,15 @@ class LocalHostedSessionHandle {
|
|
|
652
708
|
await this.session.commitUserText(buildGoalContinuationText(this.goal));
|
|
653
709
|
}
|
|
654
710
|
catch (error) {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
711
|
+
try {
|
|
712
|
+
const outcome = await this.cleanupFailedTurn(rootUserMessage.id, error);
|
|
713
|
+
await this.resolveCommittedLogicalSteering(committedSteering, outcome);
|
|
714
|
+
return outcome;
|
|
715
|
+
}
|
|
716
|
+
catch {
|
|
717
|
+
this.rejectCommittedLogicalSteering(committedSteering, error);
|
|
718
|
+
throw error;
|
|
719
|
+
}
|
|
658
720
|
}
|
|
659
721
|
}
|
|
660
722
|
}
|
|
@@ -706,11 +768,8 @@ class LocalHostedSessionHandle {
|
|
|
706
768
|
})),
|
|
707
769
|
result: submission.result.then(async (association) => {
|
|
708
770
|
const turn = turnOutcomeFromResult(association.result, association.result.finalMessage);
|
|
709
|
-
await this.
|
|
710
|
-
return {
|
|
711
|
-
userHistoryEntryId: association.historyEntryId,
|
|
712
|
-
turn,
|
|
713
|
-
};
|
|
771
|
+
await this.settleTurn(association.historyEntryId, turn);
|
|
772
|
+
return { userHistoryEntryId: association.historyEntryId, turn };
|
|
714
773
|
}),
|
|
715
774
|
};
|
|
716
775
|
}
|
|
@@ -784,7 +843,7 @@ class LocalHostedSessionHandle {
|
|
|
784
843
|
sessionId: snapshot.sessionId,
|
|
785
844
|
fromRevision,
|
|
786
845
|
toRevision: snapshot.revision,
|
|
787
|
-
|
|
846
|
+
cause: { type: "configuration" },
|
|
788
847
|
delta: {
|
|
789
848
|
type: "snapshot.patch",
|
|
790
849
|
changes: [{ type: "settings.set", settings: snapshot.settings }],
|
|
@@ -926,16 +985,11 @@ class LocalHostedSessionHandle {
|
|
|
926
985
|
signal,
|
|
927
986
|
});
|
|
928
987
|
signal.throwIfAborted();
|
|
929
|
-
return
|
|
930
|
-
this.
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
snapshot,
|
|
935
|
-
compactionMessage: result.compactionMessage,
|
|
936
|
-
includedLastAssistant: result.includedLastAssistant,
|
|
937
|
-
};
|
|
938
|
-
});
|
|
988
|
+
return {
|
|
989
|
+
snapshot: await this.snapshot(),
|
|
990
|
+
compactionMessage: result.compactionMessage,
|
|
991
|
+
includedLastAssistant: result.includedLastAssistant,
|
|
992
|
+
};
|
|
939
993
|
});
|
|
940
994
|
}
|
|
941
995
|
async runMaintenance(operation) {
|
|
@@ -979,6 +1033,9 @@ class LocalHostedSessionHandle {
|
|
|
979
1033
|
}
|
|
980
1034
|
async rewindToHistoryEntryId(historyEntryId) {
|
|
981
1035
|
this.assertActive();
|
|
1036
|
+
if (!this.timeline.items.some((item) => item.type === "message" && item.messageId === historyEntryId)) {
|
|
1037
|
+
throw new Error("rewind failed");
|
|
1038
|
+
}
|
|
982
1039
|
const result = await this.session.rewindToHistoryEntryId(historyEntryId);
|
|
983
1040
|
if (!result) {
|
|
984
1041
|
throw new Error("rewind failed");
|
|
@@ -1180,7 +1237,14 @@ class LocalHostedSessionHandle {
|
|
|
1180
1237
|
if (goal?.status !== "active") {
|
|
1181
1238
|
throw new Error("cannot commit logical steering without an active goal");
|
|
1182
1239
|
}
|
|
1183
|
-
const historyEntryId =
|
|
1240
|
+
const historyEntryId = this.createTurnHistoryEntryId();
|
|
1241
|
+
this.pendingAcceptedTurnHistoryEntryIds.add(historyEntryId);
|
|
1242
|
+
try {
|
|
1243
|
+
await this.session.commitUserText(prependGoalPolicy(formatSteeringUserMessage(submissions.map((submission) => submission.text)), goal), { historyEntryId });
|
|
1244
|
+
}
|
|
1245
|
+
finally {
|
|
1246
|
+
this.pendingAcceptedTurnHistoryEntryIds.delete(historyEntryId);
|
|
1247
|
+
}
|
|
1184
1248
|
for (const submission of submissions) {
|
|
1185
1249
|
submission.resolveApplied({ userHistoryEntryId: historyEntryId });
|
|
1186
1250
|
}
|
|
@@ -1195,8 +1259,9 @@ class LocalHostedSessionHandle {
|
|
|
1195
1259
|
throw error;
|
|
1196
1260
|
}
|
|
1197
1261
|
}
|
|
1198
|
-
resolveCommittedLogicalSteering(committed, turn) {
|
|
1262
|
+
async resolveCommittedLogicalSteering(committed, turn) {
|
|
1199
1263
|
for (const { historyEntryId, submissions } of committed) {
|
|
1264
|
+
await this.settleTurn(historyEntryId, turn);
|
|
1200
1265
|
for (const submission of submissions) {
|
|
1201
1266
|
submission.resolveResult({ userHistoryEntryId: historyEntryId, turn });
|
|
1202
1267
|
}
|
|
@@ -1216,23 +1281,20 @@ class LocalHostedSessionHandle {
|
|
|
1216
1281
|
submission.rejectResult(error);
|
|
1217
1282
|
}
|
|
1218
1283
|
}
|
|
1219
|
-
async cancelLogicalTurn(
|
|
1284
|
+
async cancelLogicalTurn(committedSteering = []) {
|
|
1220
1285
|
const outcome = abortedTurnOutcome();
|
|
1221
1286
|
await this.enqueueMutation(async () => {
|
|
1222
|
-
this.turnOutcomes.set(rootHistoryEntryId, outcome);
|
|
1223
|
-
for (const steering of committedSteering) {
|
|
1224
|
-
this.turnOutcomes.set(steering.historyEntryId, outcome);
|
|
1225
|
-
}
|
|
1226
1287
|
if (this.goal?.status === "active") {
|
|
1227
1288
|
this.goal = { ...this.goal, status: "blocked" };
|
|
1228
1289
|
}
|
|
1229
1290
|
await this.emitSnapshotResetIfChanged("assistant-message");
|
|
1230
1291
|
});
|
|
1231
|
-
this.resolveCommittedLogicalSteering(committedSteering, outcome);
|
|
1292
|
+
await this.resolveCommittedLogicalSteering(committedSteering, outcome);
|
|
1232
1293
|
return outcome;
|
|
1233
1294
|
}
|
|
1234
1295
|
async commitSnapshot() {
|
|
1235
1296
|
this.assertNotDisposed();
|
|
1297
|
+
this.reconcileProjections();
|
|
1236
1298
|
const draft = this.buildSnapshotDraft();
|
|
1237
1299
|
await this.switchSnapshotSession(draft.sessionId);
|
|
1238
1300
|
const snapshot = {
|
|
@@ -1265,7 +1327,7 @@ class LocalHostedSessionHandle {
|
|
|
1265
1327
|
this.persistedSnapshot = cloneSessionProtocolSnapshot(snapshot);
|
|
1266
1328
|
return snapshot;
|
|
1267
1329
|
}
|
|
1268
|
-
async commitSnapshotPatch(
|
|
1330
|
+
async commitSnapshotPatch(cause, changes) {
|
|
1269
1331
|
this.assertNotDisposed();
|
|
1270
1332
|
const current = this.committedSnapshot;
|
|
1271
1333
|
if (!current) {
|
|
@@ -1275,7 +1337,7 @@ class LocalHostedSessionHandle {
|
|
|
1275
1337
|
sessionId: current.sessionId,
|
|
1276
1338
|
fromRevision: current.revision,
|
|
1277
1339
|
toRevision: current.revision + 1,
|
|
1278
|
-
|
|
1340
|
+
cause: normalizeDeltaCause(cause),
|
|
1279
1341
|
delta: { type: "snapshot.patch", changes },
|
|
1280
1342
|
});
|
|
1281
1343
|
const snapshot = applySessionProtocolDelta(current, delta);
|
|
@@ -1300,10 +1362,8 @@ class LocalHostedSessionHandle {
|
|
|
1300
1362
|
reconcileProjections(options = {}) {
|
|
1301
1363
|
const messageIds = new Set(this.session.rawHistoryEntries.map((entry) => entry.id));
|
|
1302
1364
|
messageIds.add("system");
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
this.turnOutcomes.delete(id);
|
|
1306
|
-
}
|
|
1365
|
+
if (this.draftAssistantMessage) {
|
|
1366
|
+
messageIds.add(this.draftAssistantMessage.id);
|
|
1307
1367
|
}
|
|
1308
1368
|
for (const [id, tool] of this.tools) {
|
|
1309
1369
|
const messageId = tool.status === "streaming" ? tool.origin.messageId : tool.call.messageId;
|
|
@@ -1322,13 +1382,36 @@ class LocalHostedSessionHandle {
|
|
|
1322
1382
|
}
|
|
1323
1383
|
}
|
|
1324
1384
|
}
|
|
1325
|
-
const
|
|
1385
|
+
for (const id of this.operations.keys()) {
|
|
1386
|
+
const hasTimelineItem = this.timeline.items.some((item) => item.type === "operation" && item.operationId === id);
|
|
1387
|
+
if (!hasTimelineItem) {
|
|
1388
|
+
this.operations.delete(id);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
this.timeline.items = this.timeline.items.filter((item) => {
|
|
1392
|
+
switch (item.type) {
|
|
1393
|
+
case "message":
|
|
1394
|
+
return messageIds.has(item.messageId);
|
|
1395
|
+
case "tool":
|
|
1396
|
+
return this.tools.has(item.toolId);
|
|
1397
|
+
case "notice":
|
|
1398
|
+
return (item.notice.subject.type === "session" ||
|
|
1399
|
+
(item.notice.subject.type === "message" && messageIds.has(item.notice.subject.id)) ||
|
|
1400
|
+
(item.notice.subject.type === "tool" && this.tools.has(item.notice.subject.id)) ||
|
|
1401
|
+
(item.notice.subject.type === "agent" && this.agents.has(item.notice.subject.id)) ||
|
|
1402
|
+
(item.notice.subject.type === "operation" &&
|
|
1403
|
+
this.operations.has(item.notice.subject.id)));
|
|
1404
|
+
case "operation":
|
|
1405
|
+
return this.operations.has(item.operationId);
|
|
1406
|
+
}
|
|
1407
|
+
return false;
|
|
1408
|
+
});
|
|
1326
1409
|
for (const [id, facet] of this.facets) {
|
|
1327
1410
|
const subjectExists = facet.subject.type === "session" ||
|
|
1328
1411
|
(facet.subject.type === "message" && messageIds.has(facet.subject.id)) ||
|
|
1329
1412
|
(facet.subject.type === "tool" && this.tools.has(facet.subject.id)) ||
|
|
1330
1413
|
(facet.subject.type === "agent" && this.agents.has(facet.subject.id)) ||
|
|
1331
|
-
(facet.subject.type === "operation" &&
|
|
1414
|
+
(facet.subject.type === "operation" && this.operations.has(facet.subject.id));
|
|
1332
1415
|
if (!subjectExists) {
|
|
1333
1416
|
this.facets.delete(id);
|
|
1334
1417
|
}
|
|
@@ -1343,7 +1426,7 @@ class LocalHostedSessionHandle {
|
|
|
1343
1426
|
createdAt: this.createdAt,
|
|
1344
1427
|
agentState: {
|
|
1345
1428
|
revision: agentState.revision,
|
|
1346
|
-
|
|
1429
|
+
modelContextKey: agentState.modelContextKey,
|
|
1347
1430
|
...(agentState.usageCheckpoint
|
|
1348
1431
|
? { usageCheckpoint: { ...agentState.usageCheckpoint } }
|
|
1349
1432
|
: {}),
|
|
@@ -1370,8 +1453,10 @@ class LocalHostedSessionHandle {
|
|
|
1370
1453
|
catalog: structuredClone(this.catalog),
|
|
1371
1454
|
executionEnvironment: this.executionEnvironment.snapshot(),
|
|
1372
1455
|
messages,
|
|
1373
|
-
|
|
1456
|
+
turns: Object.fromEntries(this.turns),
|
|
1457
|
+
timeline: structuredClone(this.timeline),
|
|
1374
1458
|
tools: Object.fromEntries(this.tools),
|
|
1459
|
+
operations: Object.fromEntries(this.operations),
|
|
1375
1460
|
agents: Object.fromEntries(this.agents),
|
|
1376
1461
|
facets: Object.fromEntries(this.facets),
|
|
1377
1462
|
};
|
|
@@ -1387,64 +1472,90 @@ class LocalHostedSessionHandle {
|
|
|
1387
1472
|
timestamp: 0,
|
|
1388
1473
|
},
|
|
1389
1474
|
};
|
|
1390
|
-
const historyMessages = this.session.rawHistoryEntries.map((entry) => {
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
message: entry.message,
|
|
1397
|
-
...(turn ? { turn } : {}),
|
|
1398
|
-
};
|
|
1399
|
-
});
|
|
1475
|
+
const historyMessages = this.session.rawHistoryEntries.map((entry) => ({
|
|
1476
|
+
id: entry.id,
|
|
1477
|
+
state: this.messageStates.get(entry.id) ?? "committed",
|
|
1478
|
+
modelVisible: true,
|
|
1479
|
+
message: entry.message,
|
|
1480
|
+
}));
|
|
1400
1481
|
return [
|
|
1401
1482
|
systemMessage,
|
|
1402
1483
|
...historyMessages,
|
|
1403
1484
|
...(this.draftAssistantMessage ? [structuredClone(this.draftAssistantMessage)] : []),
|
|
1404
1485
|
];
|
|
1405
1486
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1487
|
+
nextTimelineItemBase(id, createdAt) {
|
|
1488
|
+
this.timeline.sequence += 1;
|
|
1489
|
+
return { id, sequence: this.timeline.sequence, createdAt };
|
|
1490
|
+
}
|
|
1491
|
+
appendMessageTimelineItem(messageId, createdAt) {
|
|
1492
|
+
const item = {
|
|
1493
|
+
...this.nextTimelineItemBase(`timeline-message-${messageId}`, createdAt),
|
|
1410
1494
|
type: "message",
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
return
|
|
1495
|
+
messageId,
|
|
1496
|
+
};
|
|
1497
|
+
this.timeline.items.push(item);
|
|
1498
|
+
return item;
|
|
1415
1499
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1500
|
+
appendToolTimelineItem(toolId, createdAt) {
|
|
1501
|
+
const item = {
|
|
1502
|
+
...this.nextTimelineItemBase(`timeline-tool-${toolId}`, createdAt),
|
|
1503
|
+
type: "tool",
|
|
1504
|
+
toolId,
|
|
1505
|
+
};
|
|
1506
|
+
this.timeline.items.push(item);
|
|
1507
|
+
return item;
|
|
1508
|
+
}
|
|
1509
|
+
appendNoticeTimelineItem(id, createdAt, notice) {
|
|
1510
|
+
const item = {
|
|
1511
|
+
...this.nextTimelineItemBase(id, createdAt),
|
|
1512
|
+
type: "notice",
|
|
1513
|
+
notice,
|
|
1514
|
+
};
|
|
1515
|
+
this.timeline.items.push(item);
|
|
1516
|
+
return item;
|
|
1517
|
+
}
|
|
1518
|
+
appendTurnFailureTimelineItem(historyEntryId, failure) {
|
|
1519
|
+
const blocked = failure.reason === "auto-compaction-failed";
|
|
1520
|
+
return this.appendNoticeTimelineItem(`turn-${blocked ? "blocked" : "failed"}-${randomUUID()}`, Date.now(), {
|
|
1521
|
+
kind: blocked ? "tau.turn.blocked" : "tau.turn.failed",
|
|
1522
|
+
version: 1,
|
|
1523
|
+
severity: "error",
|
|
1524
|
+
subject: { type: "message", id: historyEntryId },
|
|
1525
|
+
presentation: projectSessionProtocolNoticePresentation(blocked ? "turn blocked" : "turn failed", [failure.message]),
|
|
1526
|
+
data: { reason: failure.reason },
|
|
1527
|
+
});
|
|
1429
1528
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1529
|
+
appendOperationTimelineItem(operation) {
|
|
1530
|
+
const item = {
|
|
1531
|
+
...this.nextTimelineItemBase(`timeline-operation-${operation.id}`, operation.startedAt),
|
|
1532
|
+
type: "operation",
|
|
1533
|
+
operationId: operation.id,
|
|
1534
|
+
};
|
|
1535
|
+
this.operations.set(operation.id, operation);
|
|
1536
|
+
this.timeline.items.push(item);
|
|
1537
|
+
return item;
|
|
1538
|
+
}
|
|
1539
|
+
removeTimelineItem(id) {
|
|
1540
|
+
this.timeline.items = this.timeline.items.filter((item) => item.id !== id);
|
|
1434
1541
|
}
|
|
1435
1542
|
restoreProtocolState(snapshot) {
|
|
1436
1543
|
if (!snapshot) {
|
|
1437
1544
|
return;
|
|
1438
1545
|
}
|
|
1439
|
-
this.
|
|
1440
|
-
this.
|
|
1441
|
-
|
|
1442
|
-
.
|
|
1443
|
-
|
|
1546
|
+
this.timeline = structuredClone(snapshot.timeline);
|
|
1547
|
+
this.turns.clear();
|
|
1548
|
+
for (const [id, turn] of Object.entries(snapshot.turns)) {
|
|
1549
|
+
this.turns.set(id, structuredClone(turn));
|
|
1550
|
+
}
|
|
1444
1551
|
this.tools.clear();
|
|
1445
1552
|
for (const [id, tool] of Object.entries(snapshot.tools)) {
|
|
1446
1553
|
this.tools.set(id, structuredClone(tool));
|
|
1447
1554
|
}
|
|
1555
|
+
this.operations.clear();
|
|
1556
|
+
for (const [id, operation] of Object.entries(snapshot.operations)) {
|
|
1557
|
+
this.operations.set(id, structuredClone(operation));
|
|
1558
|
+
}
|
|
1448
1559
|
this.agents.clear();
|
|
1449
1560
|
this.agentCostTotals.clear();
|
|
1450
1561
|
for (const [id, agent] of Object.entries(snapshot.agents)) {
|
|
@@ -1459,14 +1570,10 @@ class LocalHostedSessionHandle {
|
|
|
1459
1570
|
const historyEntryIds = new Set(this.session.rawHistoryEntries.map((entry) => entry.id));
|
|
1460
1571
|
this.draftAssistantMessage = snapshot.messages.find((message) => message.state === "draft" && !historyEntryIds.has(message.id));
|
|
1461
1572
|
this.messageStates.clear();
|
|
1462
|
-
this.turnOutcomes.clear();
|
|
1463
1573
|
for (const message of snapshot.messages) {
|
|
1464
1574
|
if (message.state !== "committed" && message.state !== "draft") {
|
|
1465
1575
|
this.messageStates.set(message.id, message.state);
|
|
1466
1576
|
}
|
|
1467
|
-
if (message.turn) {
|
|
1468
|
-
this.turnOutcomes.set(message.id, message.turn);
|
|
1469
|
-
}
|
|
1470
1577
|
}
|
|
1471
1578
|
}
|
|
1472
1579
|
enqueueMutation(mutation) {
|
|
@@ -1514,47 +1621,34 @@ class LocalHostedSessionHandle {
|
|
|
1514
1621
|
async enqueueRuntimeEvent(event) {
|
|
1515
1622
|
await this.enqueueMutation(() => this.recordRuntimeEvent(event));
|
|
1516
1623
|
}
|
|
1517
|
-
async commitSteeringTurnOutcome(historyEntryId, turn) {
|
|
1518
|
-
await this.enqueueMutation(async () => {
|
|
1519
|
-
this.turnOutcomes.set(historyEntryId, turn);
|
|
1520
|
-
const message = this.committedSnapshot?.messages.find((candidate) => candidate.id === historyEntryId);
|
|
1521
|
-
if (!message) {
|
|
1522
|
-
throw new Error(`missing steering user message '${historyEntryId}'`);
|
|
1523
|
-
}
|
|
1524
|
-
if (isDeepStrictEqual(message.turn, turn)) {
|
|
1525
|
-
return;
|
|
1526
|
-
}
|
|
1527
|
-
await this.emitPatch("assistant-message", [
|
|
1528
|
-
{
|
|
1529
|
-
type: "message.replace",
|
|
1530
|
-
message: { ...structuredClone(message), turn },
|
|
1531
|
-
},
|
|
1532
|
-
]);
|
|
1533
|
-
});
|
|
1534
|
-
}
|
|
1535
1624
|
removeToolRun(tool, changes) {
|
|
1536
1625
|
this.tools.delete(tool.id);
|
|
1626
|
+
const timelineItemId = `timeline-tool-${tool.id}`;
|
|
1627
|
+
this.removeTimelineItem(timelineItemId);
|
|
1537
1628
|
for (const facetId of tool.facetIds) {
|
|
1538
1629
|
this.facets.delete(facetId);
|
|
1539
1630
|
changes.push({ type: "facet.remove", id: facetId });
|
|
1540
1631
|
}
|
|
1541
|
-
changes.push({ type: "tool.remove", id: tool.id });
|
|
1632
|
+
changes.push({ type: "tool.remove", id: tool.id }, { type: "timeline.remove", id: timelineItemId });
|
|
1542
1633
|
}
|
|
1543
1634
|
async recordHistoryFailure(reason) {
|
|
1544
|
-
if (!reason || this.
|
|
1635
|
+
if (!reason || this.timeline.items.some((item) => item.id === HISTORY_UNAVAILABLE_NOTICE_ID)) {
|
|
1545
1636
|
return;
|
|
1546
1637
|
}
|
|
1547
|
-
const item = {
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1638
|
+
const item = this.appendNoticeTimelineItem(HISTORY_UNAVAILABLE_NOTICE_ID, Date.now(), {
|
|
1639
|
+
kind: "tau.history.unavailable",
|
|
1640
|
+
version: 1,
|
|
1641
|
+
severity: "warn",
|
|
1642
|
+
subject: { type: "session" },
|
|
1643
|
+
presentation: projectSessionProtocolNoticePresentation("session history is unavailable", [
|
|
1644
|
+
reason,
|
|
1645
|
+
"this session will continue without durable history recording or recall",
|
|
1646
|
+
]),
|
|
1647
|
+
data: {},
|
|
1648
|
+
});
|
|
1649
|
+
await this.emitPatch({ type: "notice" }, [
|
|
1650
|
+
{ type: "timeline.append", item: structuredClone(item) },
|
|
1651
|
+
]);
|
|
1558
1652
|
}
|
|
1559
1653
|
async appendToolHistory(tool, resultHistoryEntryId, result) {
|
|
1560
1654
|
const callMessage = this.committedSnapshot?.messages.find((message) => message.id === tool.call.messageId);
|
|
@@ -1583,7 +1677,7 @@ class LocalHostedSessionHandle {
|
|
|
1583
1677
|
type: "agent-state.set",
|
|
1584
1678
|
agentState: {
|
|
1585
1679
|
revision: state.revision,
|
|
1586
|
-
|
|
1680
|
+
modelContextKey: state.modelContextKey,
|
|
1587
1681
|
...(state.usageCheckpoint ? { usageCheckpoint: { ...state.usageCheckpoint } } : {}),
|
|
1588
1682
|
},
|
|
1589
1683
|
};
|
|
@@ -1597,13 +1691,13 @@ class LocalHostedSessionHandle {
|
|
|
1597
1691
|
modelVisible: false,
|
|
1598
1692
|
message: { role: "assistant", content: [], timestamp: Date.now() },
|
|
1599
1693
|
};
|
|
1600
|
-
const timelineItem =
|
|
1694
|
+
const timelineItem = this.appendMessageTimelineItem(this.draftAssistantMessage.id, this.draftAssistantMessage.message.timestamp);
|
|
1601
1695
|
await this.emitPatch("assistant-stream", [
|
|
1602
1696
|
{
|
|
1603
1697
|
type: "message.append",
|
|
1604
1698
|
message: structuredClone(this.draftAssistantMessage),
|
|
1605
|
-
timelineItem,
|
|
1606
1699
|
},
|
|
1700
|
+
{ type: "timeline.append", item: structuredClone(timelineItem) },
|
|
1607
1701
|
{ type: "lifecycle.set", lifecycle: "running" },
|
|
1608
1702
|
], { persist: false });
|
|
1609
1703
|
return;
|
|
@@ -1651,7 +1745,8 @@ class LocalHostedSessionHandle {
|
|
|
1651
1745
|
};
|
|
1652
1746
|
this.tools.set(tool.id, tool);
|
|
1653
1747
|
this.facets.set(facet.id, facet);
|
|
1654
|
-
|
|
1748
|
+
const timelineItem = this.appendToolTimelineItem(tool.id, Date.now());
|
|
1749
|
+
changes.push({ type: "tool.set", tool: structuredClone(tool) }, { type: "facet.set", facet: structuredClone(facet) }, { type: "timeline.append", item: structuredClone(timelineItem) });
|
|
1655
1750
|
await this.emitPatch("tool-run", changes, { persist: false });
|
|
1656
1751
|
return;
|
|
1657
1752
|
}
|
|
@@ -1706,11 +1801,24 @@ class LocalHostedSessionHandle {
|
|
|
1706
1801
|
}
|
|
1707
1802
|
case "user_message": {
|
|
1708
1803
|
const pendingGoal = this.pendingGoalCommit;
|
|
1804
|
+
const acceptsTurn = this.pendingAcceptedTurnHistoryEntryIds.has(event.historyEntryId) ||
|
|
1805
|
+
event.origin === "steering";
|
|
1806
|
+
const turn = acceptsTurn
|
|
1807
|
+
? { userHistoryEntryId: event.historyEntryId, state: "running" }
|
|
1808
|
+
: undefined;
|
|
1809
|
+
if (turn) {
|
|
1810
|
+
if (this.turns.has(event.historyEntryId)) {
|
|
1811
|
+
throw new Error(`turn '${event.historyEntryId}' was already accepted`);
|
|
1812
|
+
}
|
|
1813
|
+
this.turns.set(event.historyEntryId, turn);
|
|
1814
|
+
}
|
|
1815
|
+
const timelineItem = this.appendMessageTimelineItem(event.historyEntryId, event.message.timestamp);
|
|
1709
1816
|
await this.emitPatch("user-message", [
|
|
1710
1817
|
this.agentStateChange(),
|
|
1711
1818
|
...(pendingGoal
|
|
1712
1819
|
? [{ type: "goal.set", goal: structuredClone(pendingGoal) }]
|
|
1713
1820
|
: []),
|
|
1821
|
+
...(turn ? [{ type: "turn.set", turn }] : []),
|
|
1714
1822
|
{
|
|
1715
1823
|
type: "message.append",
|
|
1716
1824
|
message: {
|
|
@@ -1719,20 +1827,31 @@ class LocalHostedSessionHandle {
|
|
|
1719
1827
|
modelVisible: true,
|
|
1720
1828
|
message: event.message,
|
|
1721
1829
|
},
|
|
1722
|
-
timelineItem: timelineItemForMessage(event.historyEntryId),
|
|
1723
1830
|
},
|
|
1831
|
+
{ type: "timeline.append", item: structuredClone(timelineItem) },
|
|
1724
1832
|
], { persist: true });
|
|
1833
|
+
if (turn) {
|
|
1834
|
+
this.pendingAcceptedTurnHistoryEntryIds.delete(event.historyEntryId);
|
|
1835
|
+
}
|
|
1725
1836
|
await this.recordHistoryFailure(this.history.append(this.sessionId, [userHistoryEntry(event.historyEntryId, event.message)], this.historyRemote));
|
|
1726
1837
|
if (pendingGoal && this.pendingGoalCommit === pendingGoal) {
|
|
1727
1838
|
this.pendingGoalCommit = undefined;
|
|
1728
1839
|
}
|
|
1729
1840
|
return;
|
|
1730
1841
|
}
|
|
1731
|
-
case "history_rewound":
|
|
1842
|
+
case "history_rewound": {
|
|
1843
|
+
const rewindItem = this.timeline.items.find((item) => item.type === "message" && item.messageId === event.historyEntryId);
|
|
1844
|
+
if (!rewindItem) {
|
|
1845
|
+
throw new Error(`missing timeline item for rewind message '${event.historyEntryId}'`);
|
|
1846
|
+
}
|
|
1847
|
+
const cutoffSequence = rewindItem.sequence - 1;
|
|
1848
|
+
this.timeline.items = this.timeline.items.filter((item) => item.sequence <= cutoffSequence);
|
|
1732
1849
|
this.reconcileProjections({ removeMissingAgents: true });
|
|
1733
|
-
await this.
|
|
1850
|
+
const snapshot = await this.commitSnapshot();
|
|
1851
|
+
this.emitSnapshotReset({ type: "rewind", epoch: this.timeline.epoch, cutoffSequence }, snapshot);
|
|
1734
1852
|
await this.recordHistoryFailure(this.history.truncateFromSources(this.sessionId, event.removedEntryIds, this.historyRemote));
|
|
1735
1853
|
return;
|
|
1854
|
+
}
|
|
1736
1855
|
case "assistant_final": {
|
|
1737
1856
|
this.draftAssistantMessage = undefined;
|
|
1738
1857
|
const messageState = event.message.stopReason === "aborted" ? "interrupted" : "committed";
|
|
@@ -1833,7 +1952,6 @@ class LocalHostedSessionHandle {
|
|
|
1833
1952
|
modelVisible: true,
|
|
1834
1953
|
message: event.message,
|
|
1835
1954
|
},
|
|
1836
|
-
timelineItem: timelineItemForMessage(event.historyEntryId),
|
|
1837
1955
|
},
|
|
1838
1956
|
{ type: "tool.set", tool: structuredClone(nextTool) },
|
|
1839
1957
|
]);
|
|
@@ -1850,7 +1968,6 @@ class LocalHostedSessionHandle {
|
|
|
1850
1968
|
modelVisible: true,
|
|
1851
1969
|
message: event.message,
|
|
1852
1970
|
},
|
|
1853
|
-
timelineItem: timelineItemForMessage(event.historyEntryId),
|
|
1854
1971
|
},
|
|
1855
1972
|
]);
|
|
1856
1973
|
return;
|
|
@@ -1884,52 +2001,137 @@ class LocalHostedSessionHandle {
|
|
|
1884
2001
|
changes.push({ type: "tool.set", tool: structuredClone(nextTool) });
|
|
1885
2002
|
historyTools.push({ tool: existing, result: toolResult });
|
|
1886
2003
|
}
|
|
1887
|
-
await this.emitPatch("
|
|
2004
|
+
await this.emitPatch({ type: "resync" }, changes);
|
|
1888
2005
|
for (const { tool, result } of historyTools) {
|
|
1889
2006
|
await this.appendToolHistory(tool, event.historyEntryId, result);
|
|
1890
2007
|
}
|
|
1891
2008
|
return;
|
|
1892
2009
|
}
|
|
1893
|
-
case "
|
|
2010
|
+
case "feedback": {
|
|
2011
|
+
if (event.presentation === "footer") {
|
|
2012
|
+
this.emitEphemeral(createSessionProtocolEphemeralMessage({
|
|
2013
|
+
sessionId: this.sessionId,
|
|
2014
|
+
event: {
|
|
2015
|
+
type: "feedback.notice",
|
|
2016
|
+
title: projectSessionProtocolNoticePresentation(event.title).title,
|
|
2017
|
+
tone: event.tone,
|
|
2018
|
+
presentation: "footer",
|
|
2019
|
+
durationMs: event.durationMs,
|
|
2020
|
+
},
|
|
2021
|
+
}));
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
const createdAt = Date.now();
|
|
1894
2025
|
const item = {
|
|
2026
|
+
...this.nextTimelineItemBase(`ephemeral-notice-${randomUUID()}`, createdAt),
|
|
1895
2027
|
type: "notice",
|
|
1896
|
-
id: `notice-${Date.now()}-${this.timelineExtras.length}`,
|
|
1897
2028
|
notice: {
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
2029
|
+
kind: "tau.runtime.feedback",
|
|
2030
|
+
version: 1,
|
|
2031
|
+
severity: event.tone === "error" ? "error" : "info",
|
|
2032
|
+
subject: { type: "session" },
|
|
2033
|
+
presentation: projectSessionProtocolNoticePresentation(event.title, event.content),
|
|
2034
|
+
data: {},
|
|
1901
2035
|
},
|
|
1902
2036
|
};
|
|
1903
|
-
this.
|
|
1904
|
-
|
|
2037
|
+
await this.emitPatch("notice", [
|
|
2038
|
+
{
|
|
2039
|
+
type: "timeline.advance",
|
|
2040
|
+
epoch: this.timeline.epoch,
|
|
2041
|
+
sequence: item.sequence,
|
|
2042
|
+
},
|
|
2043
|
+
]);
|
|
2044
|
+
this.emitEphemeral(createSessionProtocolEphemeralMessage({
|
|
2045
|
+
sessionId: this.sessionId,
|
|
2046
|
+
event: { type: "timeline.item", epoch: this.timeline.epoch, item },
|
|
2047
|
+
}));
|
|
1905
2048
|
return;
|
|
1906
2049
|
}
|
|
1907
2050
|
case "compaction_start": {
|
|
1908
|
-
const
|
|
1909
|
-
|
|
1910
|
-
id:
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
startedAt: Date.now(),
|
|
1915
|
-
},
|
|
2051
|
+
const kind = event.reason === "manual" ? "manual-compaction" : "auto-compaction";
|
|
2052
|
+
const operation = {
|
|
2053
|
+
id: `${kind}-${randomUUID()}`,
|
|
2054
|
+
kind,
|
|
2055
|
+
status: "running",
|
|
2056
|
+
startedAt: Date.now(),
|
|
1916
2057
|
};
|
|
1917
|
-
this.
|
|
2058
|
+
const item = this.appendOperationTimelineItem(operation);
|
|
1918
2059
|
await this.emitPatch("maintenance", [
|
|
2060
|
+
{ type: "operation.set", operation: structuredClone(operation) },
|
|
1919
2061
|
{ type: "timeline.append", item: structuredClone(item) },
|
|
1920
2062
|
]);
|
|
1921
2063
|
return;
|
|
1922
2064
|
}
|
|
1923
|
-
case "compaction_end":
|
|
1924
|
-
this.
|
|
1925
|
-
|
|
1926
|
-
|
|
2065
|
+
case "compaction_end": {
|
|
2066
|
+
const operation = [...this.operations.values()].findLast((candidate) => (candidate.kind === "auto-compaction" || candidate.kind === "manual-compaction") &&
|
|
2067
|
+
candidate.status === "running");
|
|
2068
|
+
if (operation) {
|
|
2069
|
+
const finishedAt = Date.now();
|
|
2070
|
+
const finishedOperation = event.outcome === "compacted"
|
|
2071
|
+
? { ...operation, status: "succeeded", finishedAt }
|
|
2072
|
+
: event.outcome === "failed"
|
|
2073
|
+
? {
|
|
2074
|
+
...operation,
|
|
2075
|
+
status: "failed",
|
|
2076
|
+
finishedAt,
|
|
2077
|
+
error: event.errorMessage,
|
|
2078
|
+
}
|
|
2079
|
+
: event.outcome === "skipped"
|
|
2080
|
+
? {
|
|
2081
|
+
...operation,
|
|
2082
|
+
status: "skipped",
|
|
2083
|
+
finishedAt,
|
|
2084
|
+
reason: "no-eligible-history",
|
|
2085
|
+
}
|
|
2086
|
+
: {
|
|
2087
|
+
...operation,
|
|
2088
|
+
status: "cancelled",
|
|
2089
|
+
finishedAt,
|
|
2090
|
+
reason: "interrupted",
|
|
2091
|
+
};
|
|
2092
|
+
this.operations.set(operation.id, finishedOperation);
|
|
2093
|
+
await this.emitPatch("maintenance", [
|
|
2094
|
+
{ type: "operation.set", operation: structuredClone(finishedOperation) },
|
|
2095
|
+
]);
|
|
2096
|
+
}
|
|
2097
|
+
if (event.outcome !== "compacted") {
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
const previousEpoch = this.timeline.epoch;
|
|
2101
|
+
this.timeline = { epoch: previousEpoch + 1, sequence: 0, items: [] };
|
|
2102
|
+
this.tools.clear();
|
|
2103
|
+
this.operations.clear();
|
|
2104
|
+
this.facets.clear();
|
|
2105
|
+
this.draftAssistantMessage = undefined;
|
|
2106
|
+
this.reconcileProjections({ removeMissingAgents: true });
|
|
2107
|
+
const summary = this.session.rawHistoryEntries.find((entry) => entry.id === event.result.summaryHistoryEntryId);
|
|
2108
|
+
if (!summary) {
|
|
2109
|
+
throw new Error(`missing compaction summary '${event.result.summaryHistoryEntryId}' in runtime history`);
|
|
2110
|
+
}
|
|
2111
|
+
this.appendMessageTimelineItem(summary.id, summary.message.timestamp);
|
|
2112
|
+
const snapshot = await this.commitSnapshot();
|
|
2113
|
+
this.emitSnapshotReset({
|
|
2114
|
+
type: "compaction",
|
|
2115
|
+
previousEpoch,
|
|
2116
|
+
epoch: this.timeline.epoch,
|
|
2117
|
+
kind: event.reason === "manual" ? "manual" : "auto",
|
|
2118
|
+
cutType: event.result.cutType,
|
|
2119
|
+
retainedMessageCount: event.result.retainedMessageCount,
|
|
2120
|
+
}, snapshot);
|
|
1927
2121
|
return;
|
|
2122
|
+
}
|
|
1928
2123
|
case "tool_activity":
|
|
1929
2124
|
await this.recordToolUiEvent(event.activity);
|
|
1930
2125
|
return;
|
|
2126
|
+
case "turn_finished": {
|
|
2127
|
+
if (!event.failure) {
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
const item = this.appendTurnFailureTimelineItem(event.historyEntryId, event.failure);
|
|
2131
|
+
await this.emitPatch("notice", [{ type: "timeline.append", item: structuredClone(item) }]);
|
|
2132
|
+
return;
|
|
2133
|
+
}
|
|
1931
2134
|
case "turn_started":
|
|
1932
|
-
case "turn_finished":
|
|
1933
2135
|
case "tool_call_admitted":
|
|
1934
2136
|
case "model_retry_scheduled":
|
|
1935
2137
|
case "model_retry_started":
|
|
@@ -2001,12 +2203,13 @@ class LocalHostedSessionHandle {
|
|
|
2001
2203
|
}
|
|
2002
2204
|
buildAssistantPartialChanges(previousDraft, nextDraft) {
|
|
2003
2205
|
if (!previousDraft) {
|
|
2206
|
+
const timelineItem = this.appendMessageTimelineItem(nextDraft.id, nextDraft.message.timestamp);
|
|
2004
2207
|
return [
|
|
2005
2208
|
{
|
|
2006
2209
|
type: "message.append",
|
|
2007
2210
|
message: structuredClone(nextDraft),
|
|
2008
|
-
timelineItem: timelineItemForMessage(nextDraft.id),
|
|
2009
2211
|
},
|
|
2212
|
+
{ type: "timeline.append", item: structuredClone(timelineItem) },
|
|
2010
2213
|
];
|
|
2011
2214
|
}
|
|
2012
2215
|
const appendChange = buildAssistantContentAppendChange(previousDraft, nextDraft);
|
|
@@ -2018,7 +2221,7 @@ class LocalHostedSessionHandle {
|
|
|
2018
2221
|
}
|
|
2019
2222
|
return [{ type: "message.replace", message: structuredClone(nextDraft) }];
|
|
2020
2223
|
}
|
|
2021
|
-
async emitPatch(
|
|
2224
|
+
async emitPatch(cause, changes, options = {}) {
|
|
2022
2225
|
if (changes.length === 0) {
|
|
2023
2226
|
return;
|
|
2024
2227
|
}
|
|
@@ -2026,44 +2229,44 @@ class LocalHostedSessionHandle {
|
|
|
2026
2229
|
if (options.persist === false) {
|
|
2027
2230
|
const snapshot = { ...this.buildSnapshotDraft(), revision: 1 };
|
|
2028
2231
|
this.committedSnapshot = snapshot;
|
|
2029
|
-
this.emitSnapshotReset(
|
|
2232
|
+
this.emitSnapshotReset(cause, snapshot);
|
|
2030
2233
|
return;
|
|
2031
2234
|
}
|
|
2032
2235
|
const snapshot = await this.commitSnapshot();
|
|
2033
|
-
this.emitSnapshotReset(
|
|
2236
|
+
this.emitSnapshotReset(cause, snapshot);
|
|
2034
2237
|
return;
|
|
2035
2238
|
}
|
|
2036
2239
|
if (options.persist !== false) {
|
|
2037
|
-
this.emitDelta(await this.commitSnapshotPatch(
|
|
2240
|
+
this.emitDelta(await this.commitSnapshotPatch(cause, changes));
|
|
2038
2241
|
return;
|
|
2039
2242
|
}
|
|
2040
2243
|
const delta = createSessionProtocolDeltaMessage({
|
|
2041
2244
|
sessionId: this.committedSnapshot.sessionId,
|
|
2042
2245
|
fromRevision: this.committedSnapshot.revision,
|
|
2043
2246
|
toRevision: this.committedSnapshot.revision + 1,
|
|
2044
|
-
|
|
2247
|
+
cause: normalizeDeltaCause(cause),
|
|
2045
2248
|
delta: { type: "snapshot.patch", changes },
|
|
2046
2249
|
});
|
|
2047
2250
|
this.committedSnapshot = applySessionProtocolDelta(this.committedSnapshot, delta);
|
|
2048
2251
|
this.emitDelta(delta);
|
|
2049
2252
|
}
|
|
2050
|
-
emitSnapshotReset(
|
|
2253
|
+
emitSnapshotReset(cause, snapshot) {
|
|
2051
2254
|
this.emitDelta(createSessionProtocolDeltaMessage({
|
|
2052
2255
|
sessionId: snapshot.sessionId,
|
|
2053
2256
|
fromRevision: null,
|
|
2054
2257
|
toRevision: snapshot.revision,
|
|
2055
|
-
|
|
2258
|
+
cause: normalizeDeltaCause(cause),
|
|
2056
2259
|
delta: { type: "snapshot.reset", snapshot },
|
|
2057
2260
|
}));
|
|
2058
2261
|
}
|
|
2059
|
-
async enqueueSnapshotResetIfChanged(
|
|
2060
|
-
await this.enqueueMutation(() => this.emitSnapshotResetIfChanged(
|
|
2262
|
+
async enqueueSnapshotResetIfChanged(cause) {
|
|
2263
|
+
await this.enqueueMutation(() => this.emitSnapshotResetIfChanged(cause));
|
|
2061
2264
|
}
|
|
2062
|
-
async emitSnapshotResetIfChanged(
|
|
2265
|
+
async emitSnapshotResetIfChanged(cause) {
|
|
2063
2266
|
const previousRevision = this.committedSnapshot?.revision;
|
|
2064
2267
|
const snapshot = await this.commitSnapshot();
|
|
2065
2268
|
if (snapshot.revision !== previousRevision) {
|
|
2066
|
-
this.emitSnapshotReset(
|
|
2269
|
+
this.emitSnapshotReset(cause, snapshot);
|
|
2067
2270
|
}
|
|
2068
2271
|
}
|
|
2069
2272
|
emitDelta(delta) {
|
|
@@ -2113,6 +2316,10 @@ class LocalHostedSessionHandle {
|
|
|
2113
2316
|
};
|
|
2114
2317
|
this.tools.set(content.id, nextTool);
|
|
2115
2318
|
changes.push({ type: "tool.set", tool: structuredClone(nextTool) });
|
|
2319
|
+
if (!existing) {
|
|
2320
|
+
const timelineItem = this.appendToolTimelineItem(content.id, Date.now());
|
|
2321
|
+
changes.push({ type: "timeline.append", item: structuredClone(timelineItem) });
|
|
2322
|
+
}
|
|
2116
2323
|
}
|
|
2117
2324
|
return changes;
|
|
2118
2325
|
}
|
|
@@ -2124,21 +2331,16 @@ class LocalHostedSessionHandle {
|
|
|
2124
2331
|
const interruptedMessage = createInterruptedAssistantMessage(draft, this.runtime.persona.model);
|
|
2125
2332
|
await this.session.commitInterruptedAssistant(interruptedMessage, draft.id);
|
|
2126
2333
|
}
|
|
2127
|
-
async cleanupFailedTurn(rootHistoryEntryId,
|
|
2334
|
+
async cleanupFailedTurn(rootHistoryEntryId, error) {
|
|
2128
2335
|
if (this.draftAssistantMessage) {
|
|
2129
2336
|
await this.interruptDraftAssistantMessage().catch(() => undefined);
|
|
2130
2337
|
}
|
|
2131
2338
|
const diagnostic = formatErrorDiagnostic(error);
|
|
2132
2339
|
await this.enqueueMutation(async () => {
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
};
|
|
2138
|
-
this.turnOutcomes.set(rootHistoryEntryId, outcome);
|
|
2139
|
-
for (const steering of committedSteering) {
|
|
2140
|
-
this.turnOutcomes.set(steering.historyEntryId, outcome);
|
|
2141
|
-
}
|
|
2340
|
+
this.appendTurnFailureTimelineItem(rootHistoryEntryId, {
|
|
2341
|
+
reason: "runtime-error",
|
|
2342
|
+
message: diagnostic,
|
|
2343
|
+
});
|
|
2142
2344
|
if (this.goal?.status === "active") {
|
|
2143
2345
|
this.goal = { ...this.goal, status: "blocked" };
|
|
2144
2346
|
}
|
|
@@ -2156,8 +2358,16 @@ class LocalHostedSessionHandle {
|
|
|
2156
2358
|
}
|
|
2157
2359
|
await this.emitSnapshotResetIfChanged("assistant-message");
|
|
2158
2360
|
});
|
|
2361
|
+
return { status: "failed", stopReason: "error", errorMessage: diagnostic };
|
|
2159
2362
|
}
|
|
2160
2363
|
}
|
|
2364
|
+
function normalizeExplicitHistoryEntryId(historyEntryId) {
|
|
2365
|
+
const normalizedId = historyEntryId.trim();
|
|
2366
|
+
if (!normalizedId) {
|
|
2367
|
+
throw new Error("history entry id must not be empty");
|
|
2368
|
+
}
|
|
2369
|
+
return normalizedId;
|
|
2370
|
+
}
|
|
2161
2371
|
function formatErrorDiagnostic(error) {
|
|
2162
2372
|
const messages = [];
|
|
2163
2373
|
const seen = new Set();
|
|
@@ -2260,9 +2470,19 @@ function getAssistantDraftBlockText(message, type) {
|
|
|
2260
2470
|
}
|
|
2261
2471
|
function normalizeRecoveredSnapshot(snapshot) {
|
|
2262
2472
|
const recovered = cloneSessionProtocolSnapshot(snapshot);
|
|
2263
|
-
const legacyAgentState = recovered.agentState.
|
|
2473
|
+
const legacyAgentState = recovered.agentState.modelContextKey === LEGACY_SESSION_MODEL_CONTEXT_KEY;
|
|
2264
2474
|
let changed = recovered.lifecycle !== "idle" || legacyAgentState;
|
|
2265
2475
|
recovered.lifecycle = "idle";
|
|
2476
|
+
for (const [id, turn] of Object.entries(recovered.turns)) {
|
|
2477
|
+
if (turn.state === "running") {
|
|
2478
|
+
changed = true;
|
|
2479
|
+
recovered.turns[id] = {
|
|
2480
|
+
userHistoryEntryId: id,
|
|
2481
|
+
state: "settled",
|
|
2482
|
+
outcome: abortedTurnOutcome(),
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2266
2486
|
if (recovered.goal?.status === "active") {
|
|
2267
2487
|
changed = true;
|
|
2268
2488
|
recovered.goal.status = "blocked";
|
|
@@ -2272,6 +2492,19 @@ function normalizeRecoveredSnapshot(snapshot) {
|
|
|
2272
2492
|
recovered.agents = {};
|
|
2273
2493
|
recovered.facets = Object.fromEntries(Object.entries(recovered.facets).filter(([, facet]) => facet.subject.type !== "agent"));
|
|
2274
2494
|
}
|
|
2495
|
+
const recoveredAt = Date.now();
|
|
2496
|
+
for (const [id, operation] of Object.entries(recovered.operations)) {
|
|
2497
|
+
if (operation.status !== "running") {
|
|
2498
|
+
continue;
|
|
2499
|
+
}
|
|
2500
|
+
changed = true;
|
|
2501
|
+
recovered.operations[id] = {
|
|
2502
|
+
...operation,
|
|
2503
|
+
status: "cancelled",
|
|
2504
|
+
finishedAt: recoveredAt,
|
|
2505
|
+
reason: "session-recovered",
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2275
2508
|
const streamingToolIds = new Set(Object.values(recovered.tools)
|
|
2276
2509
|
.filter((tool) => tool.status === "streaming")
|
|
2277
2510
|
.map((tool) => tool.id));
|
|
@@ -2279,6 +2512,10 @@ function normalizeRecoveredSnapshot(snapshot) {
|
|
|
2279
2512
|
changed = true;
|
|
2280
2513
|
recovered.tools = Object.fromEntries(Object.entries(recovered.tools).filter(([id]) => !streamingToolIds.has(id)));
|
|
2281
2514
|
recovered.facets = Object.fromEntries(Object.entries(recovered.facets).filter(([, facet]) => facet.subject.type !== "tool" || !streamingToolIds.has(facet.subject.id)));
|
|
2515
|
+
recovered.timeline.items = recovered.timeline.items.filter((item) => !(item.type === "tool" && streamingToolIds.has(item.toolId)) &&
|
|
2516
|
+
!(item.type === "notice" &&
|
|
2517
|
+
item.notice.subject.type === "tool" &&
|
|
2518
|
+
streamingToolIds.has(item.notice.subject.id)));
|
|
2282
2519
|
}
|
|
2283
2520
|
let recoveredAgentHistory = false;
|
|
2284
2521
|
recovered.messages = recovered.messages.map((message) => {
|
|
@@ -2297,7 +2534,7 @@ function normalizeRecoveredSnapshot(snapshot) {
|
|
|
2297
2534
|
if (recoveredAgentHistory) {
|
|
2298
2535
|
recovered.agentState = {
|
|
2299
2536
|
revision: recovered.agentState.revision + 1,
|
|
2300
|
-
|
|
2537
|
+
modelContextKey: recovered.agentState.modelContextKey,
|
|
2301
2538
|
...(recovered.agentState.usageCheckpoint
|
|
2302
2539
|
? { usageCheckpoint: { ...recovered.agentState.usageCheckpoint } }
|
|
2303
2540
|
: {}),
|
|
@@ -2319,13 +2556,6 @@ function promptCompositionFromSnapshot(snapshot) {
|
|
|
2319
2556
|
function cloneSubagentPrompts(subagentPrompts) {
|
|
2320
2557
|
return { ...subagentPrompts };
|
|
2321
2558
|
}
|
|
2322
|
-
function timelineItemForMessage(messageId) {
|
|
2323
|
-
return {
|
|
2324
|
-
type: "message",
|
|
2325
|
-
id: `timeline-${messageId}`,
|
|
2326
|
-
messageId,
|
|
2327
|
-
};
|
|
2328
|
-
}
|
|
2329
2559
|
function isCoreMessage(message) {
|
|
2330
2560
|
switch (message.role) {
|
|
2331
2561
|
case "user":
|