@duetso/agent 0.2.6 → 0.3.1

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.
Files changed (221) hide show
  1. package/README.md +29 -16
  2. package/dist/package.json +8 -3
  3. package/dist/src/cli/auto-upgrade.d.ts.map +1 -1
  4. package/dist/src/cli/auto-upgrade.js +7 -54
  5. package/dist/src/cli/auto-upgrade.js.map +1 -1
  6. package/dist/src/cli/connect.d.ts +45 -0
  7. package/dist/src/cli/connect.d.ts.map +1 -0
  8. package/dist/src/cli/connect.js +265 -0
  9. package/dist/src/cli/connect.js.map +1 -0
  10. package/dist/src/cli/help.d.ts +1 -0
  11. package/dist/src/cli/help.d.ts.map +1 -1
  12. package/dist/src/cli/help.js +66 -32
  13. package/dist/src/cli/help.js.map +1 -1
  14. package/dist/src/cli/memory-add.d.ts +18 -13
  15. package/dist/src/cli/memory-add.d.ts.map +1 -1
  16. package/dist/src/cli/memory-add.js +91 -32
  17. package/dist/src/cli/memory-add.js.map +1 -1
  18. package/dist/src/cli/memory-recall.d.ts.map +1 -1
  19. package/dist/src/cli/memory-recall.js +3 -0
  20. package/dist/src/cli/memory-recall.js.map +1 -1
  21. package/dist/src/cli/memory-sources.d.ts +20 -0
  22. package/dist/src/cli/memory-sources.d.ts.map +1 -0
  23. package/dist/src/cli/memory-sources.js +40 -0
  24. package/dist/src/cli/memory-sources.js.map +1 -0
  25. package/dist/src/cli/model.d.ts +5 -0
  26. package/dist/src/cli/model.d.ts.map +1 -1
  27. package/dist/src/cli/model.js +43 -0
  28. package/dist/src/cli/model.js.map +1 -1
  29. package/dist/src/cli/route.d.ts +12 -0
  30. package/dist/src/cli/route.d.ts.map +1 -1
  31. package/dist/src/cli/route.js +39 -9
  32. package/dist/src/cli/route.js.map +1 -1
  33. package/dist/src/cli/rpc.d.ts +3 -2
  34. package/dist/src/cli/rpc.d.ts.map +1 -1
  35. package/dist/src/cli/rpc.js +3 -2
  36. package/dist/src/cli/rpc.js.map +1 -1
  37. package/dist/src/cli/run.d.ts +1 -0
  38. package/dist/src/cli/run.d.ts.map +1 -1
  39. package/dist/src/cli/run.js +1 -0
  40. package/dist/src/cli/run.js.map +1 -1
  41. package/dist/src/cli/shared.d.ts +11 -0
  42. package/dist/src/cli/shared.d.ts.map +1 -1
  43. package/dist/src/cli/shared.js +31 -0
  44. package/dist/src/cli/shared.js.map +1 -1
  45. package/dist/src/cli/train.d.ts +31 -7
  46. package/dist/src/cli/train.d.ts.map +1 -1
  47. package/dist/src/cli/train.js +233 -79
  48. package/dist/src/cli/train.js.map +1 -1
  49. package/dist/src/cli.d.ts +3 -1
  50. package/dist/src/cli.d.ts.map +1 -1
  51. package/dist/src/cli.js +9 -1
  52. package/dist/src/cli.js.map +1 -1
  53. package/dist/src/connected-providers/billing.d.ts +6 -0
  54. package/dist/src/connected-providers/billing.d.ts.map +1 -0
  55. package/dist/src/connected-providers/billing.js +11 -0
  56. package/dist/src/connected-providers/billing.js.map +1 -0
  57. package/dist/src/connected-providers/capability-probe.d.ts +17 -0
  58. package/dist/src/connected-providers/capability-probe.d.ts.map +1 -0
  59. package/dist/src/connected-providers/capability-probe.js +92 -0
  60. package/dist/src/connected-providers/capability-probe.js.map +1 -0
  61. package/dist/src/connected-providers/fake-issuer.d.ts +61 -0
  62. package/dist/src/connected-providers/fake-issuer.d.ts.map +1 -0
  63. package/dist/src/connected-providers/fake-issuer.js +161 -0
  64. package/dist/src/connected-providers/fake-issuer.js.map +1 -0
  65. package/dist/src/connected-providers/fallback-notice.d.ts +11 -0
  66. package/dist/src/connected-providers/fallback-notice.d.ts.map +1 -0
  67. package/dist/src/connected-providers/fallback-notice.js +20 -0
  68. package/dist/src/connected-providers/fallback-notice.js.map +1 -0
  69. package/dist/src/connected-providers/fallback.d.ts +11 -0
  70. package/dist/src/connected-providers/fallback.d.ts.map +1 -0
  71. package/dist/src/connected-providers/fallback.js +37 -0
  72. package/dist/src/connected-providers/fallback.js.map +1 -0
  73. package/dist/src/connected-providers/refresh.d.ts +12 -0
  74. package/dist/src/connected-providers/refresh.d.ts.map +1 -0
  75. package/dist/src/connected-providers/refresh.js +72 -0
  76. package/dist/src/connected-providers/refresh.js.map +1 -0
  77. package/dist/src/connected-providers/registry.d.ts +17 -0
  78. package/dist/src/connected-providers/registry.d.ts.map +1 -0
  79. package/dist/src/connected-providers/registry.js +30 -0
  80. package/dist/src/connected-providers/registry.js.map +1 -0
  81. package/dist/src/connected-providers/store.d.ts +41 -0
  82. package/dist/src/connected-providers/store.d.ts.map +1 -0
  83. package/dist/src/connected-providers/store.js +145 -0
  84. package/dist/src/connected-providers/store.js.map +1 -0
  85. package/dist/src/connected-providers/tokens.d.ts +42 -0
  86. package/dist/src/connected-providers/tokens.d.ts.map +1 -0
  87. package/dist/src/connected-providers/tokens.js +148 -0
  88. package/dist/src/connected-providers/tokens.js.map +1 -0
  89. package/dist/src/connected-providers/transport-preference.d.ts +26 -0
  90. package/dist/src/connected-providers/transport-preference.d.ts.map +1 -0
  91. package/dist/src/connected-providers/transport-preference.js +42 -0
  92. package/dist/src/connected-providers/transport-preference.js.map +1 -0
  93. package/dist/src/core/structured-output.d.ts.map +1 -1
  94. package/dist/src/core/structured-output.js +2 -1
  95. package/dist/src/core/structured-output.js.map +1 -1
  96. package/dist/src/file-lock.d.ts +22 -0
  97. package/dist/src/file-lock.d.ts.map +1 -0
  98. package/dist/src/file-lock.js +69 -0
  99. package/dist/src/file-lock.js.map +1 -0
  100. package/dist/src/memory/context-pack.d.ts +5 -0
  101. package/dist/src/memory/context-pack.d.ts.map +1 -1
  102. package/dist/src/memory/context-pack.js +6 -0
  103. package/dist/src/memory/context-pack.js.map +1 -1
  104. package/dist/src/memory/id.d.ts +3 -0
  105. package/dist/src/memory/id.d.ts.map +1 -0
  106. package/dist/src/memory/id.js +6 -0
  107. package/dist/src/memory/id.js.map +1 -0
  108. package/dist/src/memory/observational-prompts.d.ts +2 -0
  109. package/dist/src/memory/observational-prompts.d.ts.map +1 -1
  110. package/dist/src/memory/observational-prompts.js +4 -2
  111. package/dist/src/memory/observational-prompts.js.map +1 -1
  112. package/dist/src/memory/observational.d.ts +22 -0
  113. package/dist/src/memory/observational.d.ts.map +1 -1
  114. package/dist/src/memory/observational.js +65 -22
  115. package/dist/src/memory/observational.js.map +1 -1
  116. package/dist/src/memory/paths.d.ts +9 -0
  117. package/dist/src/memory/paths.d.ts.map +1 -0
  118. package/dist/src/memory/paths.js +11 -0
  119. package/dist/src/memory/paths.js.map +1 -0
  120. package/dist/src/memory/storage.d.ts.map +1 -1
  121. package/dist/src/memory/storage.js +1 -4
  122. package/dist/src/memory/storage.js.map +1 -1
  123. package/dist/src/memory/store/discovery.d.ts +7 -0
  124. package/dist/src/memory/store/discovery.d.ts.map +1 -0
  125. package/dist/src/memory/store/discovery.js +34 -0
  126. package/dist/src/memory/store/discovery.js.map +1 -0
  127. package/dist/src/memory/store/file.d.ts +52 -0
  128. package/dist/src/memory/store/file.d.ts.map +1 -0
  129. package/dist/src/memory/store/file.js +287 -0
  130. package/dist/src/memory/store/file.js.map +1 -0
  131. package/dist/src/memory/store/index.d.ts +5 -0
  132. package/dist/src/memory/store/index.d.ts.map +1 -0
  133. package/dist/src/memory/store/index.js +5 -0
  134. package/dist/src/memory/store/index.js.map +1 -0
  135. package/dist/src/memory/store/pack.d.ts +25 -0
  136. package/dist/src/memory/store/pack.d.ts.map +1 -0
  137. package/dist/src/memory/store/pack.js +71 -0
  138. package/dist/src/memory/store/pack.js.map +1 -0
  139. package/dist/src/memory/store/sources.d.ts +55 -0
  140. package/dist/src/memory/store/sources.d.ts.map +1 -0
  141. package/dist/src/memory/store/sources.js +85 -0
  142. package/dist/src/memory/store/sources.js.map +1 -0
  143. package/dist/src/memory/store/store.d.ts +46 -0
  144. package/dist/src/memory/store/store.d.ts.map +1 -0
  145. package/dist/src/memory/store/store.js +171 -0
  146. package/dist/src/memory/store/store.js.map +1 -0
  147. package/dist/src/memory/store.d.ts +19 -16
  148. package/dist/src/memory/store.d.ts.map +1 -1
  149. package/dist/src/memory/store.js +14 -11
  150. package/dist/src/memory/store.js.map +1 -1
  151. package/dist/src/memory-store.js +554 -0
  152. package/dist/src/model-resolution/catalog.d.ts +24 -10
  153. package/dist/src/model-resolution/catalog.d.ts.map +1 -1
  154. package/dist/src/model-resolution/catalog.js +61 -36
  155. package/dist/src/model-resolution/catalog.js.map +1 -1
  156. package/dist/src/model-resolution/duet-gateway.d.ts +2 -0
  157. package/dist/src/model-resolution/duet-gateway.d.ts.map +1 -1
  158. package/dist/src/model-resolution/duet-gateway.js +57 -4
  159. package/dist/src/model-resolution/duet-gateway.js.map +1 -1
  160. package/dist/src/model-resolution/resolver.d.ts +17 -0
  161. package/dist/src/model-resolution/resolver.d.ts.map +1 -1
  162. package/dist/src/model-resolution/resolver.js +49 -4
  163. package/dist/src/model-resolution/resolver.js.map +1 -1
  164. package/dist/src/model-routing/advisor-context.d.ts +31 -5
  165. package/dist/src/model-routing/advisor-context.d.ts.map +1 -1
  166. package/dist/src/model-routing/advisor-context.js +87 -10
  167. package/dist/src/model-routing/advisor-context.js.map +1 -1
  168. package/dist/src/model-routing/advisor-lifecycle.d.ts +14 -5
  169. package/dist/src/model-routing/advisor-lifecycle.d.ts.map +1 -1
  170. package/dist/src/model-routing/advisor-lifecycle.js +24 -7
  171. package/dist/src/model-routing/advisor-lifecycle.js.map +1 -1
  172. package/dist/src/model-routing/prompts.d.ts.map +1 -1
  173. package/dist/src/model-routing/prompts.js +18 -10
  174. package/dist/src/model-routing/prompts.js.map +1 -1
  175. package/dist/src/model-routing/table.js +18 -18
  176. package/dist/src/model-routing/table.js.map +1 -1
  177. package/dist/src/session/session-manager.d.ts +1 -4
  178. package/dist/src/session/session-manager.d.ts.map +1 -1
  179. package/dist/src/session/session-manager.js +2 -5
  180. package/dist/src/session/session-manager.js.map +1 -1
  181. package/dist/src/session/session.d.ts +2 -2
  182. package/dist/src/session/session.d.ts.map +1 -1
  183. package/dist/src/session/session.js +10 -7
  184. package/dist/src/session/session.js.map +1 -1
  185. package/dist/src/train/archive.d.ts.map +1 -1
  186. package/dist/src/train/archive.js +5 -0
  187. package/dist/src/train/archive.js.map +1 -1
  188. package/dist/src/train/types.d.ts +8 -7
  189. package/dist/src/train/types.d.ts.map +1 -1
  190. package/dist/src/tui/sidebar.d.ts.map +1 -1
  191. package/dist/src/tui/sidebar.js +40 -1
  192. package/dist/src/tui/sidebar.js.map +1 -1
  193. package/dist/src/turn-runner/agent-events.d.ts +2 -0
  194. package/dist/src/turn-runner/agent-events.d.ts.map +1 -1
  195. package/dist/src/turn-runner/agent-events.js +11 -0
  196. package/dist/src/turn-runner/agent-events.js.map +1 -1
  197. package/dist/src/turn-runner/subagent.d.ts +4 -1
  198. package/dist/src/turn-runner/subagent.d.ts.map +1 -1
  199. package/dist/src/turn-runner/subagent.js +11 -3
  200. package/dist/src/turn-runner/subagent.js.map +1 -1
  201. package/dist/src/turn-runner/tools.d.ts +3 -3
  202. package/dist/src/turn-runner/tools.d.ts.map +1 -1
  203. package/dist/src/turn-runner/tools.js +1 -1
  204. package/dist/src/turn-runner/tools.js.map +1 -1
  205. package/dist/src/turn-runner/turn-runner.d.ts +19 -3
  206. package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
  207. package/dist/src/turn-runner/turn-runner.js +162 -33
  208. package/dist/src/turn-runner/turn-runner.js.map +1 -1
  209. package/dist/src/turn-runner/usage-accounting.d.ts +5 -2
  210. package/dist/src/turn-runner/usage-accounting.d.ts.map +1 -1
  211. package/dist/src/turn-runner/usage-accounting.js +18 -6
  212. package/dist/src/turn-runner/usage-accounting.js.map +1 -1
  213. package/dist/src/turn-runner/wire-shaping.d.ts +3 -1
  214. package/dist/src/turn-runner/wire-shaping.d.ts.map +1 -1
  215. package/dist/src/turn-runner/wire-shaping.js +5 -1
  216. package/dist/src/turn-runner/wire-shaping.js.map +1 -1
  217. package/dist/src/types/config.d.ts +7 -0
  218. package/dist/src/types/config.d.ts.map +1 -1
  219. package/dist/src/types/protocol.d.ts +13 -5
  220. package/dist/src/types/protocol.d.ts.map +1 -1
  221. package/package.json +8 -3
@@ -1,6 +1,13 @@
1
+ import { resolve } from "node:path";
1
2
  import { Agent, } from "@earendil-works/pi-agent-core";
2
3
  import { isContextOverflow } from "@earendil-works/pi-ai";
3
4
  import { resolveProviderApiKey } from "../model-resolution/duet-gateway.js";
5
+ import { ensureFreshConnectedTokens, markConnectedProviderReconnectNeeded, } from "../connected-providers/tokens.js";
6
+ import { isConnectedProviderId } from "../connected-providers/store.js";
7
+ import { connectedFallbackCause, isPostOutputConnectedFailure, nextTransportAfterConnectedFailure, } from "../connected-providers/fallback.js";
8
+ import { transportFallbackNotice } from "../connected-providers/fallback-notice.js";
9
+ import { usageForTransport } from "../connected-providers/billing.js";
10
+ import { connectedTransportSnapshot, demoteConnectedTransport } from "../cli/shared.js";
4
11
  import dedent from "dedent";
5
12
  import { stripSystemReminders, systemReminder } from "../lib/system-reminder.js";
6
13
  import { assistantText } from "../core/serializer.js";
@@ -8,20 +15,22 @@ import { classifyRoute } from "../model-routing/classifier.js";
8
15
  import { loadRoutingTable } from "../model-routing/loader.js";
9
16
  import { AdvisorTurnLifecycle } from "../model-routing/advisor-lifecycle.js";
10
17
  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";
18
+ import { ADVISOR_RECENT_MESSAGE_TARGET_TOKENS, buildAdvisorContext, captureAdvisorExecutorContext, } from "../model-routing/advisor-context.js";
19
+ import { ADVISOR_MAX_OUTPUT_TOKENS } from "../model-routing/advisor.js";
12
20
  import { resolveTierDefault } from "../model-routing/resolve.js";
13
21
  import { ModelRouter, } from "../model-routing/router.js";
14
22
  import { BUILT_IN_ROUTING_TABLE, isVirtualModel, } from "../model-routing/table.js";
15
23
  import { toXML } from "../lib/xml.js";
16
- import { createObservationalContextTransform, DEFAULT_EFFECTIVE_CONTEXT, estimateTokens, resolveObservationalMemorySettings, stripObservationalContextMessages, updateObservationalMemory, } from "../memory/observational.js";
17
- import { rebuildMemoryContextPack } from "../memory/context-pack.js";
24
+ import { compactObservationalContext, createObservationalContextTransform, DEFAULT_EFFECTIVE_CONTEXT, estimateTokens, resolveObservationalMemorySettings, stripObservationalContextMessages, updateObservationalMemory, } from "../memory/observational.js";
25
+ import { rebuildMemoryContextPack, rebuildPinnedStoreContextPack } from "../memory/context-pack.js";
18
26
  import { createEmbeddingClient } from "../memory/embedding.js";
19
27
  import { loadStoredMemory, replaceSessionObservations, } from "../memory/storage.js";
20
28
  import { MemoryContextCache } from "../memory/store.js";
21
- import { DEFAULT_CLI_MEMORY_MODEL, DEFAULT_CLI_MODEL, resolveModelName, routingCatalogAdapter, } from "../model-resolution/resolver.js";
29
+ import { discoverMemoryStores } from "../memory/store/discovery.js";
30
+ import { DEFAULT_CLI_MEMORY_MODEL, DEFAULT_CLI_MODEL, resolveMeteredModelName, resolveModelName, routingCatalogAdapter, } from "../model-resolution/resolver.js";
22
31
  import { DEFAULT_TASK_WAIT_BUDGET_MS } from "../types/config.js";
23
32
  import { COMPACT_MESSAGE_TOKENS_RATIO, compactTurnState, } from "./state-compaction.js";
24
- import { createAgentEventTranslator, agentMessageText } from "./agent-events.js";
33
+ import { createAgentEventTranslator, agentMessageText, isExternallyVisibleAgentEvent, } from "./agent-events.js";
25
34
  import { createRecallMemorySystemPromptLayer, createSourceOfTruthSystemPromptLayer, createStateMachineSystemPromptLayer, withheldAskReminder, parkNudge, } from "./prompts.js";
26
35
  import { applyEvictionHorizon, calculateWireTokens, createInitialHorizon, findEvictionHorizon, } from "./wire-shaping.js";
27
36
  import { createDefaultTurnRunnerTools, createTurnRunnerTools, resolveStateCwd, formatCarriedTodosReminder, formatStateMachineTerminalAcknowledgmentPrompt, isTurnRunnerControlResult, MINIMUM_STATE_MACHINE_DELAY_MS, } from "./tools.js";
@@ -284,8 +293,9 @@ export class TurnRunner {
284
293
  }
285
294
  : undefined),
286
295
  retryTransientServerErrors: (agent) => this.retryTransientServerErrors(agent),
296
+ retryConnectedTransportFallback: (agent, visibleOutput) => this.retryConnectedTransportFallback(agent, visibleOutput),
287
297
  emitAgentEvent: (event, origin) => this.emitAgentEvent(event, origin),
288
- recordUsage: (usage, modelId) => this.recordUsage(usage, modelId),
298
+ recordUsage: (usage, modelId, provider) => this.recordUsage(usage, modelId, provider),
289
299
  emitTurnUsage: (origin) => this.emitTurnUsage(origin),
290
300
  });
291
301
  }
@@ -442,6 +452,7 @@ export class TurnRunner {
442
452
  * sees available skills before typing the first prompt.
443
453
  */
444
454
  async start(command) {
455
+ await ensureFreshConnectedTokens();
445
456
  await this.ensureMemoryLoaded();
446
457
  await this.ensureSkillsLoaded();
447
458
  await this.ensureMcpServersConnected(command.mcpServers);
@@ -508,6 +519,7 @@ export class TurnRunner {
508
519
  */
509
520
  async turn(command, onAccepted) {
510
521
  this.requireStarted();
522
+ await ensureFreshConnectedTokens();
511
523
  await this.ensureMemoryLoaded();
512
524
  await this.ensureSkillsLoaded();
513
525
  // Serialize behind an in-flight compact. Compact advances the wire
@@ -1881,12 +1893,9 @@ export class TurnRunner {
1881
1893
  });
1882
1894
  }
1883
1895
  createAskAdvisorStorage(router, policy) {
1884
- const resolveAdvisorModel = () => resolveModelName(policy.target.modelName);
1896
+ const resolveAdvisorModel = () => resolveMeteredModelName(policy.target.modelName);
1885
1897
  return {
1886
- getContext: async () => {
1887
- const agent = this.requireParentAgent();
1888
- return await captureAdvisorExecutorContext(agent);
1889
- },
1898
+ getContext: async (contextWindowTokens) => await this.captureContextForAdvisor(contextWindowTokens),
1890
1899
  resolveModel: () => {
1891
1900
  const model = resolveAdvisorModel();
1892
1901
  return {
@@ -1905,7 +1914,7 @@ export class TurnRunner {
1905
1914
  },
1906
1915
  recordUsage: (usage) => {
1907
1916
  const model = resolveAdvisorModel();
1908
- this.recordAndEmitUsage(usageFromAiSdk(usage, model), model.id);
1917
+ this.recordAndEmitUsage(usageFromAiSdk(usage, model), model.id, model.provider);
1909
1918
  },
1910
1919
  onUsageError: (error) => {
1911
1920
  const reason = error instanceof Error ? error.message : String(error);
@@ -1925,6 +1934,55 @@ export class TurnRunner {
1925
1934
  },
1926
1935
  };
1927
1936
  }
1937
+ /**
1938
+ * Keep ordinary advisor calls raw when already efficient. Above the soft
1939
+ * target, drain older work through the normal memory observer and project it
1940
+ * with an advisor-owned horizon so the executor's next request is unchanged.
1941
+ */
1942
+ async captureContextForAdvisor(contextWindowTokens, options = {}) {
1943
+ const agent = this.requireParentAgent();
1944
+ const rawContext = await captureAdvisorExecutorContext(agent);
1945
+ const rawRequest = buildAdvisorContext({
1946
+ context: rawContext,
1947
+ contextWindowTokens,
1948
+ reservedOutputTokens: ADVISOR_MAX_OUTPUT_TOKENS,
1949
+ });
1950
+ const inputTargetTokens = rawRequest.metadata.inputTargetTokens;
1951
+ if (!rawRequest.metadata.truncated &&
1952
+ rawRequest.metadata.estimatedInputTokens <= inputTargetTokens) {
1953
+ return rawContext;
1954
+ }
1955
+ if (!this.memoryPersistence?.session)
1956
+ return rawContext;
1957
+ let recentMessageTarget = Math.min(ADVISOR_RECENT_MESSAGE_TARGET_TOKENS, Math.floor(inputTargetTokens / 2));
1958
+ let projectedContext = rawContext;
1959
+ for (let attempt = 0; attempt < 2; attempt += 1) {
1960
+ projectedContext = await captureAdvisorExecutorContext(agent, {
1961
+ transformMessages: async (messages) => await compactObservationalContext({
1962
+ messages,
1963
+ memory: this.memory,
1964
+ horizon: this.advisorWireGuardHorizon,
1965
+ targetMessageTokens: recentMessageTarget,
1966
+ protectRecentToolInteraction: true,
1967
+ ...(options.drainObservations === false
1968
+ ? {}
1969
+ : {
1970
+ onCompaction: async (observable) => await this.ensureMemoryCoverageForCompaction(observable),
1971
+ }),
1972
+ }),
1973
+ });
1974
+ const projectedRequest = buildAdvisorContext({
1975
+ context: projectedContext,
1976
+ contextWindowTokens,
1977
+ reservedOutputTokens: ADVISOR_MAX_OUTPUT_TOKENS,
1978
+ });
1979
+ const excess = projectedRequest.metadata.estimatedInputTokens - inputTargetTokens;
1980
+ if (excess <= 0)
1981
+ return projectedContext;
1982
+ recentMessageTarget = Math.max(1, recentMessageTarget - excess);
1983
+ }
1984
+ return projectedContext;
1985
+ }
1928
1986
  /**
1929
1987
  * Pass-prep applied to EVERY parent pass, including harness overrides of
1930
1988
  * `runAgentWorker` (they must call this first). One-shot reminders that must
@@ -1947,7 +2005,12 @@ export class TurnRunner {
1947
2005
  agent.state.tools = this.turnTools ?? this.createTools(input.state.mode).tools;
1948
2006
  this.parentControlResults.length = 0;
1949
2007
  this.setParentAgentRunning(true);
1950
- const unsubscribe = agent.subscribe((event) => this.emitParentAgentEvent(event));
2008
+ let visibleOutput = false;
2009
+ const unsubscribe = agent.subscribe((event) => {
2010
+ if (isExternallyVisibleAgentEvent(event))
2011
+ visibleOutput = true;
2012
+ this.emitParentAgentEvent(event);
2013
+ });
1951
2014
  this.parentAgentInterrupted = false;
1952
2015
  try {
1953
2016
  if (!input.continuation) {
@@ -1960,6 +2023,7 @@ export class TurnRunner {
1960
2023
  if (switched)
1961
2024
  await this.applyRouterSwitch(agent, switched);
1962
2025
  await agent.prompt(input.prompt, input.images);
2026
+ await this.retryConnectedTransportFallback(agent, () => visibleOutput);
1963
2027
  // Single-shot recovery: if the provider rejected the first attempt
1964
2028
  // with a context-overflow error, advance the sticky wire-shaping
1965
2029
  // horizon so the next send carries roughly the newer half of
@@ -1971,7 +2035,9 @@ export class TurnRunner {
1971
2035
  if (await this.tryRecoverFromContextOverflow(agent)) {
1972
2036
  await agent.continue();
1973
2037
  }
1974
- await this.retryTransientServerErrors(agent);
2038
+ if (!isPostOutputConnectedFailure(agent.state.model.provider, agent.state.messages.at(-1), visibleOutput)) {
2039
+ await this.retryTransientServerErrors(agent);
2040
+ }
1975
2041
  }
1976
2042
  catch (error) {
1977
2043
  if (!this.parentAgentInterrupted) {
@@ -2152,6 +2218,37 @@ export class TurnRunner {
2152
2218
  attempt = messagesAppended > 1 ? 1 : attempt + 1;
2153
2219
  }
2154
2220
  }
2221
+ /** Retry a connected-provider refusal on the next transport before any visible effect. */
2222
+ async retryConnectedTransportFallback(agent, visibleOutput) {
2223
+ while (agent.state.errorMessage) {
2224
+ const provider = agent.state.model.provider;
2225
+ if (!isConnectedProviderId(provider))
2226
+ return;
2227
+ const cause = connectedFallbackCause(agent.state.messages.at(-1));
2228
+ if (!cause || visibleOutput())
2229
+ return;
2230
+ const fallback = nextTransportAfterConnectedFailure(provider, agent.state.model.id, connectedTransportSnapshot());
2231
+ if (!fallback || fallback.transport === provider)
2232
+ return;
2233
+ const failedModelName = agent.state.model.id;
2234
+ demoteConnectedTransport(provider);
2235
+ if (cause === "auth_failed")
2236
+ await markConnectedProviderReconnectNeeded(provider);
2237
+ agent.state.messages.pop();
2238
+ agent.state.model = resolveModelName(`${fallback.transport}:${fallback.modelId}`);
2239
+ this.emit({
2240
+ type: "system",
2241
+ level: "info",
2242
+ message: transportFallbackNotice({
2243
+ provider,
2244
+ modelName: failedModelName,
2245
+ fallbackTransport: fallback.transport,
2246
+ cause,
2247
+ }),
2248
+ });
2249
+ await agent.continue();
2250
+ }
2251
+ }
2155
2252
  async updateMemoryAfterAgentRun(messages, options, sessionId = this.config.sessionId, refreshReflections = true) {
2156
2253
  if (this.config.memoryDbPath === undefined) {
2157
2254
  return;
@@ -2164,13 +2261,14 @@ export class TurnRunner {
2164
2261
  // state-agent entries in `usageByModel` (e.g. `openai/gpt-5.4-mini`) rather
2165
2262
  // than mixing a shorthand in among resolved ids.
2166
2263
  const memoryModel = this.resolveMemoryActorModel(options);
2167
- const memoryModelId = resolveModelName(memoryModel).id;
2264
+ const resolvedMemoryModel = resolveMeteredModelName(memoryModel);
2265
+ const memoryModelId = resolvedMemoryModel.id;
2168
2266
  const result = await updateObservationalMemory({
2169
2267
  session,
2170
2268
  memory: this.memory,
2171
2269
  sessionId,
2172
2270
  effectiveContext: this.resolveEffectiveContext(this.parentAgent?.state.model.contextWindow),
2173
- actorModel: memoryModel,
2271
+ actorModel: `${resolvedMemoryModel.provider}:${resolvedMemoryModel.id}`,
2174
2272
  settings: this.config.memory,
2175
2273
  messages,
2176
2274
  cwd: this.config.cwd ?? process.cwd(),
@@ -2180,7 +2278,7 @@ export class TurnRunner {
2180
2278
  // the terminal aggregate even though memory runs after the parent's
2181
2279
  // final `message_end`.
2182
2280
  onUsage: (usage) => {
2183
- this.recordUsage(usage, memoryModelId);
2281
+ this.recordUsage(usage, memoryModelId, resolvedMemoryModel.provider);
2184
2282
  this.emitTurnUsage();
2185
2283
  },
2186
2284
  onActivity: (event) => this.emit({ type: "memory", ...event }),
@@ -2333,18 +2431,21 @@ export class TurnRunner {
2333
2431
  // `runAgentWorker` on a provider context-overflow error so the retry
2334
2432
  // sends the newer half of history.
2335
2433
  wireGuardHorizon = createInitialHorizon();
2434
+ /** Sticky only for repeated advisor projections; never persisted into executor state. */
2435
+ advisorWireGuardHorizon = createInitialHorizon();
2336
2436
  async getSkills() {
2337
2437
  await this.ensureSkillsLoaded();
2338
2438
  return this.skillContext.getSkills();
2339
2439
  }
2340
2440
  /**
2341
- * Re-discover installed skills from disk. Surfaces newly added skills
2342
- * (e.g. installed in this session) without restarting the runner.
2441
+ * Re-discover installed skills and curated memory stores from disk. Surfaces
2442
+ * newly added skills or edited memory files without restarting the runner.
2343
2443
  * Callers typically pair this with `getSkills()` to refresh their
2344
2444
  * autocomplete catalog.
2345
2445
  */
2346
2446
  async reloadSkills() {
2347
2447
  await this.skillContext.reload();
2448
+ await this.refreshPinnedStoreContextPack();
2348
2449
  return this.skillContext.getSkills();
2349
2450
  }
2350
2451
  /** System-prompt files (AGENTS.md by default) that resolved on disk for this session. */
@@ -2383,10 +2484,11 @@ export class TurnRunner {
2383
2484
  // call so a `duet login` mid-session lights up retrieval without
2384
2485
  // a runner restart.
2385
2486
  //
2386
- // Initial context pack build runs synchronously inside
2387
- // loadStoredMemory so the very first dispatched turn already sees
2388
- // a frozen memory prefix. Subsequent compaction triggers (reflector
2389
- // completion, wire-shaping eviction) refresh it.
2487
+ // The file-backed pack loads alongside the database-backed pack so the
2488
+ // first dispatched turn sees both frozen layers. Later database compaction
2489
+ // triggers refresh only observations; explicit skills reload refreshes the
2490
+ // file-backed layer.
2491
+ const pinnedStoreLoad = this.refreshPinnedStoreContextPack();
2390
2492
  this.memoryPersistence = await loadStoredMemory(this.config.memoryDbPath, this.config.cwd ?? process.cwd(), {
2391
2493
  embed: createEmbeddingClient(),
2392
2494
  contextPack: {
@@ -2415,6 +2517,27 @@ export class TurnRunner {
2415
2517
  });
2416
2518
  },
2417
2519
  });
2520
+ await pinnedStoreLoad;
2521
+ }
2522
+ /** Reload the frozen file-backed layer without touching database memory. */
2523
+ async refreshPinnedStoreContextPack() {
2524
+ if (this.config.memoryStores === false) {
2525
+ this.memory.setStoredContextPack([]);
2526
+ return;
2527
+ }
2528
+ try {
2529
+ const base = this.config.cwd ?? process.cwd();
2530
+ // Explicit entries may be relative; resolve them against the configured
2531
+ // cwd like relative db and system-prompt paths, not the process cwd.
2532
+ const stores = this.config.memoryStores
2533
+ ? this.config.memoryStores.map((store) => resolve(base, store))
2534
+ : await discoverMemoryStores(base);
2535
+ await rebuildPinnedStoreContextPack({ stores, cache: this.memory });
2536
+ }
2537
+ catch (error) {
2538
+ const reason = error instanceof Error ? error.message : String(error);
2539
+ console.warn(`[duet-agent] failed to load pinned memory stores: ${reason}`);
2540
+ }
2418
2541
  }
2419
2542
  createBaseSystemPromptWithAppendedLayers(input) {
2420
2543
  return this.skillContext.createSystemPromptWithAppendedLayers(input);
@@ -2433,15 +2556,21 @@ export class TurnRunner {
2433
2556
  thinkingLevel: options?.thinkingLevel ?? base?.thinkingLevel ?? this.config.thinkingLevel,
2434
2557
  };
2435
2558
  }
2436
- recordUsage(usage, modelId) {
2437
- this.turnUsage = addUsage(this.turnUsage, usage);
2438
- if (usage && modelId) {
2439
- this.turnUsageByModel = addUsageByModel(this.turnUsageByModel, modelId, usage);
2559
+ recordUsage(usage, modelId, provider) {
2560
+ const transport = provider ?? this.parentAgent?.state.model.provider;
2561
+ if (usage && (!modelId || !transport)) {
2562
+ throw new Error("Usage accounting requires both model id and transport provider.");
2563
+ }
2564
+ const accounted = usage && transport ? usageForTransport(usage, transport) : usage;
2565
+ this.turnUsage = addUsage(this.turnUsage, accounted);
2566
+ if (accounted && modelId && transport) {
2567
+ this.turnUsageByModel = addUsageByModel(this.turnUsageByModel, modelId, transport, accounted);
2568
+ this.turnUsage.cost.total = this.turnUsageByModel.reduce((total, entry) => total + entry.usage.cost.total, 0);
2440
2569
  }
2441
2570
  }
2442
2571
  /** Attribute a non-parent model call and publish it once a flat context snapshot exists. */
2443
- recordAndEmitUsage(usage, modelId) {
2444
- this.recordUsage(usage, modelId);
2572
+ recordAndEmitUsage(usage, modelId, provider) {
2573
+ this.recordUsage(usage, modelId, provider);
2445
2574
  this.emitTurnUsage();
2446
2575
  }
2447
2576
  emitAgentEvent(event, origin) {
@@ -2475,7 +2604,7 @@ export class TurnRunner {
2475
2604
  // consumed tokens, so a rejected attempt (e.g. a provider context-overflow
2476
2605
  // error the turn then recovers from) carries no usage and would otherwise
2477
2606
  // emit a degenerate all-zero bar.
2478
- this.recordUsage(event.message.usage, event.message.model);
2607
+ this.recordUsage(event.message.usage, event.message.model, event.message.provider);
2479
2608
  if (event.message.usage.totalTokens > 0)
2480
2609
  this.emitTurnUsage();
2481
2610
  }
@@ -2593,7 +2722,7 @@ export class TurnRunner {
2593
2722
  systemPrompt: estimateTokens(agent.state.systemPrompt),
2594
2723
  messages: messageWireTokens,
2595
2724
  localMemory: pack.local.reduce((total, row) => total + estimateTokens(row.content), 0),
2596
- globalMemory: pack.global.reduce((total, row) => total + estimateTokens(row.content), 0),
2725
+ globalMemory: [...pack.stored, ...pack.global].reduce((total, row) => total + estimateTokens(row.content), 0),
2597
2726
  };
2598
2727
  }
2599
2728
  /**
@@ -2646,11 +2775,11 @@ export class TurnRunner {
2646
2775
  }
2647
2776
  /** One metered classifier contract shared by parent routing and spawned children. */
2648
2777
  classifierOptions(table) {
2649
- const model = resolveModelName(table.classifier.target.modelName);
2778
+ const model = resolveMeteredModelName(table.classifier.target.modelName);
2650
2779
  return {
2651
2780
  model: `${model.provider}:${model.id}`,
2652
2781
  thinkingLevel: table.classifier.target.thinkingLevel,
2653
- onUsage: (usage) => this.recordAndEmitUsage(usage, model.id),
2782
+ onUsage: (usage) => this.recordAndEmitUsage(usage, model.id, model.provider),
2654
2783
  };
2655
2784
  }
2656
2785
  /** True when a model name is owned by the validated project table. */