@namzu/cli 24.0.0 → 25.0.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 (192) hide show
  1. package/CHANGELOG.md +841 -0
  2. package/README.md +1 -1
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +1 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/drain.d.ts +0 -23
  7. package/dist/commands/drain.d.ts.map +1 -1
  8. package/dist/commands/drain.js +23 -9
  9. package/dist/commands/drain.js.map +1 -1
  10. package/dist/commands/resident-flags.d.ts +6 -1
  11. package/dist/commands/resident-flags.d.ts.map +1 -1
  12. package/dist/commands/resident-flags.js +39 -1
  13. package/dist/commands/resident-flags.js.map +1 -1
  14. package/dist/commands/resident-learning.d.ts +6 -0
  15. package/dist/commands/resident-learning.d.ts.map +1 -0
  16. package/dist/commands/resident-learning.js +293 -0
  17. package/dist/commands/resident-learning.js.map +1 -0
  18. package/dist/commands/resident.d.ts.map +1 -1
  19. package/dist/commands/resident.js +42 -4
  20. package/dist/commands/resident.js.map +1 -1
  21. package/dist/commands/run-flags.d.ts +2 -2
  22. package/dist/commands/run-flags.d.ts.map +1 -1
  23. package/dist/commands/run-flags.js +6 -6
  24. package/dist/commands/run-flags.js.map +1 -1
  25. package/dist/commands/run-stream.d.ts.map +1 -1
  26. package/dist/commands/run-stream.js +9 -10
  27. package/dist/commands/run-stream.js.map +1 -1
  28. package/dist/commands/run.d.ts.map +1 -1
  29. package/dist/commands/run.js +3 -2
  30. package/dist/commands/run.js.map +1 -1
  31. package/dist/config/load.d.ts.map +1 -1
  32. package/dist/config/load.js +27 -4
  33. package/dist/config/load.js.map +1 -1
  34. package/dist/config/run-limits.d.ts +11 -0
  35. package/dist/config/run-limits.d.ts.map +1 -0
  36. package/dist/config/run-limits.js +64 -0
  37. package/dist/config/run-limits.js.map +1 -0
  38. package/dist/config/schema.d.ts +12 -4
  39. package/dist/config/schema.d.ts.map +1 -1
  40. package/dist/config/schema.js.map +1 -1
  41. package/dist/context/runtime-message.d.ts.map +1 -1
  42. package/dist/context/runtime-message.js +6 -0
  43. package/dist/context/runtime-message.js.map +1 -1
  44. package/dist/integrations/plugins/runtime.d.ts +19 -1
  45. package/dist/integrations/plugins/runtime.d.ts.map +1 -1
  46. package/dist/integrations/plugins/runtime.js +89 -1
  47. package/dist/integrations/plugins/runtime.js.map +1 -1
  48. package/dist/integrations/plugins/settings.d.ts +15 -0
  49. package/dist/integrations/plugins/settings.d.ts.map +1 -0
  50. package/dist/integrations/plugins/settings.js +106 -0
  51. package/dist/integrations/plugins/settings.js.map +1 -0
  52. package/dist/integrations/resident/inspection.d.ts +10 -0
  53. package/dist/integrations/resident/inspection.d.ts.map +1 -0
  54. package/dist/integrations/resident/inspection.js +263 -0
  55. package/dist/integrations/resident/inspection.js.map +1 -0
  56. package/dist/integrations/resident/learning-sources.d.ts +4 -0
  57. package/dist/integrations/resident/learning-sources.d.ts.map +1 -0
  58. package/dist/integrations/resident/learning-sources.js +79 -0
  59. package/dist/integrations/resident/learning-sources.js.map +1 -0
  60. package/dist/integrations/resident/learning-storage.d.ts +5 -0
  61. package/dist/integrations/resident/learning-storage.d.ts.map +1 -0
  62. package/dist/integrations/resident/learning-storage.js +51 -0
  63. package/dist/integrations/resident/learning-storage.js.map +1 -0
  64. package/dist/integrations/resident/runner-launch.d.ts +5 -0
  65. package/dist/integrations/resident/runner-launch.d.ts.map +1 -1
  66. package/dist/integrations/resident/runner-launch.js +2 -0
  67. package/dist/integrations/resident/runner-launch.js.map +1 -1
  68. package/dist/integrations/resident/runner-store.d.ts.map +1 -1
  69. package/dist/integrations/resident/runner-store.js +2 -4
  70. package/dist/integrations/resident/runner-store.js.map +1 -1
  71. package/dist/integrations/resident/runner-worker.js +3 -0
  72. package/dist/integrations/resident/runner-worker.js.map +1 -1
  73. package/dist/integrations/resident/session-step.d.ts +6 -1
  74. package/dist/integrations/resident/session-step.d.ts.map +1 -1
  75. package/dist/integrations/resident/session-step.js +110 -14
  76. package/dist/integrations/resident/session-step.js.map +1 -1
  77. package/dist/integrations/resident/storage.d.ts.map +1 -1
  78. package/dist/integrations/resident/storage.js +7 -6
  79. package/dist/integrations/resident/storage.js.map +1 -1
  80. package/dist/integrations/resident/tool-evidence.d.ts +6 -0
  81. package/dist/integrations/resident/tool-evidence.d.ts.map +1 -0
  82. package/dist/integrations/resident/tool-evidence.js +117 -0
  83. package/dist/integrations/resident/tool-evidence.js.map +1 -0
  84. package/dist/integrations/resident/verification.d.ts +11 -0
  85. package/dist/integrations/resident/verification.d.ts.map +1 -0
  86. package/dist/integrations/resident/verification.js +109 -0
  87. package/dist/integrations/resident/verification.js.map +1 -0
  88. package/dist/integrations/sessions/compaction-evidence.d.ts +5 -0
  89. package/dist/integrations/sessions/compaction-evidence.d.ts.map +1 -0
  90. package/dist/integrations/sessions/compaction-evidence.js +59 -0
  91. package/dist/integrations/sessions/compaction-evidence.js.map +1 -0
  92. package/dist/integrations/sessions/context-inventory.js +1 -1
  93. package/dist/integrations/sessions/context-inventory.js.map +1 -1
  94. package/dist/integrations/sessions/conversation-search.d.ts +56 -8
  95. package/dist/integrations/sessions/conversation-search.d.ts.map +1 -1
  96. package/dist/integrations/sessions/conversation-search.js +600 -63
  97. package/dist/integrations/sessions/conversation-search.js.map +1 -1
  98. package/dist/integrations/sessions/database.d.ts +5 -0
  99. package/dist/integrations/sessions/database.d.ts.map +1 -0
  100. package/dist/integrations/sessions/database.js +34 -0
  101. package/dist/integrations/sessions/database.js.map +1 -0
  102. package/dist/integrations/sessions/evidence-page-validation.d.ts +8 -0
  103. package/dist/integrations/sessions/evidence-page-validation.d.ts.map +1 -0
  104. package/dist/integrations/sessions/evidence-page-validation.js +74 -0
  105. package/dist/integrations/sessions/evidence-page-validation.js.map +1 -0
  106. package/dist/integrations/sessions/evidence-recall.d.ts +5 -0
  107. package/dist/integrations/sessions/evidence-recall.d.ts.map +1 -0
  108. package/dist/integrations/sessions/evidence-recall.js +173 -0
  109. package/dist/integrations/sessions/evidence-recall.js.map +1 -0
  110. package/dist/integrations/sessions/paths.d.ts +7 -0
  111. package/dist/integrations/sessions/paths.d.ts.map +1 -0
  112. package/dist/integrations/sessions/paths.js +14 -0
  113. package/dist/integrations/sessions/paths.js.map +1 -0
  114. package/dist/integrations/sessions/run-discovery.d.ts +24 -0
  115. package/dist/integrations/sessions/run-discovery.d.ts.map +1 -0
  116. package/dist/integrations/sessions/run-discovery.js +142 -0
  117. package/dist/integrations/sessions/run-discovery.js.map +1 -0
  118. package/dist/integrations/sessions/store.d.ts +8 -18
  119. package/dist/integrations/sessions/store.d.ts.map +1 -1
  120. package/dist/integrations/sessions/store.js +14 -39
  121. package/dist/integrations/sessions/store.js.map +1 -1
  122. package/dist/integrations/sessions/transcript-export.d.ts +0 -1
  123. package/dist/integrations/sessions/transcript-export.d.ts.map +1 -1
  124. package/dist/integrations/sessions/transcript-export.js +3 -2
  125. package/dist/integrations/sessions/transcript-export.js.map +1 -1
  126. package/dist/integrations/sessions/turn-evidence.d.ts +0 -9
  127. package/dist/integrations/sessions/turn-evidence.d.ts.map +1 -1
  128. package/dist/integrations/sessions/turn-evidence.js +5 -2
  129. package/dist/integrations/sessions/turn-evidence.js.map +1 -1
  130. package/dist/integrations/state/report.d.ts.map +1 -1
  131. package/dist/integrations/state/report.js +78 -21
  132. package/dist/integrations/state/report.js.map +1 -1
  133. package/dist/integrations/subagents/parent.d.ts +3 -2
  134. package/dist/integrations/subagents/parent.d.ts.map +1 -1
  135. package/dist/integrations/subagents/parent.js +11 -6
  136. package/dist/integrations/subagents/parent.js.map +1 -1
  137. package/dist/integrations/subagents/policy.d.ts +2 -5
  138. package/dist/integrations/subagents/policy.d.ts.map +1 -1
  139. package/dist/integrations/subagents/policy.js +2 -5
  140. package/dist/integrations/subagents/policy.js.map +1 -1
  141. package/dist/integrations/subagents/runtime.d.ts +7 -0
  142. package/dist/integrations/subagents/runtime.d.ts.map +1 -1
  143. package/dist/integrations/subagents/runtime.js +9 -7
  144. package/dist/integrations/subagents/runtime.js.map +1 -1
  145. package/dist/permissions/rules.d.ts +2 -8
  146. package/dist/permissions/rules.d.ts.map +1 -1
  147. package/dist/permissions/rules.js +19 -10
  148. package/dist/permissions/rules.js.map +1 -1
  149. package/dist/tui/App.d.ts +0 -12
  150. package/dist/tui/App.d.ts.map +1 -1
  151. package/dist/tui/App.js +245 -19
  152. package/dist/tui/App.js.map +1 -1
  153. package/dist/tui/ChoicePicker.d.ts +2 -1
  154. package/dist/tui/ChoicePicker.d.ts.map +1 -1
  155. package/dist/tui/ChoicePicker.js +10 -8
  156. package/dist/tui/ChoicePicker.js.map +1 -1
  157. package/dist/tui/ToolOutputViewer.js +1 -1
  158. package/dist/tui/ToolOutputViewer.js.map +1 -1
  159. package/dist/tui/agent.d.ts +20 -3
  160. package/dist/tui/agent.d.ts.map +1 -1
  161. package/dist/tui/agent.js +211 -39
  162. package/dist/tui/agent.js.map +1 -1
  163. package/dist/tui/conversation-evidence-view.d.ts +6 -0
  164. package/dist/tui/conversation-evidence-view.d.ts.map +1 -0
  165. package/dist/tui/conversation-evidence-view.js +96 -0
  166. package/dist/tui/conversation-evidence-view.js.map +1 -0
  167. package/dist/tui/conversation-history.d.ts +3 -1
  168. package/dist/tui/conversation-history.d.ts.map +1 -1
  169. package/dist/tui/conversation-history.js +16 -1
  170. package/dist/tui/conversation-history.js.map +1 -1
  171. package/dist/tui/markdownParser.d.ts.map +1 -1
  172. package/dist/tui/markdownParser.js +2 -1
  173. package/dist/tui/markdownParser.js.map +1 -1
  174. package/dist/tui/plugin-view.d.ts +9 -0
  175. package/dist/tui/plugin-view.d.ts.map +1 -0
  176. package/dist/tui/plugin-view.js +59 -0
  177. package/dist/tui/plugin-view.js.map +1 -0
  178. package/dist/tui/run-interruption.d.ts +2 -2
  179. package/dist/tui/run-interruption.d.ts.map +1 -1
  180. package/dist/tui/run-interruption.js +5 -1
  181. package/dist/tui/run-interruption.js.map +1 -1
  182. package/dist/tui/run-limits-settings.d.ts +39 -0
  183. package/dist/tui/run-limits-settings.d.ts.map +1 -0
  184. package/dist/tui/run-limits-settings.js +88 -0
  185. package/dist/tui/run-limits-settings.js.map +1 -0
  186. package/dist/tui/slashCommands.d.ts +6 -1
  187. package/dist/tui/slashCommands.d.ts.map +1 -1
  188. package/dist/tui/slashCommands.js +40 -9
  189. package/dist/tui/slashCommands.js.map +1 -1
  190. package/dist/tui/types.d.ts +4 -2
  191. package/dist/tui/types.d.ts.map +1 -1
  192. package/package.json +7 -7
package/dist/tui/agent.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createCurrentCredentialReader } from '../integrations/providers/current-credential.js';
2
+ import { CliPathBuilder } from '../integrations/sessions/paths.js';
2
3
  import { createWebSearchTool, resolveWebSearch, webSearchLabel, } from '../integrations/web/search.js';
3
4
  /**
4
5
  * TUI agent session — provider-direct, tool-enabled.
@@ -22,12 +23,13 @@ import { createWebSearchTool, resolveWebSearch, webSearchLabel, } from '../integ
22
23
  * `emptySession()` whose `send()` yields a single error event so the UI
23
24
  * renders an actionable hint rather than crashing.
24
25
  */
25
- import { BOOT_EVENT_NAMES, BackgroundJobRegistry, DefaultPathBuilder, DiskCheckpointStore, DiskMemoryStore, DiskTaskStore, EVENT_NAME_ATTRIBUTE, GuardedFetchProvider, PromptContributionRegistry, ProviderRegistry, SESSION_GOAL_TOOL_NAMES, ToolRegistry, WebFetchTool, batchNeedsReview, buildAskUserQuestionTool, buildMemoryTools, buildSessionGoalTools, compactNow, createComputerUseTool, createFileReadTracker, createMemoryPromoter, createMemoryRecallStep, createResidentStepContributions, createReviewHandler, createToolPresenter, generateProjectId, generateRunId, generateSessionId, generateTenantId, generateTopicId, getBuiltinTools, isReviewExempt, query, resumeRun, webGuidanceContribution, withProviderFallback, } from '@namzu/sdk';
26
+ import { BOOT_EVENT_NAMES, BackgroundJobRegistry, DiskCheckpointStore, DiskMemoryStore, DiskTaskStore, EVENT_NAME_ATTRIBUTE, GuardedFetchProvider, PromptContributionRegistry, ProviderRegistry, SESSION_GOAL_TOOL_NAMES, ToolRegistry, WebFetchTool, asRunId, batchNeedsReview, buildAskUserQuestionTool, buildMemoryTools, buildResidentHistoryTools, buildResidentToolEvidenceTools, buildSessionGoalTools, compactNow, createComputerUseTool, createFileReadTracker, createMemoryPromoter, createMemoryRecallStep, createResidentStepContext, createResidentStepContributions, createReviewHandler, createToolPresenter, generateProjectId, generateRunId, generateSessionId, generateTenantId, generateTopicId, getBuiltinTools, isReviewExempt, query, resumeRun, seedObservationLedger, webGuidanceContribution, withProviderFallback, } from '@namzu/sdk';
26
27
  import { SubprocessComputerUseHost } from '@namzu/computer-use';
27
28
  import { realpath, stat } from 'node:fs/promises';
28
29
  import { join, parse, resolve } from 'node:path';
29
30
  import { FileCheckpointStore } from '../checkpoints/store.js';
30
31
  import { CHECKPOINTED_TOOLS, withCheckpoints } from '../checkpoints/wrap.js';
32
+ import { readStoredRunGuards, resolveRunGuards } from '../config/run-limits.js';
31
33
  import { probeCapabilities } from '../context/capabilities.js';
32
34
  import { NAMZU_DELEGATION_DOCTRINE, NAMZU_PLAN_MODE_DOCTRINE, NAMZU_WORKING_DOCTRINE, } from '../context/doctrine.js';
33
35
  import { composeEnvironmentPrompt, readEnvironmentFacts } from '../context/environment.js';
@@ -41,15 +43,16 @@ import { canSelectModel } from '../integrations/providers/access.js';
41
43
  import { createGeminiAccessTokenResolver } from '../integrations/providers/gemini-credentials.js';
42
44
  import { CredentialRefreshRejectedError, CredentialWithdrawnError, PROVIDER_REGISTRY, chainCapabilityDisagreements, chainPositionName, describeAcceptedMismatch, describeCapabilityRefusal, discoverProviders, ensureFreshAnthropicToken, ensureFreshStoredCodexCredential, ensureRegistered, findDetected, isAnthropicOAuthToken, isRegistered, missingCredentialMessage, primaryProvider, readCodexCredentialFile, readPreferences, readSubscriptionCredential, resolveChainCapabilities, sameOAuthCredential, unresolvedMembers, unsupportedProviderMessage, } from '../integrations/providers/index.js';
43
45
  import { modelReasoningView } from '../integrations/providers/model-reasoning.js';
46
+ import { retainManualCompaction } from '../integrations/sessions/compaction-evidence.js';
44
47
  import { createContextInventoryStep } from '../integrations/sessions/context-inventory.js';
45
- import { buildConversationReadTool, buildConversationSearchTool, } from '../integrations/sessions/conversation-search.js';
48
+ import { CONVERSATION_EVIDENCE_GUIDANCE, buildConversationReadTool, buildConversationSearchTool, releaseConversationEvidence, } from '../integrations/sessions/conversation-search.js';
49
+ import { createConversationEvidenceRecall } from '../integrations/sessions/evidence-recall.js';
46
50
  import { createTaskContextStep } from '../integrations/sessions/task-context.js';
47
51
  import { ensurePrivateStateDirectory } from '../integrations/state/private-directory.js';
48
52
  import { discoverAgentDefinitions } from '../integrations/subagents/definitions.js';
49
53
  import { createDelegationHistoryStep } from '../integrations/subagents/history.js';
50
54
  import { prepareDelegatedEffort } from '../integrations/subagents/model-effort.js';
51
55
  import { SubagentPathBuilder, resolveSubagentParent } from '../integrations/subagents/parent.js';
52
- import { CLI_INTERACTIVE_RUN_TIMEOUT_MS } from '../integrations/subagents/policy.js';
53
56
  import { createSubagentRuntime } from '../integrations/subagents/runtime.js';
54
57
  import { cliLogger } from '../logging.js';
55
58
  import { formatMemoryDiagnostics } from '../memory/presentation.js';
@@ -97,10 +100,22 @@ export class SessionOperationOwner {
97
100
  streamClosers = new Set();
98
101
  closeReason = new DOMException('Agent session closed.', 'AbortError');
99
102
  closed = false;
103
+ exclusiveOperation = false;
100
104
  closePromise;
101
105
  constructor(cleanup) {
102
106
  this.cleanup = cleanup;
103
107
  }
108
+ /** Change shared session resources only when no invocation can still use them. */
109
+ exclusive(start) {
110
+ if (this.active.size > 0 || this.exclusiveOperation) {
111
+ return Promise.reject(new Error('Wait for the active session operation to finish before changing plugins.'));
112
+ }
113
+ const operation = this.promise(undefined, start);
114
+ this.exclusiveOperation = true;
115
+ return operation.finally(() => {
116
+ this.exclusiveOperation = false;
117
+ });
118
+ }
104
119
  promise(callerSignal, start) {
105
120
  let signal;
106
121
  try {
@@ -230,14 +245,18 @@ export class SessionOperationOwner {
230
245
  operationSignal(callerSignal) {
231
246
  if (this.closed)
232
247
  throw this.closeReason;
248
+ if (this.exclusiveOperation)
249
+ throw new Error('A plugin change is in progress; try again when it finishes.');
233
250
  return callerSignal
234
251
  ? AbortSignal.any([callerSignal, this.lifetime.signal])
235
252
  : this.lifetime.signal;
236
253
  }
237
254
  track(operation) {
238
- const settlement = Promise.resolve(operation).then(() => { }, () => { });
255
+ const release = () => {
256
+ this.active.delete(settlement);
257
+ };
258
+ const settlement = Promise.resolve(operation).then(release, release);
239
259
  this.active.add(settlement);
240
- void settlement.then(() => this.active.delete(settlement));
241
260
  }
242
261
  async finishClose() {
243
262
  await Promise.allSettled([...this.streamClosers].map((close) => close()));
@@ -340,8 +359,15 @@ const EAGER_TOOLS_WHEN_DEFERRED = [
340
359
  'edit',
341
360
  'bash',
342
361
  'job',
362
+ 'wait_for_job',
343
363
  'web_search',
344
364
  'web_fetch',
365
+ 'search_conversation',
366
+ 'read_conversation',
367
+ 'search_resident_history',
368
+ 'read_resident_history',
369
+ 'search_resident_tools',
370
+ 'read_resident_tool',
345
371
  // query mounts discovery when absent; an existing discovery tool must stay ready.
346
372
  'search_tools',
347
373
  ];
@@ -402,12 +428,14 @@ function builtinTools(backgroundJobs) {
402
428
  : source;
403
429
  if (backgroundJobs)
404
430
  return [tool];
405
- if (tool.name === 'job')
431
+ // `wait_for_job` is as useless as `job` itself with no registry to
432
+ // back it — both refuse every call, so neither ships.
433
+ if (tool.name === 'job' || tool.name === 'wait_for_job')
406
434
  return [];
407
435
  return [tool.name === 'bash' ? foregroundOnlyBash(tool) : tool];
408
436
  });
409
437
  }
410
- function buildToolRegistry(cwd, projectStateRoot = join(cwd, '.namzu'), backgroundJobs = true, checkpoints) {
438
+ function buildToolRegistry(cwd, projectStateRoot = join(cwd, '.namzu'), backgroundJobs = true, checkpoints, projectId) {
411
439
  const registry = new ToolRegistry();
412
440
  registry.register(builtinTools(backgroundJobs));
413
441
  // The file tools take a checkpoint before they write, so `/restore` can
@@ -427,8 +455,9 @@ function buildToolRegistry(cwd, projectStateRoot = join(cwd, '.namzu'), backgrou
427
455
  // surfaces inject the central application-home hierarchy; embedded callers
428
456
  // that omit it retain the historical `<cwd>/.namzu` layout.
429
457
  // Separate from the user-curated MEMORY.md that is injected into the prompt.
430
- ensurePrivateStateDirectory(projectStateRoot, 'memory');
431
- const memoryStore = new DiskMemoryStore({ baseDir: projectStateRoot });
458
+ const memoryRoot = ensurePrivateStateDirectory(projectStateRoot, 'memory');
459
+ const directory = projectId ? ensurePrivateStateDirectory(memoryRoot, projectId) : memoryRoot;
460
+ const memoryStore = new DiskMemoryStore({ baseDir: projectStateRoot, directory });
432
461
  // Search through the store's async boundary. Its concrete index is lazy:
433
462
  // handing `getIndex()` to the synchronous overload before the first store
434
463
  // read makes a new process report every persisted memory as absent.
@@ -447,13 +476,54 @@ async function currentPluginSkills(registry) {
447
476
  export async function createAgentSession(prefs, detected, options = {}) {
448
477
  const scope = options.scope ?? mintScope();
449
478
  const fileObservations = new Map();
450
- const observationsFor = (id) => {
451
- let tracker = fileObservations.get(id);
452
- if (!tracker) {
453
- tracker = createFileReadTracker();
454
- fileObservations.set(id, tracker);
479
+ /**
480
+ * This process's observation ledger for one conversation, seeded from that
481
+ * conversation's own history the first time it is asked for.
482
+ *
483
+ * The ledger is memory, and a resumed conversation used to get an empty one:
484
+ * the agent re-read files it had written in full before the session closed,
485
+ * every time. `prior` is the history the caller is about to send, so a
486
+ * resumed session seeds from what it restored and a forked one from its own
487
+ * copied messages — never from the conversation it was branched out of.
488
+ *
489
+ * Keyed on first use of a session id rather than on process start, because
490
+ * the TUI switches ids mid-process: `/resume` and a new conversation both
491
+ * arrive as an id this map has not seen. The map holds the seeding itself
492
+ * rather than its result, so the entry is in place before the first `await`
493
+ * inside it: a second turn that starts while the first is still seeding
494
+ * waits for that same ledger instead of building a second one.
495
+ */
496
+ const observationsFor = (id, prior) => {
497
+ let seeded = fileObservations.get(id);
498
+ if (!seeded) {
499
+ seeded = (async () => {
500
+ const tracker = createFileReadTracker();
501
+ try {
502
+ const report = await seedObservationLedger(prior, tracker, {
503
+ workingDirectory: cwd,
504
+ ...(directories.length > 0 ? { additionalDirectories: [...directories] } : {}),
505
+ sandboxed: sandbox.provider !== undefined,
506
+ });
507
+ cliLogger().debug('file observation ledger rebuilt', {
508
+ 'namzu.files.witnessed': report.pathsWitnessed,
509
+ 'namzu.files.seen': report.pathsSeen,
510
+ 'namzu.files.replayed_units': report.unitsReplayed,
511
+ });
512
+ }
513
+ catch (error) {
514
+ // A ledger that could not be rebuilt is the empty one every resume
515
+ // used to get, so the turn proceeds without its witnesses — but it
516
+ // says so, because a seeding that failed and one that found nothing
517
+ // are otherwise the same silence.
518
+ cliLogger().debug('file observation ledger not rebuilt', {
519
+ 'namzu.files.error': error instanceof Error ? error.message : String(error),
520
+ });
521
+ }
522
+ return tracker;
523
+ })();
524
+ fileObservations.set(id, seeded);
455
525
  }
456
- return tracker;
526
+ return seeded;
457
527
  };
458
528
  const requestedCwd = resolve(options.cwd ?? process.cwd());
459
529
  let cwd;
@@ -474,13 +544,10 @@ export async function createAgentSession(prefs, detected, options = {}) {
474
544
  return emptySession(`Working-directory sandboxing refuses filesystem root ${cwd}; choose a project directory so confinement has a boundary, or explicitly select an ephemeral workspace.`, 'invocation');
475
545
  }
476
546
  const hierarchyRoot = resolve(options.stateRoot ?? join(cwd, '.namzu'));
477
- const pathBuilder = new DefaultPathBuilder(hierarchyRoot);
478
- let projectStateRoot = hierarchyRoot;
547
+ const pathBuilder = new CliPathBuilder(hierarchyRoot);
548
+ const projectStateRoot = hierarchyRoot;
479
549
  try {
480
- if (options.stateRoot) {
481
- const projectsRoot = ensurePrivateStateDirectory(hierarchyRoot, 'projects');
482
- projectStateRoot = ensurePrivateStateDirectory(projectsRoot, scope.projectId);
483
- }
550
+ ensurePrivateStateDirectory(hierarchyRoot, 'sessions');
484
551
  // Refuse an aliased or otherwise unsafe generated-state root before any
485
552
  // provider, sandbox or plugin runtime is constructed. Project-authored
486
553
  // `.namzu` content may coexist here, but generated memory must never be
@@ -833,7 +900,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
833
900
  },
834
901
  };
835
902
  const checkpoints = new FileCheckpointStore(join(ensurePrivateStateDirectory(projectStateRoot, 'checkpoints'), scope.sessionId), cwd);
836
- const { registry, memoryStore } = buildToolRegistry(cwd, projectStateRoot, backgroundJobs, checkpoints);
903
+ const { registry, memoryStore } = buildToolRegistry(cwd, projectStateRoot, backgroundJobs, checkpoints, options.stateRoot ? scope.projectId : undefined);
837
904
  // Package presence is not tool reachability. The CLI used to probe and
838
905
  // report @namzu/computer-use without ever constructing its host or mounting
839
906
  // SDK's computer_use definition, so even an installed, healthy package was
@@ -954,7 +1021,37 @@ export async function createAgentSession(prefs, detected, options = {}) {
954
1021
  // delegate a self-contained task to a fresh sub-agent (own context window).
955
1022
  // Best-effort — if the runtime can't stand up, the chat still works.
956
1023
  const delegationScopes = new Map();
1024
+ const delegationLimits = new Map();
957
1025
  const delegatedInputWaiters = new Map();
1026
+ if (options.residentHistory) {
1027
+ const history = options.residentHistory;
1028
+ const historyOwner = { ...scope };
1029
+ registry.register(buildResidentHistoryTools((context) => {
1030
+ const owner = delegationScopes.get(context.runId);
1031
+ if (!owner ||
1032
+ owner.sessionId !== historyOwner.sessionId ||
1033
+ owner.projectId !== historyOwner.projectId ||
1034
+ owner.tenantId !== historyOwner.tenantId ||
1035
+ owner.tenantId !== history.scope.tenantId)
1036
+ throw new Error('The requesting run does not own this resident history.');
1037
+ return history;
1038
+ }));
1039
+ }
1040
+ if (options.residentToolEvidence) {
1041
+ const evidence = options.residentToolEvidence;
1042
+ const evidenceOwner = { ...scope };
1043
+ registry.register(buildResidentToolEvidenceTools((context) => {
1044
+ const owner = delegationScopes.get(context.runId);
1045
+ if (!owner ||
1046
+ owner.sessionId !== evidenceOwner.sessionId ||
1047
+ owner.projectId !== evidenceOwner.projectId ||
1048
+ owner.tenantId !== evidenceOwner.tenantId ||
1049
+ owner.projectId !== evidence.scope.projectId ||
1050
+ owner.tenantId !== evidence.scope.tenantId)
1051
+ throw new Error('The requesting run does not own this resident tool evidence.');
1052
+ return evidence;
1053
+ }));
1054
+ }
958
1055
  if (options.conversationSessions) {
959
1056
  const sessions = options.conversationSessions;
960
1057
  for (const build of [buildConversationSearchTool, buildConversationReadTool])
@@ -967,6 +1064,25 @@ export async function createAgentSession(prefs, detected, options = {}) {
967
1064
  return { sessions, sessionId: owner.sessionId };
968
1065
  }));
969
1066
  }
1067
+ const evidenceRecallSteps = new Map();
1068
+ const evidenceRecallFor = (sessionId) => {
1069
+ const sessions = options.conversationSessions;
1070
+ if (!sessions || options.compaction?.recallEvidence === false)
1071
+ return [];
1072
+ let step = evidenceRecallSteps.get(sessionId);
1073
+ if (!step) {
1074
+ step = createConversationEvidenceRecall(sessions, sessionId, (runId) => {
1075
+ const owner = delegationScopes.get(asRunId(runId));
1076
+ if (!owner ||
1077
+ owner.sessionId !== sessionId ||
1078
+ owner.tenantId !== sessions.tenantId ||
1079
+ owner.projectId !== sessions.projectId)
1080
+ throw new Error('The requesting run no longer owns this conversation.');
1081
+ }, options.compaction?.resolveEvidenceQueries !== false);
1082
+ evidenceRecallSteps.set(sessionId, step);
1083
+ }
1084
+ return [step];
1085
+ };
970
1086
  let subagentRuntime;
971
1087
  // Stays empty when the runtime below throws, which is the honest answer: the
972
1088
  // catch is non-fatal and the session then genuinely has no delegate to
@@ -989,6 +1105,9 @@ export async function createAgentSession(prefs, detected, options = {}) {
989
1105
  cwd,
990
1106
  model,
991
1107
  tokenBudget: options.limits?.tokenBudget,
1108
+ maxIterations: options.limits?.maxIterations,
1109
+ resolveLimits: (runId) => delegationLimits.get(runId),
1110
+ timeoutMs: options.limits?.timeoutMs,
992
1111
  definitions: discovered.definitions,
993
1112
  pathBuilder: new SubagentPathBuilder(projectStateRoot, scope.projectId),
994
1113
  resolveParent: async (runId) => {
@@ -1110,7 +1229,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1110
1229
  // delegation, and a parent that delegated six times would leave
1111
1230
  // seven accounts of one piece of work for the next run to read.
1112
1231
  // The parent's settle is the one that speaks for the whole task.
1113
- const childTools = buildToolRegistry(cwd, projectStateRoot, backgroundJobs).registry;
1232
+ const childTools = buildToolRegistry(cwd, projectStateRoot, backgroundJobs, undefined, options.stateRoot ? scope.projectId : undefined).registry;
1114
1233
  // Search owns its provider connection per call, so it is safe to share
1115
1234
  // with a child. Preserve the parent's configured backend/off choice.
1116
1235
  const search = registry.get('web_search');
@@ -1320,6 +1439,9 @@ export async function createAgentSession(prefs, detected, options = {}) {
1320
1439
  });
1321
1440
  const operations = new SessionOperationOwner(async () => {
1322
1441
  const results = await Promise.allSettled([
1442
+ options.conversationSessions
1443
+ ? releaseConversationEvidence(options.conversationSessions, scope.sessionId)
1444
+ : undefined,
1323
1445
  subagentRuntime?.close?.(),
1324
1446
  sessionPlugins
1325
1447
  ? sessionPlugins.manager
@@ -1396,6 +1518,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1396
1518
  */
1397
1519
  const kernelResume = ({ entry, checkpointStore, claimFence, signal, checkpointId, listener, }) => operations.promise(signal, async (ownedSignal) => {
1398
1520
  const selectTaskStore = beginTaskStoreReadout();
1521
+ const resumedLimits = (await readStoredRunGuards(join(pathBuilder.runDir(entry.projectId, entry.sessionId, entry.runId), 'run.json'), entry)) ?? resolveRunGuards(options.limits);
1399
1522
  // The same prelude a turn runs, and for the same reasons: a lapsed
1400
1523
  // OAuth token has to be renewed before the provider is used, and the
1401
1524
  // fallback chain has to be built AFTER that so its members do not
@@ -1416,6 +1539,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1416
1539
  NAMZU_IDENTITY,
1417
1540
  NAMZU_WORKING_DOCTRINE,
1418
1541
  NAMZU_DELEGATION_DOCTRINE,
1542
+ options.conversationSessions ? CONVERSATION_EVIDENCE_GUIDANCE : undefined,
1419
1543
  environmentPrompt,
1420
1544
  memoryPrompt,
1421
1545
  ]
@@ -1427,6 +1551,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1427
1551
  }
1428
1552
  delegatedResumeHandlers.set(entry.runId, resumeHandler);
1429
1553
  delegationScopes.set(entry.runId, { ...entry, topicId: scope.topicId });
1554
+ delegationLimits.set(entry.runId, resumedLimits);
1430
1555
  const runTaskStore = selectTaskStore(entry.runId, {
1431
1556
  ...entry,
1432
1557
  topicId: scope.topicId,
@@ -1457,6 +1582,9 @@ export async function createAgentSession(prefs, detected, options = {}) {
1457
1582
  : {}),
1458
1583
  authorizationGate: gateFor(options.rules),
1459
1584
  compactionConfig: compactionConfigFor(options.compaction),
1585
+ retainedToolPreviewChars: options.conversationSessions
1586
+ ? (options.compaction?.retainedToolPreviewChars ?? 4_000)
1587
+ : undefined,
1460
1588
  prepareStep: [
1461
1589
  createTaskContextStep(runTaskStore, entry.tenantId),
1462
1590
  createDelegationHistoryStep(projectStateRoot, entry.sessionId),
@@ -1469,6 +1597,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1469
1597
  }),
1470
1598
  ]),
1471
1599
  ...(options.conversationSessions ? [createContextInventoryStep()] : []),
1600
+ ...evidenceRecallFor(entry.sessionId),
1472
1601
  ],
1473
1602
  ...(options.compaction?.consolidate
1474
1603
  ? { consolidateInto: memoryStore }
@@ -1488,9 +1617,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1488
1617
  model,
1489
1618
  ...(nativeWebSearch ? { webSearch: nativeWebSearch } : {}),
1490
1619
  ...(sandbox.provider ? { sandbox: { workspace: sandboxWorkspace } } : {}),
1491
- timeoutMs: CLI_INTERACTIVE_RUN_TIMEOUT_MS,
1492
- tokenBudget: options.limits?.tokenBudget ?? 0,
1493
- maxIterations: options.limits?.maxIterations ?? 50,
1620
+ ...resumedLimits,
1494
1621
  maxResponseTokens: 8192,
1495
1622
  permissionMode: 'auto',
1496
1623
  },
@@ -1530,6 +1657,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1530
1657
  if (delegatedResumeHandlers.get(entry.runId) === resumeHandler) {
1531
1658
  delegatedResumeHandlers.delete(entry.runId);
1532
1659
  delegationScopes.delete(entry.runId);
1660
+ delegationLimits.delete(entry.runId);
1533
1661
  await subagentRuntime?.releaseRun(entry.runId);
1534
1662
  }
1535
1663
  }
@@ -1622,6 +1750,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1622
1750
  reasoningEffortDefault,
1623
1751
  compact: (messages) => operations.promise(undefined, async (signal) => {
1624
1752
  const sessionId = scope.sessionId;
1753
+ const sessions = options.conversationSessions;
1625
1754
  await prepareProviderCredential(signal);
1626
1755
  return compactNow({
1627
1756
  messages,
@@ -1629,6 +1758,16 @@ export async function createAgentSession(prefs, detected, options = {}) {
1629
1758
  provider: providerForSession(sessionId),
1630
1759
  model,
1631
1760
  signal,
1761
+ ...(sessions
1762
+ ? {
1763
+ onShed: async (removed) => {
1764
+ if (sessions.projectId !== scope.projectId ||
1765
+ sessions.tenantId !== scope.tenantId)
1766
+ throw new Error('Manual compaction is outside the current conversation scope.');
1767
+ await retainManualCompaction(sessions, sessionId, removed, signal);
1768
+ },
1769
+ }
1770
+ : {}),
1632
1771
  });
1633
1772
  }),
1634
1773
  // Reads the same registry object the deferred registration mutates, at
@@ -1638,6 +1777,17 @@ export async function createAgentSession(prefs, detected, options = {}) {
1638
1777
  .getCallableTools()
1639
1778
  .map((t) => t.name)
1640
1779
  .filter((name) => !goalToolNames.has(name)),
1780
+ ...(pluginRuntime
1781
+ ? {
1782
+ plugins: {
1783
+ list: pluginRuntime.list,
1784
+ rememberState: (name) => operations.exclusive(() => pluginRuntime.rememberState(name)),
1785
+ setEnabled: (name, enabled) => operations.exclusive(async () => {
1786
+ await pluginRuntime.setEnabled(name, enabled);
1787
+ }),
1788
+ },
1789
+ }
1790
+ : {}),
1641
1791
  agentIds: allowedAgentIds,
1642
1792
  currentTaskStore,
1643
1793
  resetTaskStore,
@@ -1685,6 +1835,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1685
1835
  send: (messages, opts) => operations.stream(opts?.signal, (signal) => (async function* () {
1686
1836
  const selectTaskStore = beginTaskStoreReadout();
1687
1837
  const runId = opts?.runId ?? generateRunId();
1838
+ const turnLimits = resolveRunGuards(options.limits, opts?.limits);
1688
1839
  const turnOpts = { ...opts, runId, signal };
1689
1840
  let runTools = registry;
1690
1841
  const resumeHandler = makeResumeHandler(approval, opts?.onPermission, opts?.permissionMode ?? options.permissionMode, (name, input) => isPromptExempt(runTools, name, input));
@@ -1698,6 +1849,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1698
1849
  delegatedInputWaiters.set(runId, opts.waitForInbound);
1699
1850
  const turnScope = { ...scope };
1700
1851
  delegationScopes.set(runId, turnScope);
1852
+ delegationLimits.set(runId, turnLimits);
1701
1853
  try {
1702
1854
  // Renew a lapsed OAuth token before the turn runs (no-op for valid
1703
1855
  // tokens and non-subscription credentials).
@@ -1764,10 +1916,24 @@ export async function createAgentSession(prefs, detected, options = {}) {
1764
1916
  });
1765
1917
  const residentContext = opts?.residentContext;
1766
1918
  if (residentContext) {
1767
- for (const contribution of createResidentStepContributions({
1919
+ const contextOptions = {
1768
1920
  ...residentContext,
1769
1921
  readOnly: (opts?.permissionMode ?? options.permissionMode) === 'plan',
1770
- }))
1922
+ };
1923
+ const bundle = opts?.residentLearningDisclosure === 'on-demand'
1924
+ ? createResidentStepContext({
1925
+ ...contextOptions,
1926
+ authorizeLearningRead: (context) => context.runId === runId &&
1927
+ delegationScopes.get(context.runId) === turnScope,
1928
+ })
1929
+ : { contributions: createResidentStepContributions(contextOptions), tools: [] };
1930
+ if (bundle.tools.length) {
1931
+ // Per-send membership: neither another send nor delegated runs inherit this tool.
1932
+ runTools = runTools.fork();
1933
+ for (const tool of bundle.tools)
1934
+ runTools.register(tool);
1935
+ }
1936
+ for (const contribution of bundle.contributions)
1771
1937
  promptContributions.register(contribution);
1772
1938
  // These are invocation snapshots, not the stable working policy.
1773
1939
  const invocationContext = [environmentPrompt, memoryPrompt, opts?.extraSystem]
@@ -1783,6 +1949,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1783
1949
  NAMZU_IDENTITY,
1784
1950
  residentContext ? undefined : NAMZU_WORKING_DOCTRINE,
1785
1951
  residentContext ? undefined : NAMZU_DELEGATION_DOCTRINE,
1952
+ options.conversationSessions ? CONVERSATION_EVIDENCE_GUIDANCE : undefined,
1786
1953
  options.toolLoading === 'deferred' ? DEFERRED_TOOL_GUIDANCE : undefined,
1787
1954
  // Present only while the turn runs under `plan`. A mode change
1788
1955
  // is rare, so the cached prefix it re-keys is a price paid once
@@ -1828,8 +1995,11 @@ export async function createAgentSession(prefs, detected, options = {}) {
1828
1995
  try {
1829
1996
  yield* runTurn({
1830
1997
  provider: providerForSession(turnScope.sessionId),
1831
- fileReadTracker: observationsFor(turnScope.sessionId),
1998
+ fileReadTracker: await observationsFor(turnScope.sessionId, messages),
1832
1999
  compactionConfig: compactionConfigFor(options.compaction),
2000
+ retainedToolPreviewChars: options.conversationSessions
2001
+ ? (options.compaction?.retainedToolPreviewChars ?? 4_000)
2002
+ : undefined,
1833
2003
  ...(options.compaction?.consolidate ? { consolidateInto: memoryStore } : {}),
1834
2004
  ...(jobRegistry
1835
2005
  ? {
@@ -1854,7 +2024,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1854
2024
  pathBuilder,
1855
2025
  workingDirectory: cwd,
1856
2026
  ...(directories.length > 0 ? { additionalDirectories: [...directories] } : {}),
1857
- ...(options.limits ? { limits: options.limits } : {}),
2027
+ limits: turnLimits,
1858
2028
  sandboxWorkspace,
1859
2029
  rules: options.rules,
1860
2030
  structuredOutput: options.structuredOutput,
@@ -1874,6 +2044,8 @@ export async function createAgentSession(prefs, detected, options = {}) {
1874
2044
  }),
1875
2045
  ]),
1876
2046
  ...(options.conversationSessions ? [createContextInventoryStep()] : []),
2047
+ ...evidenceRecallFor(turnScope.sessionId),
2048
+ ...(options.residentEvidenceRecall ? [options.residentEvidenceRecall] : []),
1877
2049
  ],
1878
2050
  taskStore: runTaskStore,
1879
2051
  systemPrompt,
@@ -1893,6 +2065,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1893
2065
  task_update: options.toolLoading === 'deferred' ? 'deferred' : 'active',
1894
2066
  task_list: options.toolLoading === 'deferred' ? 'deferred' : 'active',
1895
2067
  },
2068
+ emergencySave: options.emergencySave ?? true,
1896
2069
  onRunEvent: options.onRunEvent,
1897
2070
  ...(sandbox.provider ? { sandboxProvider: sandbox.provider } : {}),
1898
2071
  ...(options.sandbox?.teardownTimeoutMs !== undefined
@@ -1916,6 +2089,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
1916
2089
  modelSwitchHandlers.delete(runId);
1917
2090
  delegatedInputWaiters.delete(runId);
1918
2091
  delegationScopes.delete(runId);
2092
+ delegationLimits.delete(runId);
1919
2093
  await subagentRuntime?.releaseRun(runId);
1920
2094
  }
1921
2095
  }
@@ -2339,7 +2513,7 @@ function compactionConfigFor(compaction) {
2339
2513
  : {}),
2340
2514
  };
2341
2515
  }
2342
- async function* runTurn({ fileReadTracker, provider, compactionConfig, consolidateInto, backgroundJobs, backgroundJobOwner, fallbackProviders, model, tools, pluginManager, skillRegistry, skills, scope, pathBuilder, workingDirectory, limits, additionalDirectories, sandboxWorkspace, rules, structuredOutput, reviewAnswer, maxAnswerReviews, promoteMemory, prepareStep, resumeHandler, taskStore, systemPrompt, messages, projectInstructionContext, opts, taskGateway, completionInbox, promptContributions, runtimeToolOverrides, webSearch, web, sandboxProvider, sandboxTeardownTimeoutMs, onRunEvent, }) {
2516
+ async function* runTurn({ emergencySave, retainedToolPreviewChars, fileReadTracker, provider, compactionConfig, consolidateInto, backgroundJobs, backgroundJobOwner, fallbackProviders, model, tools, pluginManager, skillRegistry, skills, scope, pathBuilder, workingDirectory, limits, additionalDirectories, sandboxWorkspace, rules, structuredOutput, reviewAnswer, maxAnswerReviews, promoteMemory, prepareStep, resumeHandler, taskStore, systemPrompt, messages, projectInstructionContext, opts, taskGateway, completionInbox, promptContributions, runtimeToolOverrides, webSearch, web, sandboxProvider, sandboxTeardownTimeoutMs, onRunEvent, }) {
2343
2517
  const signal = opts?.signal;
2344
2518
  // One presenter for the whole stream, built from the registry this scope
2345
2519
  // already holds. Its absence HERE is what forced presentation to be name
@@ -2348,6 +2522,7 @@ async function* runTurn({ fileReadTracker, provider, compactionConfig, consolida
2348
2522
  const presenter = createToolPresenter(tools);
2349
2523
  try {
2350
2524
  const events = query({
2525
+ ...(retainedToolPreviewChars !== undefined ? { retainedToolPreviewChars } : {}),
2351
2526
  ...(fileReadTracker ? { fileReadTracker } : {}),
2352
2527
  ...(structuredOutput ? { structuredOutput } : {}),
2353
2528
  provider,
@@ -2376,19 +2551,15 @@ async function* runTurn({ fileReadTracker, provider, compactionConfig, consolida
2376
2551
  compactionConfig,
2377
2552
  ...(consolidateInto ? { consolidateInto } : {}),
2378
2553
  ...(backgroundJobs ? { backgroundJobs, backgroundJobOwner } : {}),
2379
- // The CLI owns its process end to end, so it can safely hand the
2380
- // termination path to the kernel: a Ctrl-C mid-run now leaves a
2381
- // dump under the injected hierarchy's emergency partition instead of
2382
- // losing the turn.
2383
- emergencySave: true,
2554
+ // Interactive chat may own process exit. An enclosing resident host
2555
+ // instead drains the run and writes its finish/runner receipts first.
2556
+ emergencySave,
2384
2557
  runConfig: {
2385
2558
  model,
2386
2559
  ...(sandboxProvider ? { sandbox: { workspace: sandboxWorkspace } } : {}),
2387
2560
  ...(opts?.effort !== undefined ? { effort: opts.effort } : {}),
2388
2561
  ...(webSearch ? { webSearch } : {}),
2389
- timeoutMs: CLI_INTERACTIVE_RUN_TIMEOUT_MS,
2390
- tokenBudget: limits?.tokenBudget ?? 0,
2391
- maxIterations: limits?.maxIterations ?? 50,
2562
+ ...resolveRunGuards(limits),
2392
2563
  maxResponseTokens: 8192,
2393
2564
  permissionMode: 'auto',
2394
2565
  },
@@ -2535,6 +2706,7 @@ export function toAgentEvent(event, presenter) {
2535
2706
  return {
2536
2707
  kind: 'delta',
2537
2708
  text: event.text,
2709
+ ...(event.textPart ? { textPart: event.textPart } : {}),
2538
2710
  ...(event.messageId ? { messageId: event.messageId } : {}),
2539
2711
  ...(event.runId ? { runId: event.runId } : {}),
2540
2712
  };