@markusylisiurunen/tau 0.3.43 → 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 +5 -8
- package/dist/core/agent/agent_runtime.js +23 -8
- package/dist/core/agent/agent_runtime.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/subagents/agent_supervisor.js +1 -1
- 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/tools/catalog.js +2 -2
- package/dist/core/tools/spawn_agent.js +30 -40
- package/dist/core/tools/spawn_agent.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 +140 -28
- 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 +2 -7
- package/dist/host/session_protocol_handler.js.map +1 -1
- package/dist/protocol/index.d.ts +1 -1
- package/dist/protocol/index.js.map +1 -1
- package/dist/protocol/session_protocol.d.ts +13 -1
- package/dist/protocol/session_protocol.js +57 -3
- package/dist/protocol/session_protocol.js.map +1 -1
- package/dist/sdk/index.d.ts +4 -2
- package/dist/sdk/index.js +9 -0
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/types.d.ts +2 -1
- package/dist/store/session_snapshot_migrations.js +21 -1
- package/dist/store/session_snapshot_migrations.js.map +1 -1
- package/package.json +1 -1
|
@@ -13,7 +13,7 @@ import { hasGoalTurnMetadata } from "../core/utils/user_metadata.js";
|
|
|
13
13
|
import { applySessionProtocolDelta, createSessionProtocolDeltaMessage, createSessionProtocolEphemeralMessage, projectSessionProtocolNoticePresentation, } from "../protocol/session_protocol.js";
|
|
14
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;
|
|
@@ -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,
|
|
@@ -368,6 +368,8 @@ class LocalHostedSessionHandle {
|
|
|
368
368
|
draftAssistantMessage;
|
|
369
369
|
messageStates = new Map();
|
|
370
370
|
timeline = { epoch: 1, sequence: 0, items: [] };
|
|
371
|
+
turns = new Map();
|
|
372
|
+
pendingAcceptedTurnHistoryEntryIds = new Set();
|
|
371
373
|
tools = new Map();
|
|
372
374
|
operations = new Map();
|
|
373
375
|
agents = new Map();
|
|
@@ -485,19 +487,14 @@ class LocalHostedSessionHandle {
|
|
|
485
487
|
this.pendingGoalCommit = goal;
|
|
486
488
|
});
|
|
487
489
|
try {
|
|
488
|
-
const { userHistoryEntryId } = await this.
|
|
490
|
+
const { userHistoryEntryId } = await this.acceptTurn({
|
|
489
491
|
text: prependGoalPolicy(goal.objective, goal),
|
|
490
492
|
});
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
}
|
|
497
|
-
return {
|
|
498
|
-
userHistoryEntryId,
|
|
499
|
-
turn: await this.runTurnNow(logicalTurn, userHistoryEntryId),
|
|
500
|
-
};
|
|
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 };
|
|
501
498
|
}
|
|
502
499
|
catch (error) {
|
|
503
500
|
if (this.pendingGoalCommit === goal) {
|
|
@@ -526,13 +523,14 @@ class LocalHostedSessionHandle {
|
|
|
526
523
|
await this.blockActiveGoal();
|
|
527
524
|
return { turn: abortedTurnOutcome() };
|
|
528
525
|
}
|
|
529
|
-
const { userHistoryEntryId } = await this.
|
|
526
|
+
const { userHistoryEntryId } = await this.acceptTurn({
|
|
530
527
|
text: buildGoalContinuationText(goal),
|
|
531
528
|
});
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
529
|
+
const turn = logicalTurn.cancellationRequested
|
|
530
|
+
? await this.cancelLogicalTurn()
|
|
531
|
+
: await this.runTurnNow(logicalTurn, userHistoryEntryId);
|
|
532
|
+
await this.settleTurn(userHistoryEntryId, turn);
|
|
533
|
+
return { turn };
|
|
536
534
|
}
|
|
537
535
|
catch (error) {
|
|
538
536
|
await this.blockActiveGoal().catch(() => undefined);
|
|
@@ -550,15 +548,83 @@ class LocalHostedSessionHandle {
|
|
|
550
548
|
}
|
|
551
549
|
async record(options) {
|
|
552
550
|
this.assertActive();
|
|
553
|
-
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 });
|
|
554
560
|
return {
|
|
555
561
|
snapshot: await this.snapshot(),
|
|
556
562
|
userHistoryEntryId,
|
|
557
563
|
};
|
|
558
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
|
+
}
|
|
559
606
|
async runTurn() {
|
|
560
607
|
return await this.runLogicalTurn((logicalTurn) => this.runTurnNow(logicalTurn));
|
|
561
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
|
+
}
|
|
562
628
|
async retryTurn() {
|
|
563
629
|
const userMessage = this.session.rawHistoryEntries.findLast((entry) => entry.message.role === "user");
|
|
564
630
|
if (userMessage && hasGoalTurnMetadata(userMessage.message)) {
|
|
@@ -629,7 +695,7 @@ class LocalHostedSessionHandle {
|
|
|
629
695
|
}
|
|
630
696
|
await this.emitSnapshotResetIfChanged("assistant-message");
|
|
631
697
|
});
|
|
632
|
-
this.resolveCommittedLogicalSteering(committedSteering, initialOutcome);
|
|
698
|
+
await this.resolveCommittedLogicalSteering(committedSteering, initialOutcome);
|
|
633
699
|
if (logicalTurn.cancellationRequested) {
|
|
634
700
|
return await this.cancelLogicalTurn();
|
|
635
701
|
}
|
|
@@ -644,7 +710,7 @@ class LocalHostedSessionHandle {
|
|
|
644
710
|
catch (error) {
|
|
645
711
|
try {
|
|
646
712
|
const outcome = await this.cleanupFailedTurn(rootUserMessage.id, error);
|
|
647
|
-
this.resolveCommittedLogicalSteering(committedSteering, outcome);
|
|
713
|
+
await this.resolveCommittedLogicalSteering(committedSteering, outcome);
|
|
648
714
|
return outcome;
|
|
649
715
|
}
|
|
650
716
|
catch {
|
|
@@ -700,10 +766,11 @@ class LocalHostedSessionHandle {
|
|
|
700
766
|
applied: submission.applied.then((association) => ({
|
|
701
767
|
userHistoryEntryId: association.historyEntryId,
|
|
702
768
|
})),
|
|
703
|
-
result: submission.result.then((association) =>
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
769
|
+
result: submission.result.then(async (association) => {
|
|
770
|
+
const turn = turnOutcomeFromResult(association.result, association.result.finalMessage);
|
|
771
|
+
await this.settleTurn(association.historyEntryId, turn);
|
|
772
|
+
return { userHistoryEntryId: association.historyEntryId, turn };
|
|
773
|
+
}),
|
|
707
774
|
};
|
|
708
775
|
}
|
|
709
776
|
const logicalTurn = this.activeLogicalTurn;
|
|
@@ -1170,7 +1237,14 @@ class LocalHostedSessionHandle {
|
|
|
1170
1237
|
if (goal?.status !== "active") {
|
|
1171
1238
|
throw new Error("cannot commit logical steering without an active goal");
|
|
1172
1239
|
}
|
|
1173
|
-
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
|
+
}
|
|
1174
1248
|
for (const submission of submissions) {
|
|
1175
1249
|
submission.resolveApplied({ userHistoryEntryId: historyEntryId });
|
|
1176
1250
|
}
|
|
@@ -1185,8 +1259,9 @@ class LocalHostedSessionHandle {
|
|
|
1185
1259
|
throw error;
|
|
1186
1260
|
}
|
|
1187
1261
|
}
|
|
1188
|
-
resolveCommittedLogicalSteering(committed, turn) {
|
|
1262
|
+
async resolveCommittedLogicalSteering(committed, turn) {
|
|
1189
1263
|
for (const { historyEntryId, submissions } of committed) {
|
|
1264
|
+
await this.settleTurn(historyEntryId, turn);
|
|
1190
1265
|
for (const submission of submissions) {
|
|
1191
1266
|
submission.resolveResult({ userHistoryEntryId: historyEntryId, turn });
|
|
1192
1267
|
}
|
|
@@ -1214,7 +1289,7 @@ class LocalHostedSessionHandle {
|
|
|
1214
1289
|
}
|
|
1215
1290
|
await this.emitSnapshotResetIfChanged("assistant-message");
|
|
1216
1291
|
});
|
|
1217
|
-
this.resolveCommittedLogicalSteering(committedSteering, outcome);
|
|
1292
|
+
await this.resolveCommittedLogicalSteering(committedSteering, outcome);
|
|
1218
1293
|
return outcome;
|
|
1219
1294
|
}
|
|
1220
1295
|
async commitSnapshot() {
|
|
@@ -1378,6 +1453,7 @@ class LocalHostedSessionHandle {
|
|
|
1378
1453
|
catalog: structuredClone(this.catalog),
|
|
1379
1454
|
executionEnvironment: this.executionEnvironment.snapshot(),
|
|
1380
1455
|
messages,
|
|
1456
|
+
turns: Object.fromEntries(this.turns),
|
|
1381
1457
|
timeline: structuredClone(this.timeline),
|
|
1382
1458
|
tools: Object.fromEntries(this.tools),
|
|
1383
1459
|
operations: Object.fromEntries(this.operations),
|
|
@@ -1468,6 +1544,10 @@ class LocalHostedSessionHandle {
|
|
|
1468
1544
|
return;
|
|
1469
1545
|
}
|
|
1470
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
|
+
}
|
|
1471
1551
|
this.tools.clear();
|
|
1472
1552
|
for (const [id, tool] of Object.entries(snapshot.tools)) {
|
|
1473
1553
|
this.tools.set(id, structuredClone(tool));
|
|
@@ -1721,12 +1801,24 @@ class LocalHostedSessionHandle {
|
|
|
1721
1801
|
}
|
|
1722
1802
|
case "user_message": {
|
|
1723
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
|
+
}
|
|
1724
1815
|
const timelineItem = this.appendMessageTimelineItem(event.historyEntryId, event.message.timestamp);
|
|
1725
1816
|
await this.emitPatch("user-message", [
|
|
1726
1817
|
this.agentStateChange(),
|
|
1727
1818
|
...(pendingGoal
|
|
1728
1819
|
? [{ type: "goal.set", goal: structuredClone(pendingGoal) }]
|
|
1729
1820
|
: []),
|
|
1821
|
+
...(turn ? [{ type: "turn.set", turn }] : []),
|
|
1730
1822
|
{
|
|
1731
1823
|
type: "message.append",
|
|
1732
1824
|
message: {
|
|
@@ -1738,6 +1830,9 @@ class LocalHostedSessionHandle {
|
|
|
1738
1830
|
},
|
|
1739
1831
|
{ type: "timeline.append", item: structuredClone(timelineItem) },
|
|
1740
1832
|
], { persist: true });
|
|
1833
|
+
if (turn) {
|
|
1834
|
+
this.pendingAcceptedTurnHistoryEntryIds.delete(event.historyEntryId);
|
|
1835
|
+
}
|
|
1741
1836
|
await this.recordHistoryFailure(this.history.append(this.sessionId, [userHistoryEntry(event.historyEntryId, event.message)], this.historyRemote));
|
|
1742
1837
|
if (pendingGoal && this.pendingGoalCommit === pendingGoal) {
|
|
1743
1838
|
this.pendingGoalCommit = undefined;
|
|
@@ -2266,6 +2361,13 @@ class LocalHostedSessionHandle {
|
|
|
2266
2361
|
return { status: "failed", stopReason: "error", errorMessage: diagnostic };
|
|
2267
2362
|
}
|
|
2268
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
|
+
}
|
|
2269
2371
|
function formatErrorDiagnostic(error) {
|
|
2270
2372
|
const messages = [];
|
|
2271
2373
|
const seen = new Set();
|
|
@@ -2371,6 +2473,16 @@ function normalizeRecoveredSnapshot(snapshot) {
|
|
|
2371
2473
|
const legacyAgentState = recovered.agentState.modelContextKey === LEGACY_SESSION_MODEL_CONTEXT_KEY;
|
|
2372
2474
|
let changed = recovered.lifecycle !== "idle" || legacyAgentState;
|
|
2373
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
|
+
}
|
|
2374
2486
|
if (recovered.goal?.status === "active") {
|
|
2375
2487
|
changed = true;
|
|
2376
2488
|
recovered.goal.status = "blocked";
|