@duetso/agent 0.2.4 → 0.2.6
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/package.json +3 -1
- package/dist/src/cli/route.d.ts +4 -8
- package/dist/src/cli/route.d.ts.map +1 -1
- package/dist/src/cli/route.js +26 -49
- package/dist/src/cli/route.js.map +1 -1
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +12 -2
- package/dist/src/cli.js.map +1 -1
- package/dist/src/memory/pglite.d.ts +11 -0
- package/dist/src/memory/pglite.d.ts.map +1 -1
- package/dist/src/memory/pglite.js +77 -4
- package/dist/src/memory/pglite.js.map +1 -1
- package/dist/src/model-routing/advisor-context.d.ts +71 -0
- package/dist/src/model-routing/advisor-context.d.ts.map +1 -0
- package/dist/src/model-routing/advisor-context.js +114 -0
- package/dist/src/model-routing/advisor-context.js.map +1 -0
- package/dist/src/model-routing/advisor-lifecycle.d.ts +27 -0
- package/dist/src/model-routing/advisor-lifecycle.d.ts.map +1 -0
- package/dist/src/model-routing/advisor-lifecycle.js +61 -0
- package/dist/src/model-routing/advisor-lifecycle.js.map +1 -0
- package/dist/src/model-routing/advisor.d.ts +11 -6
- package/dist/src/model-routing/advisor.d.ts.map +1 -1
- package/dist/src/model-routing/advisor.js +16 -4
- package/dist/src/model-routing/advisor.js.map +1 -1
- package/dist/src/model-routing/prompts.d.ts +4 -0
- package/dist/src/model-routing/prompts.d.ts.map +1 -1
- package/dist/src/model-routing/prompts.js +36 -11
- package/dist/src/model-routing/prompts.js.map +1 -1
- package/dist/src/model-routing/router.d.ts +7 -1
- package/dist/src/model-routing/router.d.ts.map +1 -1
- package/dist/src/model-routing/router.js +10 -2
- package/dist/src/model-routing/router.js.map +1 -1
- package/dist/src/model-routing/table.d.ts +2 -5
- package/dist/src/model-routing/table.d.ts.map +1 -1
- package/dist/src/model-routing/table.js +0 -13
- package/dist/src/model-routing/table.js.map +1 -1
- package/dist/src/session/session.js +1 -1
- package/dist/src/turn-runner/prompts.js +1 -1
- package/dist/src/turn-runner/tools.d.ts +16 -12
- package/dist/src/turn-runner/tools.d.ts.map +1 -1
- package/dist/src/turn-runner/tools.js +39 -32
- package/dist/src/turn-runner/tools.js.map +1 -1
- package/dist/src/turn-runner/turn-runner.d.ts +15 -1
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
- package/dist/src/turn-runner/turn-runner.js +110 -15
- package/dist/src/turn-runner/turn-runner.js.map +1 -1
- package/package.json +3 -1
- package/dist/src/model-routing/advisor-transcript.d.ts +0 -36
- package/dist/src/model-routing/advisor-transcript.d.ts.map +0 -1
- package/dist/src/model-routing/advisor-transcript.js +0 -103
- package/dist/src/model-routing/advisor-transcript.js.map +0 -1
|
@@ -6,7 +6,9 @@ import { stripSystemReminders, systemReminder } from "../lib/system-reminder.js"
|
|
|
6
6
|
import { assistantText } from "../core/serializer.js";
|
|
7
7
|
import { classifyRoute } from "../model-routing/classifier.js";
|
|
8
8
|
import { loadRoutingTable } from "../model-routing/loader.js";
|
|
9
|
-
import {
|
|
9
|
+
import { AdvisorTurnLifecycle } from "../model-routing/advisor-lifecycle.js";
|
|
10
|
+
import { ADVISOR_COMPLETION_REVIEW_REMINDER, ADVISOR_EXECUTOR_GUIDANCE_LAYER, ADVISOR_ORIENTATION_REMINDER, } from "../model-routing/prompts.js";
|
|
11
|
+
import { captureAdvisorExecutorContext } from "../model-routing/advisor-context.js";
|
|
10
12
|
import { resolveTierDefault } from "../model-routing/resolve.js";
|
|
11
13
|
import { ModelRouter, } from "../model-routing/router.js";
|
|
12
14
|
import { BUILT_IN_ROUTING_TABLE, isVirtualModel, } from "../model-routing/table.js";
|
|
@@ -14,7 +16,7 @@ import { toXML } from "../lib/xml.js";
|
|
|
14
16
|
import { createObservationalContextTransform, DEFAULT_EFFECTIVE_CONTEXT, estimateTokens, resolveObservationalMemorySettings, stripObservationalContextMessages, updateObservationalMemory, } from "../memory/observational.js";
|
|
15
17
|
import { rebuildMemoryContextPack } from "../memory/context-pack.js";
|
|
16
18
|
import { createEmbeddingClient } from "../memory/embedding.js";
|
|
17
|
-
import { loadStoredMemory,
|
|
19
|
+
import { loadStoredMemory, replaceSessionObservations, } from "../memory/storage.js";
|
|
18
20
|
import { MemoryContextCache } from "../memory/store.js";
|
|
19
21
|
import { DEFAULT_CLI_MEMORY_MODEL, DEFAULT_CLI_MODEL, resolveModelName, routingCatalogAdapter, } from "../model-resolution/resolver.js";
|
|
20
22
|
import { DEFAULT_TASK_WAIT_BUDGET_MS } from "../types/config.js";
|
|
@@ -25,7 +27,7 @@ import { applyEvictionHorizon, calculateWireTokens, createInitialHorizon, findEv
|
|
|
25
27
|
import { createDefaultTurnRunnerTools, createTurnRunnerTools, resolveStateCwd, formatCarriedTodosReminder, formatStateMachineTerminalAcknowledgmentPrompt, isTurnRunnerControlResult, MINIMUM_STATE_MACHINE_DELAY_MS, } from "./tools.js";
|
|
26
28
|
import { connectMcpServers } from "./mcp.js";
|
|
27
29
|
import { SkillContext } from "./skill-context.js";
|
|
28
|
-
import { currentParkState } from "./state-machine-session.js";
|
|
30
|
+
import { currentParkState, currentScheduledState } from "./state-machine-session.js";
|
|
29
31
|
import { failActiveSession, markTerminalAcknowledged, planDecision, planWake, recordPlannedTerminal, recordScheduled, recordSettled, repeatedSelectionLoopCount, startSession as startStateMachineSession, supersede, } from "./state-machine-decisions.js";
|
|
30
32
|
import { createSubagentExecutor, classifySpawnModel, } from "./subagent.js";
|
|
31
33
|
import { completeTurn, copyOptionalArray, createInitialTurnState } from "./turn-state.js";
|
|
@@ -145,6 +147,8 @@ export class TurnRunner {
|
|
|
145
147
|
pendingModelSelection;
|
|
146
148
|
/** Advisor target and transcript budget selected with the current virtual tier. */
|
|
147
149
|
advisorPolicy;
|
|
150
|
+
/** Product-owned early/final consultation checkpoints for the active public agent-mode turn. */
|
|
151
|
+
advisorTurnLifecycle;
|
|
148
152
|
/** True only while the parent pi agent is actively producing the public terminal event. */
|
|
149
153
|
parentAgentRunning = false;
|
|
150
154
|
/** Control results captured during the current parent pass. More than one is a protocol error. */
|
|
@@ -541,6 +545,11 @@ export class TurnRunner {
|
|
|
541
545
|
this.interruptCleanup = undefined;
|
|
542
546
|
this.terminalEmitted = false;
|
|
543
547
|
this.activeRootScopeId = `turn-${this.nextRootScope++}`;
|
|
548
|
+
const routeStatus = this.modelRouter?.status();
|
|
549
|
+
this.advisorTurnLifecycle =
|
|
550
|
+
this.requireRunnerState().mode === "agent" && this.advisorPolicy?.enabled && routeStatus
|
|
551
|
+
? new AdvisorTurnLifecycle(routeStatus.assistantSteps)
|
|
552
|
+
: undefined;
|
|
544
553
|
this.turnTools = this.createTools(this.requireRunnerState().mode).tools;
|
|
545
554
|
this.enqueueParentInput(command.type === "wake" ? { type: "wake" } : { type: "user_command", command });
|
|
546
555
|
const carriedUserCommands = this.hydratedQueuedCommands?.filter((queued) => queued.type !== "wake");
|
|
@@ -616,6 +625,8 @@ export class TurnRunner {
|
|
|
616
625
|
});
|
|
617
626
|
}
|
|
618
627
|
if (result?.type === "terminal") {
|
|
628
|
+
if (this.queueAdvisorCompletionReviewIfDue(result.status))
|
|
629
|
+
continue;
|
|
619
630
|
completion = {
|
|
620
631
|
status: result.status === "error" ? "failed" : "completed",
|
|
621
632
|
...(result.result !== undefined ? { result: result.result } : {}),
|
|
@@ -697,6 +708,7 @@ export class TurnRunner {
|
|
|
697
708
|
this.setState(terminal.state);
|
|
698
709
|
this.emitTerminalOnce(terminal);
|
|
699
710
|
this.turnTools = undefined;
|
|
711
|
+
this.advisorTurnLifecycle = undefined;
|
|
700
712
|
this.activeRootScopeId = undefined;
|
|
701
713
|
this.turnUsage = undefined;
|
|
702
714
|
this.turnUsageByModel = undefined;
|
|
@@ -937,6 +949,8 @@ export class TurnRunner {
|
|
|
937
949
|
return this.selectNextStateAfterCompletion(input.stateName, input.output);
|
|
938
950
|
case "terminal_acknowledgment":
|
|
939
951
|
return this.runTerminalAcknowledgmentPass();
|
|
952
|
+
case "advisor_completion_review":
|
|
953
|
+
return this.runAdvisorCompletionReviewPass();
|
|
940
954
|
case "wake":
|
|
941
955
|
return this.runWakeInput();
|
|
942
956
|
}
|
|
@@ -1051,6 +1065,48 @@ export class TurnRunner {
|
|
|
1051
1065
|
return { type: "interrupted" };
|
|
1052
1066
|
return this.stateMachineResultFromWorker(worker, worker.outcome.state);
|
|
1053
1067
|
}
|
|
1068
|
+
async runAdvisorCompletionReviewPass() {
|
|
1069
|
+
const worker = await this.runParentPass({
|
|
1070
|
+
state: this.snapshotState({ ...this.requireRunnerState(), status: "running" }),
|
|
1071
|
+
prompt: systemReminder(ADVISOR_COMPLETION_REVIEW_REMINDER),
|
|
1072
|
+
continuation: true,
|
|
1073
|
+
});
|
|
1074
|
+
this.setState(worker.outcome.state);
|
|
1075
|
+
if (worker.outcome.type === "interrupted")
|
|
1076
|
+
return { type: "interrupted" };
|
|
1077
|
+
if (worker.control.type === "ask_user_question") {
|
|
1078
|
+
return { type: "ask", questions: worker.control.questions };
|
|
1079
|
+
}
|
|
1080
|
+
return worker.outcome.status === "failed"
|
|
1081
|
+
? { type: "terminal", status: "error", error: worker.outcome.error }
|
|
1082
|
+
: { type: "terminal", status: "completed", result: worker.outcome.result };
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Queue a fresh parent continuation only when a completed agent-mode turn did enough work to
|
|
1086
|
+
* benefit from review. A successful consultation with no subsequent real-world tool work already
|
|
1087
|
+
* saw the final available evidence and satisfies this checkpoint without call counting. Otherwise
|
|
1088
|
+
* completion starts a distinct consultation phase, so its first call gets a fresh ordinary floor
|
|
1089
|
+
* even when the orientation call was recent. The checkpoint is marked before enqueueing so
|
|
1090
|
+
* failures and ignored reminders remain non-recursive.
|
|
1091
|
+
*/
|
|
1092
|
+
queueAdvisorCompletionReviewIfDue(status) {
|
|
1093
|
+
const lifecycle = this.advisorTurnLifecycle;
|
|
1094
|
+
const routeStatus = this.modelRouter?.status();
|
|
1095
|
+
if (status !== "completed" ||
|
|
1096
|
+
!lifecycle ||
|
|
1097
|
+
!routeStatus ||
|
|
1098
|
+
this.taskManager.pendingWork().kind === "open" ||
|
|
1099
|
+
!lifecycle.takeCompletionCheckpoint(routeStatus.assistantSteps)) {
|
|
1100
|
+
return false;
|
|
1101
|
+
}
|
|
1102
|
+
// Completion review is a distinct product lifecycle phase, just like a
|
|
1103
|
+
// replacement model starts a fresh phase after a route switch. Reset the
|
|
1104
|
+
// step floor instead of bypassing the advisor gate so the ordinary
|
|
1105
|
+
// in-flight reservation and post-success cooldown still apply.
|
|
1106
|
+
this.modelRouter?.resetAdvisorCooldown();
|
|
1107
|
+
this.parentInputs.unshift({ type: "advisor_completion_review" });
|
|
1108
|
+
return true;
|
|
1109
|
+
}
|
|
1054
1110
|
async executePlannedWork(work) {
|
|
1055
1111
|
if ("terminal" in work) {
|
|
1056
1112
|
const settled = recordPlannedTerminal(this.requireStateMachine(), work.terminal);
|
|
@@ -1265,6 +1321,19 @@ export class TurnRunner {
|
|
|
1265
1321
|
return metadata.shell.partialOutput();
|
|
1266
1322
|
}
|
|
1267
1323
|
async interruptTasks(reason) {
|
|
1324
|
+
const scheduledState = currentScheduledState(this.stateMachine);
|
|
1325
|
+
const scheduledTask = scheduledState
|
|
1326
|
+
? this.taskManager
|
|
1327
|
+
.list()
|
|
1328
|
+
.find((task) => task.kind === "scheduled" &&
|
|
1329
|
+
task.status === "scheduled" &&
|
|
1330
|
+
task.name === scheduledState.name)
|
|
1331
|
+
: undefined;
|
|
1332
|
+
if (scheduledState && scheduledTask) {
|
|
1333
|
+
const recorded = recordSettled(this.requireStateMachine(), scheduledState.name, scheduledState.kind, { type: "interrupted", reason }, undefined, this.clock.now());
|
|
1334
|
+
this.setStateMachine(recorded.session);
|
|
1335
|
+
this.ignoredTaskSettlements.add(scheduledTask.id);
|
|
1336
|
+
}
|
|
1268
1337
|
for (const [id, metadata] of this.stateTasks) {
|
|
1269
1338
|
if (this.taskManager.output(id)?.descriptor.status !== "running")
|
|
1270
1339
|
continue;
|
|
@@ -1814,21 +1883,26 @@ export class TurnRunner {
|
|
|
1814
1883
|
createAskAdvisorStorage(router, policy) {
|
|
1815
1884
|
const resolveAdvisorModel = () => resolveModelName(policy.target.modelName);
|
|
1816
1885
|
return {
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1886
|
+
getContext: async () => {
|
|
1887
|
+
const agent = this.requireParentAgent();
|
|
1888
|
+
return await captureAdvisorExecutorContext(agent);
|
|
1889
|
+
},
|
|
1890
|
+
resolveModel: () => {
|
|
1891
|
+
const model = resolveAdvisorModel();
|
|
1892
|
+
return {
|
|
1893
|
+
modelName: model.id,
|
|
1894
|
+
contextWindowTokens: model.contextWindow,
|
|
1895
|
+
acceptsImages: model.input.includes("image"),
|
|
1896
|
+
};
|
|
1826
1897
|
},
|
|
1827
|
-
budgetTokens: policy.transcriptTokens,
|
|
1828
|
-
modelName: () => resolveAdvisorModel().id,
|
|
1829
1898
|
thinkingLevel: policy.target.thinkingLevel,
|
|
1830
1899
|
advisorGate: () => router.beginAdvisorConsult(),
|
|
1831
|
-
noteAdvisorConsult: (success = true) =>
|
|
1900
|
+
noteAdvisorConsult: (success = true) => {
|
|
1901
|
+
router.endAdvisorConsult(success);
|
|
1902
|
+
if (success && this.advisorTurnLifecycle) {
|
|
1903
|
+
this.advisorTurnLifecycle.noteSuccessfulConsult(router.status().assistantSteps);
|
|
1904
|
+
}
|
|
1905
|
+
},
|
|
1832
1906
|
recordUsage: (usage) => {
|
|
1833
1907
|
const model = resolveAdvisorModel();
|
|
1834
1908
|
this.recordAndEmitUsage(usageFromAiSdk(usage, model), model.id);
|
|
@@ -1841,6 +1915,14 @@ export class TurnRunner {
|
|
|
1841
1915
|
message: `Advisor usage accounting failed; advice was retained: ${reason}`,
|
|
1842
1916
|
});
|
|
1843
1917
|
},
|
|
1918
|
+
onAdvisorError: (error) => {
|
|
1919
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1920
|
+
this.emit({
|
|
1921
|
+
type: "system",
|
|
1922
|
+
level: "warn",
|
|
1923
|
+
message: `Advisor consultation failed; executor continued: ${reason}`,
|
|
1924
|
+
});
|
|
1925
|
+
},
|
|
1844
1926
|
};
|
|
1845
1927
|
}
|
|
1846
1928
|
/**
|
|
@@ -2374,6 +2456,8 @@ export class TurnRunner {
|
|
|
2374
2456
|
this.emitAgentEvent(event);
|
|
2375
2457
|
if (event.type === "turn_end" && event.message.role === "assistant") {
|
|
2376
2458
|
this.modelRouter?.noteAssistantStep(routerStepObservation(event.message, event.toolResults));
|
|
2459
|
+
this.advisorTurnLifecycle?.noteExecutedTools(event.toolResults.map((result) => result.toolName));
|
|
2460
|
+
this.steerAdvisorOrientationIfDue();
|
|
2377
2461
|
}
|
|
2378
2462
|
if (event.type !== "message_end" || event.message.role !== "assistant")
|
|
2379
2463
|
return;
|
|
@@ -2395,6 +2479,17 @@ export class TurnRunner {
|
|
|
2395
2479
|
if (event.message.usage.totalTokens > 0)
|
|
2396
2480
|
this.emitTurnUsage();
|
|
2397
2481
|
}
|
|
2482
|
+
/** Deliver one in-transcript orientation checkpoint without changing the public protocol. */
|
|
2483
|
+
steerAdvisorOrientationIfDue() {
|
|
2484
|
+
const lifecycle = this.advisorTurnLifecycle;
|
|
2485
|
+
const routeStatus = this.modelRouter?.status();
|
|
2486
|
+
if (!lifecycle ||
|
|
2487
|
+
!routeStatus ||
|
|
2488
|
+
!lifecycle.takeOrientationCheckpoint(routeStatus.assistantSteps)) {
|
|
2489
|
+
return;
|
|
2490
|
+
}
|
|
2491
|
+
this.requireParentAgent().steer(buildUserAgentMessage(systemReminder(ADVISOR_ORIENTATION_REMINDER), undefined));
|
|
2492
|
+
}
|
|
2398
2493
|
/**
|
|
2399
2494
|
* Re-emit the context-window breakdown after `compact()` advances the
|
|
2400
2495
|
* eviction horizon. The bar's numerator is the latest parent
|