@markusylisiurunen/tau 0.3.21 → 0.3.22
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/dist/core/auth/auth_storage.js +249 -23
- package/dist/core/auth/auth_storage.js.map +1 -1
- package/dist/core/auth/cli.js +66 -0
- package/dist/core/auth/cli.js.map +1 -1
- package/dist/core/auth/credential_store.js +42 -10
- package/dist/core/auth/credential_store.js.map +1 -1
- package/dist/core/auth/index.js +1 -1
- package/dist/core/auth/index.js.map +1 -1
- package/dist/core/auth/providers/openai_codex.js +46 -22
- package/dist/core/auth/providers/openai_codex.js.map +1 -1
- package/dist/core/events/types.js +1 -7
- package/dist/core/events/types.js.map +1 -1
- package/dist/core/index.js +1 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/models/catalog.js +20 -24
- package/dist/core/models/catalog.js.map +1 -1
- package/dist/core/session/compaction.js +0 -4
- package/dist/core/session/compaction.js.map +1 -1
- package/dist/core/session/core_session.js +3 -0
- package/dist/core/session/core_session.js.map +1 -1
- package/dist/core/session/pruning.js +8 -17
- package/dist/core/session/pruning.js.map +1 -1
- package/dist/core/session/runner.js +12 -2
- package/dist/core/session/runner.js.map +1 -1
- package/dist/core/session/session_engine.js +62 -38
- package/dist/core/session/session_engine.js.map +1 -1
- package/dist/core/subagents/control_plane.js +8 -9
- package/dist/core/subagents/control_plane.js.map +1 -1
- package/dist/core/version.js +1 -1
- package/dist/execution/cloudflare_sandbox_execution_environment.js +54 -6
- package/dist/execution/cloudflare_sandbox_execution_environment.js.map +1 -1
- package/dist/host/hosted_ephemeral_agent_session.js +19 -14
- package/dist/host/hosted_ephemeral_agent_session.js.map +1 -1
- package/dist/host/local_session_host.js +269 -97
- package/dist/host/local_session_host.js.map +1 -1
- package/dist/host/session_host.js +2 -1
- package/dist/host/session_host.js.map +1 -1
- package/dist/host/session_protocol_handler.js +34 -5
- package/dist/host/session_protocol_handler.js.map +1 -1
- package/dist/main.js +17 -15
- package/dist/main.js.map +1 -1
- package/dist/protocol/session_protocol.d.ts +0 -1
- package/dist/protocol/session_protocol.js +103 -5
- package/dist/protocol/session_protocol.js.map +1 -1
- package/dist/store/file_session_store.js +223 -41
- package/dist/store/file_session_store.js.map +1 -1
- package/dist/transport/stdio_session_transport.d.ts +2 -0
- package/dist/transport/stdio_session_transport.js +30 -3
- package/dist/transport/stdio_session_transport.js.map +1 -1
- package/dist/tui/chat_controller/diff_review_service.js +8 -5
- package/dist/tui/chat_controller/diff_review_service.js.map +1 -1
- package/dist/tui/session_chat_controller.js +113 -46
- package/dist/tui/session_chat_controller.js.map +1 -1
- package/package.json +3 -3
- package/dist/core/events/index.js +0 -3
- package/dist/core/events/index.js.map +0 -1
- package/dist/core/events/parser.js +0 -334
- package/dist/core/events/parser.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
3
|
import { resolvePromptTemplateWithBackend } from "../core/config/index.js";
|
|
3
4
|
import { ChatRuntime } from "../core/runtime/chat_runtime.js";
|
|
4
5
|
import { filterProjectPathAutocompleteEntries, loadProjectPathAutocompleteEntriesWithBackend, } from "../core/utils/project_files.js";
|
|
@@ -55,15 +56,27 @@ export class LocalSessionHost {
|
|
|
55
56
|
return hostedSession;
|
|
56
57
|
}
|
|
57
58
|
async createRecoveredSession(snapshot, executionEnvironment) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
:
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
let hostedSession;
|
|
60
|
+
try {
|
|
61
|
+
const recovered = normalizeRecoveredSnapshot(snapshot);
|
|
62
|
+
hostedSession = await this.createLocalSessionHandle(executionEnvironment, recovered.snapshot, undefined, recovered.changed);
|
|
63
|
+
hostedSession.session.restoreState({
|
|
64
|
+
sessionId: recovered.snapshot.sessionId,
|
|
65
|
+
historyEntries: recovered.snapshot.messages.flatMap((entry) => entry.modelVisible && isCoreMessage(entry.message)
|
|
66
|
+
? [{ id: entry.id, message: entry.message }]
|
|
67
|
+
: []),
|
|
68
|
+
});
|
|
69
|
+
return hostedSession;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (hostedSession) {
|
|
73
|
+
await hostedSession.dispose();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
await executionEnvironment.dispose();
|
|
77
|
+
}
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
67
80
|
}
|
|
68
81
|
async createLocalSessionHandle(executionEnvironment, committedSnapshot, createParams, forceNextSnapshotRevision = false) {
|
|
69
82
|
const bootstrap = await this.resolveNewSessionBootstrap(executionEnvironment);
|
|
@@ -235,6 +248,16 @@ export class LocalSessionHost {
|
|
|
235
248
|
errors.push(result.reason);
|
|
236
249
|
}
|
|
237
250
|
}
|
|
251
|
+
for (const session of this.sessions) {
|
|
252
|
+
session.interruptTurn();
|
|
253
|
+
session.interruptMaintenance();
|
|
254
|
+
}
|
|
255
|
+
const settlementResults = await Promise.allSettled([...this.sessions].map((session) => session.waitForActiveWork()));
|
|
256
|
+
for (const result of settlementResults) {
|
|
257
|
+
if (result.status === "rejected") {
|
|
258
|
+
errors.push(result.reason);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
238
261
|
for (const session of this.sessions) {
|
|
239
262
|
try {
|
|
240
263
|
await session.snapshot();
|
|
@@ -285,7 +308,7 @@ class LocalHostedSessionHandle {
|
|
|
285
308
|
session;
|
|
286
309
|
committedSessionId;
|
|
287
310
|
committedSnapshot;
|
|
288
|
-
|
|
311
|
+
persistedSnapshot;
|
|
289
312
|
draftAssistantMessage;
|
|
290
313
|
messageStates = new Map();
|
|
291
314
|
restoredMessageIds;
|
|
@@ -293,6 +316,7 @@ class LocalHostedSessionHandle {
|
|
|
293
316
|
timelineExtras = [];
|
|
294
317
|
tools = new Map();
|
|
295
318
|
agents = new Map();
|
|
319
|
+
agentCostTotals = new Map();
|
|
296
320
|
facets = new Map();
|
|
297
321
|
deltaListeners = new Set();
|
|
298
322
|
ephemeralListeners = new Set();
|
|
@@ -302,6 +326,12 @@ class LocalHostedSessionHandle {
|
|
|
302
326
|
unsubscribeSubagentEvent;
|
|
303
327
|
runtimeEventQueue = Promise.resolve();
|
|
304
328
|
snapshotQueue = Promise.resolve();
|
|
329
|
+
snapshotGeneration = 0;
|
|
330
|
+
maintenanceAbortControllers = new Set();
|
|
331
|
+
maintenancePromises = new Set();
|
|
332
|
+
activeTurnPromise;
|
|
333
|
+
disposePromise;
|
|
334
|
+
disposing = false;
|
|
305
335
|
costTotal = 0;
|
|
306
336
|
forceNextSnapshotRevision;
|
|
307
337
|
disposed = false;
|
|
@@ -319,7 +349,9 @@ class LocalHostedSessionHandle {
|
|
|
319
349
|
this.committedSnapshot = committedSnapshot
|
|
320
350
|
? cloneSessionProtocolSnapshot(committedSnapshot)
|
|
321
351
|
: undefined;
|
|
322
|
-
this.
|
|
352
|
+
this.persistedSnapshot = committedSnapshot
|
|
353
|
+
? cloneSessionProtocolSnapshot(committedSnapshot)
|
|
354
|
+
: undefined;
|
|
323
355
|
this.forceNextSnapshotRevision = forceNextSnapshotRevision;
|
|
324
356
|
this.restoreProtocolState(committedSnapshot);
|
|
325
357
|
this.unsubscribeSubagentEvent = this.session.onSubagentEvent((event) => {
|
|
@@ -362,6 +394,18 @@ class LocalHostedSessionHandle {
|
|
|
362
394
|
}
|
|
363
395
|
async runTurn() {
|
|
364
396
|
this.assertActive();
|
|
397
|
+
const run = this.runTurnNow();
|
|
398
|
+
this.activeTurnPromise = run;
|
|
399
|
+
try {
|
|
400
|
+
return await run;
|
|
401
|
+
}
|
|
402
|
+
finally {
|
|
403
|
+
if (this.activeTurnPromise === run) {
|
|
404
|
+
this.activeTurnPromise = undefined;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async runTurnNow() {
|
|
365
409
|
try {
|
|
366
410
|
const result = await this.runtime.runTurn({
|
|
367
411
|
onEvent: (event) => this.recordTurnRuntimeEvent(event),
|
|
@@ -386,6 +430,23 @@ class LocalHostedSessionHandle {
|
|
|
386
430
|
this.assertActive();
|
|
387
431
|
return this.runtime.interruptTurn();
|
|
388
432
|
}
|
|
433
|
+
interruptMaintenance() {
|
|
434
|
+
let interrupted = false;
|
|
435
|
+
for (const abortController of this.maintenanceAbortControllers) {
|
|
436
|
+
if (!abortController.signal.aborted) {
|
|
437
|
+
abortController.abort();
|
|
438
|
+
interrupted = true;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return interrupted;
|
|
442
|
+
}
|
|
443
|
+
async waitForActiveWork() {
|
|
444
|
+
await Promise.allSettled([
|
|
445
|
+
...(this.activeTurnPromise ? [this.activeTurnPromise] : []),
|
|
446
|
+
...this.maintenancePromises,
|
|
447
|
+
]);
|
|
448
|
+
await this.runtimeEventQueue;
|
|
449
|
+
}
|
|
389
450
|
requestTurnBoundaryStop() {
|
|
390
451
|
this.assertActive();
|
|
391
452
|
return this.runtime.requestTurnBoundaryStop();
|
|
@@ -415,7 +476,7 @@ class LocalHostedSessionHandle {
|
|
|
415
476
|
else if (snapshot.revision !== fromRevision) {
|
|
416
477
|
this.emitDelta(createSessionProtocolDeltaMessage({
|
|
417
478
|
sessionId: snapshot.sessionId,
|
|
418
|
-
fromRevision,
|
|
479
|
+
fromRevision: snapshot.revision - 1,
|
|
419
480
|
toRevision: snapshot.revision,
|
|
420
481
|
reason: "configuration",
|
|
421
482
|
delta: {
|
|
@@ -547,37 +608,60 @@ class LocalHostedSessionHandle {
|
|
|
547
608
|
return await this.pathAutocompleteLoad;
|
|
548
609
|
}
|
|
549
610
|
async compact(options) {
|
|
550
|
-
this.
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
611
|
+
return await this.runMaintenance(async (signal) => {
|
|
612
|
+
const result = await this.session.compact({
|
|
613
|
+
mode: options.mode === "summary-only" ? "only-summary" : "with-last-assistant",
|
|
614
|
+
...(options.guidance !== undefined ? { guidance: options.guidance } : {}),
|
|
615
|
+
signal,
|
|
616
|
+
});
|
|
617
|
+
signal.throwIfAborted();
|
|
618
|
+
await this.runtimeEventQueue;
|
|
619
|
+
this.reconcileProjections();
|
|
620
|
+
const snapshot = await this.commitSnapshot();
|
|
621
|
+
this.emitSnapshotReset("maintenance", snapshot);
|
|
622
|
+
return {
|
|
623
|
+
snapshot,
|
|
624
|
+
compactionMessage: result.compactionMessage,
|
|
625
|
+
includedLastAssistant: result.includedLastAssistant,
|
|
626
|
+
};
|
|
554
627
|
});
|
|
555
|
-
const snapshot = await this.commitSnapshot();
|
|
556
|
-
this.emitSnapshotReset("maintenance", snapshot);
|
|
557
|
-
return {
|
|
558
|
-
snapshot,
|
|
559
|
-
compactionMessage: result.compactionMessage,
|
|
560
|
-
includedLastAssistant: result.includedLastAssistant,
|
|
561
|
-
};
|
|
562
628
|
}
|
|
563
629
|
async pruneToolResults(options) {
|
|
564
|
-
this.
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
630
|
+
return await this.runMaintenance(async (signal) => {
|
|
631
|
+
const result = await this.session.pruneToolResults({
|
|
632
|
+
strategy: options.strategy,
|
|
633
|
+
fraction: options.fraction,
|
|
634
|
+
...(options.guidance !== undefined ? { guidance: options.guidance } : {}),
|
|
635
|
+
signal,
|
|
636
|
+
});
|
|
637
|
+
signal.throwIfAborted();
|
|
638
|
+
this.reconcileProjections({ prunedToolResults: result.prunedToolResults });
|
|
639
|
+
const snapshot = await this.commitSnapshot();
|
|
640
|
+
this.emitSnapshotReset("maintenance", snapshot);
|
|
641
|
+
return {
|
|
642
|
+
snapshot,
|
|
643
|
+
message: result.message,
|
|
644
|
+
noop: result.noop,
|
|
645
|
+
bashResultsPruned: result.bashResultsPruned,
|
|
646
|
+
editCallsPruned: result.editCallsPruned,
|
|
647
|
+
editResultsPruned: result.editResultsPruned,
|
|
648
|
+
bytesPruned: result.bytesPruned,
|
|
649
|
+
};
|
|
569
650
|
});
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
651
|
+
}
|
|
652
|
+
async runMaintenance(operation) {
|
|
653
|
+
this.assertActive();
|
|
654
|
+
const abortController = new AbortController();
|
|
655
|
+
this.maintenanceAbortControllers.add(abortController);
|
|
656
|
+
const promise = operation(abortController.signal);
|
|
657
|
+
this.maintenancePromises.add(promise);
|
|
658
|
+
try {
|
|
659
|
+
return await promise;
|
|
660
|
+
}
|
|
661
|
+
finally {
|
|
662
|
+
this.maintenanceAbortControllers.delete(abortController);
|
|
663
|
+
this.maintenancePromises.delete(promise);
|
|
664
|
+
}
|
|
581
665
|
}
|
|
582
666
|
async rewindToHistoryEntryId(historyEntryId) {
|
|
583
667
|
this.assertActive();
|
|
@@ -585,6 +669,8 @@ class LocalHostedSessionHandle {
|
|
|
585
669
|
if (!result) {
|
|
586
670
|
throw new Error("rewind failed");
|
|
587
671
|
}
|
|
672
|
+
await this.runtimeEventQueue;
|
|
673
|
+
this.reconcileProjections({ removeMissingAgents: true });
|
|
588
674
|
const snapshot = await this.commitSnapshot();
|
|
589
675
|
this.emitSnapshotReset("maintenance", snapshot);
|
|
590
676
|
return {
|
|
@@ -646,95 +732,172 @@ class LocalHostedSessionHandle {
|
|
|
646
732
|
return await this.commitSnapshot();
|
|
647
733
|
}
|
|
648
734
|
async dispose() {
|
|
735
|
+
if (!this.disposePromise) {
|
|
736
|
+
this.disposing = true;
|
|
737
|
+
this.disposePromise = this.disposeNow();
|
|
738
|
+
}
|
|
739
|
+
return await this.disposePromise;
|
|
740
|
+
}
|
|
741
|
+
async disposeNow() {
|
|
649
742
|
if (this.disposed) {
|
|
650
743
|
return;
|
|
651
744
|
}
|
|
745
|
+
const errors = [];
|
|
746
|
+
this.runtime.interruptTurn();
|
|
747
|
+
this.interruptMaintenance();
|
|
748
|
+
try {
|
|
749
|
+
await this.waitForActiveWork();
|
|
750
|
+
}
|
|
751
|
+
catch (error) {
|
|
752
|
+
errors.push(error);
|
|
753
|
+
}
|
|
652
754
|
this.disposed = true;
|
|
653
755
|
try {
|
|
654
756
|
this.unsubscribeSubagentEvent();
|
|
655
|
-
|
|
757
|
+
}
|
|
758
|
+
catch (error) {
|
|
759
|
+
errors.push(error);
|
|
760
|
+
}
|
|
761
|
+
for (const session of this.ephemeralAgentSessions.values()) {
|
|
762
|
+
try {
|
|
656
763
|
session.dispose();
|
|
657
764
|
}
|
|
658
|
-
|
|
765
|
+
catch (error) {
|
|
766
|
+
errors.push(error);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
this.ephemeralAgentSessions.clear();
|
|
770
|
+
try {
|
|
659
771
|
this.session.dispose();
|
|
772
|
+
}
|
|
773
|
+
catch (error) {
|
|
774
|
+
errors.push(error);
|
|
775
|
+
}
|
|
776
|
+
try {
|
|
660
777
|
await this.executionEnvironment.dispose();
|
|
661
778
|
}
|
|
779
|
+
catch (error) {
|
|
780
|
+
errors.push(error);
|
|
781
|
+
}
|
|
662
782
|
finally {
|
|
663
783
|
this.removeFromHost(this);
|
|
664
784
|
}
|
|
785
|
+
if (errors.length > 0) {
|
|
786
|
+
throw new AggregateError(errors, "failed to dispose hosted session");
|
|
787
|
+
}
|
|
665
788
|
}
|
|
666
789
|
async commitSnapshot() {
|
|
667
790
|
const write = this.snapshotQueue.catch(() => undefined).then(() => this.writeSnapshot());
|
|
668
791
|
this.snapshotQueue = write.catch(() => undefined);
|
|
669
792
|
return await write;
|
|
670
793
|
}
|
|
671
|
-
async commitSnapshotWithRevision(revision
|
|
794
|
+
async commitSnapshotWithRevision(revision) {
|
|
672
795
|
const write = this.snapshotQueue
|
|
673
796
|
.catch(() => undefined)
|
|
674
|
-
.then(() => this.writeSnapshotWithRevision(revision
|
|
797
|
+
.then(() => this.writeSnapshotWithRevision(revision));
|
|
675
798
|
this.snapshotQueue = write.catch(() => undefined);
|
|
676
799
|
return await write;
|
|
677
800
|
}
|
|
678
801
|
async writeSnapshot() {
|
|
679
|
-
this.
|
|
802
|
+
this.assertNotDisposed();
|
|
803
|
+
const generation = this.snapshotGeneration;
|
|
680
804
|
const draft = this.buildSnapshotDraft();
|
|
681
|
-
|
|
682
|
-
await this.store.deleteSession(this.committedSessionId, {
|
|
683
|
-
...(this.persistedSnapshotRevision !== undefined
|
|
684
|
-
? { expectedRevision: this.persistedSnapshotRevision }
|
|
685
|
-
: {}),
|
|
686
|
-
});
|
|
687
|
-
this.committedSessionId = draft.sessionId;
|
|
688
|
-
this.committedSnapshot = undefined;
|
|
689
|
-
this.persistedSnapshotRevision = undefined;
|
|
690
|
-
}
|
|
805
|
+
await this.switchSnapshotSession(draft.sessionId);
|
|
691
806
|
const snapshot = {
|
|
692
807
|
...draft,
|
|
693
808
|
revision: this.nextSnapshotRevision(draft),
|
|
694
809
|
};
|
|
695
|
-
if (this.
|
|
696
|
-
|
|
697
|
-
this.persistedSnapshotRevision === snapshot.revision) {
|
|
698
|
-
return cloneSessionProtocolSnapshot(this.committedSnapshot);
|
|
810
|
+
if (this.persistedSnapshot && isDeepStrictEqual(this.persistedSnapshot, snapshot)) {
|
|
811
|
+
return cloneSessionProtocolSnapshot(this.committedSnapshot ?? snapshot);
|
|
699
812
|
}
|
|
700
813
|
await this.store.commitSessionSnapshot(snapshot, {
|
|
701
|
-
expectedRevision: this.
|
|
814
|
+
expectedRevision: this.persistedSnapshot?.revision ?? 0,
|
|
702
815
|
});
|
|
703
|
-
this.
|
|
816
|
+
this.persistedSnapshot = cloneSessionProtocolSnapshot(snapshot);
|
|
817
|
+
if (generation !== this.snapshotGeneration) {
|
|
818
|
+
return await this.writeSnapshot();
|
|
819
|
+
}
|
|
704
820
|
return cloneSessionProtocolSnapshot(this.updateCommittedSnapshotAfterWrite(snapshot));
|
|
705
821
|
}
|
|
706
|
-
async writeSnapshotWithRevision(revision
|
|
707
|
-
this.
|
|
822
|
+
async writeSnapshotWithRevision(revision) {
|
|
823
|
+
this.assertNotDisposed();
|
|
824
|
+
const generation = this.snapshotGeneration;
|
|
708
825
|
const draft = this.buildSnapshotDraft();
|
|
709
|
-
|
|
710
|
-
if (this.committedSessionId !== draft.sessionId) {
|
|
711
|
-
await this.store.deleteSession(this.committedSessionId, {
|
|
712
|
-
...(this.persistedSnapshotRevision !== undefined
|
|
713
|
-
? { expectedRevision: this.persistedSnapshotRevision }
|
|
714
|
-
: {}),
|
|
715
|
-
});
|
|
716
|
-
this.committedSessionId = draft.sessionId;
|
|
717
|
-
this.committedSnapshot = undefined;
|
|
718
|
-
this.persistedSnapshotRevision = undefined;
|
|
719
|
-
}
|
|
826
|
+
await this.switchSnapshotSession(draft.sessionId);
|
|
720
827
|
const snapshot = {
|
|
721
828
|
...draft,
|
|
722
829
|
revision,
|
|
723
830
|
};
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
831
|
+
await this.store.commitSessionSnapshot(snapshot, {
|
|
832
|
+
expectedRevision: this.persistedSnapshot?.revision ?? 0,
|
|
833
|
+
});
|
|
834
|
+
this.persistedSnapshot = cloneSessionProtocolSnapshot(snapshot);
|
|
835
|
+
if (generation !== this.snapshotGeneration) {
|
|
836
|
+
return await this.writeSnapshot();
|
|
729
837
|
}
|
|
730
838
|
return cloneSessionProtocolSnapshot(this.updateCommittedSnapshotAfterWrite(snapshot));
|
|
731
839
|
}
|
|
840
|
+
async switchSnapshotSession(sessionId) {
|
|
841
|
+
if (this.committedSessionId === sessionId) {
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
await this.store.deleteSession(this.committedSessionId, {
|
|
845
|
+
...(this.persistedSnapshot ? { expectedRevision: this.persistedSnapshot.revision } : {}),
|
|
846
|
+
});
|
|
847
|
+
this.committedSessionId = sessionId;
|
|
848
|
+
this.committedSnapshot = undefined;
|
|
849
|
+
this.persistedSnapshot = undefined;
|
|
850
|
+
}
|
|
732
851
|
updateCommittedSnapshotAfterWrite(snapshot) {
|
|
733
|
-
if (!this.committedSnapshot || this.committedSnapshot.revision
|
|
852
|
+
if (!this.committedSnapshot || this.committedSnapshot.revision < snapshot.revision) {
|
|
734
853
|
this.committedSnapshot = cloneSessionProtocolSnapshot(snapshot);
|
|
854
|
+
this.snapshotGeneration += 1;
|
|
735
855
|
}
|
|
736
856
|
return this.committedSnapshot;
|
|
737
857
|
}
|
|
858
|
+
reconcileProjections(options = {}) {
|
|
859
|
+
const messageIds = new Set(this.session.rawHistoryEntries.map((entry) => entry.id));
|
|
860
|
+
messageIds.add("system");
|
|
861
|
+
for (const [id, tool] of this.tools) {
|
|
862
|
+
if (!messageIds.has(tool.call.messageId) ||
|
|
863
|
+
(tool.resultMessageId !== undefined && !messageIds.has(tool.resultMessageId))) {
|
|
864
|
+
this.tools.delete(id);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (options.removeMissingAgents) {
|
|
868
|
+
for (const id of this.agents.keys()) {
|
|
869
|
+
if (!this.session.hasSubagent(id)) {
|
|
870
|
+
this.agents.delete(id);
|
|
871
|
+
this.agentCostTotals.delete(id);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
const operationIds = new Set(this.timelineExtras.filter((item) => item.type === "operation").map((item) => item.id));
|
|
876
|
+
const prunedByToolId = new Map((options.prunedToolResults ?? []).map((result) => [result.toolCallId, result.content]));
|
|
877
|
+
for (const [id, facet] of this.facets) {
|
|
878
|
+
const subjectExists = facet.subject.type === "session" ||
|
|
879
|
+
(facet.subject.type === "message" && messageIds.has(facet.subject.id)) ||
|
|
880
|
+
(facet.subject.type === "tool" && this.tools.has(facet.subject.id)) ||
|
|
881
|
+
(facet.subject.type === "agent" && this.agents.has(facet.subject.id)) ||
|
|
882
|
+
(facet.subject.type === "operation" && operationIds.has(facet.subject.id));
|
|
883
|
+
if (!subjectExists) {
|
|
884
|
+
this.facets.delete(id);
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
if (facet.subject.type === "tool") {
|
|
888
|
+
const toolCallId = facet.subject.id;
|
|
889
|
+
const content = prunedByToolId.get(toolCallId);
|
|
890
|
+
if (content !== undefined && facet.kind === "tau.tool-ui-events") {
|
|
891
|
+
this.facets.set(id, {
|
|
892
|
+
...facet,
|
|
893
|
+
data: {
|
|
894
|
+
events: [{ type: "tool_pruned", toolCallId, content }],
|
|
895
|
+
},
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
738
901
|
buildSnapshotDraft() {
|
|
739
902
|
const messages = this.buildProtocolMessages();
|
|
740
903
|
return {
|
|
@@ -832,8 +995,10 @@ class LocalHostedSessionHandle {
|
|
|
832
995
|
this.tools.set(id, structuredClone(tool));
|
|
833
996
|
}
|
|
834
997
|
this.agents.clear();
|
|
998
|
+
this.agentCostTotals.clear();
|
|
835
999
|
for (const [id, agent] of Object.entries(snapshot.agents)) {
|
|
836
1000
|
this.agents.set(id, structuredClone(agent));
|
|
1001
|
+
this.agentCostTotals.set(id, agent.costTotal);
|
|
837
1002
|
}
|
|
838
1003
|
this.costTotal = snapshot.costTotal;
|
|
839
1004
|
this.facets.clear();
|
|
@@ -849,6 +1014,11 @@ class LocalHostedSessionHandle {
|
|
|
849
1014
|
}
|
|
850
1015
|
}
|
|
851
1016
|
assertActive() {
|
|
1017
|
+
if (this.disposing || this.disposed) {
|
|
1018
|
+
throw new Error(`session is shut down: ${this.committedSessionId}`);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
assertNotDisposed() {
|
|
852
1022
|
if (this.disposed) {
|
|
853
1023
|
throw new Error(`session is shut down: ${this.committedSessionId}`);
|
|
854
1024
|
}
|
|
@@ -1047,13 +1217,14 @@ class LocalHostedSessionHandle {
|
|
|
1047
1217
|
}
|
|
1048
1218
|
case "compaction_end":
|
|
1049
1219
|
this.clearRunningAutoCompactionOperations();
|
|
1220
|
+
this.reconcileProjections();
|
|
1050
1221
|
await this.emitSnapshotReset("maintenance", await this.commitSnapshot());
|
|
1051
1222
|
return;
|
|
1052
1223
|
case "tool_ui":
|
|
1053
1224
|
await this.recordToolUiEvent(event.uiEvent);
|
|
1054
1225
|
return;
|
|
1055
1226
|
case "subagent_ui":
|
|
1056
|
-
await this.recordSubagentUiEvent(event.event
|
|
1227
|
+
await this.recordSubagentUiEvent(event.event);
|
|
1057
1228
|
return;
|
|
1058
1229
|
}
|
|
1059
1230
|
}
|
|
@@ -1062,6 +1233,10 @@ class LocalHostedSessionHandle {
|
|
|
1062
1233
|
}
|
|
1063
1234
|
async recordToolUiEvent(event) {
|
|
1064
1235
|
const toolCallId = event.toolCallId;
|
|
1236
|
+
const existingTool = this.tools.get(toolCallId);
|
|
1237
|
+
if (!existingTool) {
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1065
1240
|
const facetId = `tool-ui-${toolCallId}`;
|
|
1066
1241
|
const existing = this.facets.get(facetId);
|
|
1067
1242
|
const events = Array.isArray(existing?.data.events) ? existing.data.events : [];
|
|
@@ -1073,18 +1248,10 @@ class LocalHostedSessionHandle {
|
|
|
1073
1248
|
data: { events: [...events, structuredClone(event)] },
|
|
1074
1249
|
};
|
|
1075
1250
|
this.facets.set(facet.id, facet);
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1078
|
-
? {
|
|
1079
|
-
type: "tool.set",
|
|
1080
|
-
tool: this.updateToolRunFromUiEvent(existingTool, event),
|
|
1081
|
-
}
|
|
1082
|
-
: undefined;
|
|
1083
|
-
if (toolChange) {
|
|
1084
|
-
this.tools.set(toolChange.tool.id, toolChange.tool);
|
|
1085
|
-
}
|
|
1251
|
+
const tool = this.updateToolRunFromUiEvent(existingTool, event);
|
|
1252
|
+
this.tools.set(tool.id, tool);
|
|
1086
1253
|
await this.emitPatch("tool-run", [
|
|
1087
|
-
|
|
1254
|
+
{ type: "tool.set", tool },
|
|
1088
1255
|
{ type: "facet.set", facet: structuredClone(facet) },
|
|
1089
1256
|
]);
|
|
1090
1257
|
}
|
|
@@ -1113,13 +1280,18 @@ class LocalHostedSessionHandle {
|
|
|
1113
1280
|
}
|
|
1114
1281
|
return next;
|
|
1115
1282
|
}
|
|
1116
|
-
async recordSubagentUiEvent(event
|
|
1283
|
+
async recordSubagentUiEvent(event) {
|
|
1117
1284
|
const existing = "id" in event ? this.agents.get(event.id) : this.agents.get(event.state.id);
|
|
1118
|
-
const agent = agentRunFromSubagentEvent(event,
|
|
1285
|
+
const agent = agentRunFromSubagentEvent(event, existing);
|
|
1119
1286
|
if (!agent) {
|
|
1120
1287
|
return;
|
|
1121
1288
|
}
|
|
1122
|
-
|
|
1289
|
+
const previousCost = this.agentCostTotals.get(agent.id) ?? 0;
|
|
1290
|
+
this.costTotal += Math.max(0, agent.costTotal - previousCost);
|
|
1291
|
+
this.agentCostTotals.set(agent.id, agent.costTotal);
|
|
1292
|
+
if (!this.session.hasSubagent(agent.id)) {
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1123
1295
|
this.agents.set(agent.id, agent);
|
|
1124
1296
|
await this.emitPatch("agent-run", [
|
|
1125
1297
|
{ type: "cost.set", costTotal: this.costTotal },
|
|
@@ -1165,10 +1337,11 @@ class LocalHostedSessionHandle {
|
|
|
1165
1337
|
});
|
|
1166
1338
|
if (options.persist === false) {
|
|
1167
1339
|
this.committedSnapshot = applySessionProtocolDelta(this.committedSnapshot, delta);
|
|
1340
|
+
this.snapshotGeneration += 1;
|
|
1168
1341
|
this.emitDelta(delta);
|
|
1169
1342
|
return;
|
|
1170
1343
|
}
|
|
1171
|
-
const snapshot = await this.commitSnapshotWithRevision(toRevision
|
|
1344
|
+
const snapshot = await this.commitSnapshotWithRevision(toRevision);
|
|
1172
1345
|
this.emitDelta(createSessionProtocolDeltaMessage({
|
|
1173
1346
|
sessionId: delta.sessionId,
|
|
1174
1347
|
fromRevision,
|
|
@@ -1433,7 +1606,7 @@ function createInterruptedAssistantMessageFromModelSnapshot(draft, model) {
|
|
|
1433
1606
|
timestamp: draft.message.timestamp,
|
|
1434
1607
|
};
|
|
1435
1608
|
}
|
|
1436
|
-
function agentRunFromSubagentEvent(event,
|
|
1609
|
+
function agentRunFromSubagentEvent(event, existing) {
|
|
1437
1610
|
const state = event.type === "subagent_spawned" || event.type === "subagent_finished"
|
|
1438
1611
|
? event.state
|
|
1439
1612
|
: undefined;
|
|
@@ -1467,7 +1640,6 @@ function agentRunFromSubagentEvent(event, originHistoryEntryId, existing) {
|
|
|
1467
1640
|
: state.status === "aborted"
|
|
1468
1641
|
? "cancelled"
|
|
1469
1642
|
: "running",
|
|
1470
|
-
originMessageId: originHistoryEntryId,
|
|
1471
1643
|
...(state.modelLabel !== undefined ? { modelLabel: state.modelLabel } : {}),
|
|
1472
1644
|
costTotal: state.costTotal,
|
|
1473
1645
|
turns: state.turns,
|