@wrongstack/core 0.309.1 → 0.310.0

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 (116) hide show
  1. package/dist/chronicle/index.js +113 -41
  2. package/dist/chronicle/metrics-ingest.d.ts +7 -0
  3. package/dist/chronicle/metrics-schema.d.ts +4 -1
  4. package/dist/chronicle/project-server.js +94 -38
  5. package/dist/chronicle/query.d.ts +1 -0
  6. package/dist/chronicle/sqlite-journal-schema.d.ts +2 -1
  7. package/dist/chronicle/types.d.ts +2 -0
  8. package/dist/{agent-status-helpers.d.ts → coordination/agent-status-helpers.d.ts} +1 -1
  9. package/dist/{agent-status-tracker.d.ts → coordination/agent-status-tracker.d.ts} +2 -2
  10. package/dist/{middleware → coordination}/collab-pause.d.ts +1 -1
  11. package/dist/coordination/director-kanban-queue-helpers.d.ts +1 -1
  12. package/dist/coordination/fleet-status-tool.d.ts +1 -1
  13. package/dist/coordination/index.d.ts +5 -1
  14. package/dist/coordination/index.js +2061 -346
  15. package/dist/coordination/kanban-dispatch-port.d.ts +30 -0
  16. package/dist/coordination/kanban-ops-port.d.ts +21 -0
  17. package/dist/coordination/mailbox-hooks.d.ts +1 -1
  18. package/dist/coordination/mailbox-project-server.js +14 -9
  19. package/dist/core/context.d.ts +35 -44
  20. package/dist/core/conversation-state.d.ts +16 -52
  21. package/dist/core/index.js +53 -16
  22. package/dist/core/provider-runner.d.ts +2 -2
  23. package/dist/core/run-env.d.ts +7 -28
  24. package/dist/core/streaming-response-builder.d.ts +2 -2
  25. package/dist/execution/index.js +111 -153
  26. package/dist/goal/index.js +100 -22
  27. package/dist/hq/auth-store.d.ts +9 -0
  28. package/dist/hq/cost-bridge.d.ts +1 -1
  29. package/dist/hq/index.js +24 -2
  30. package/dist/index.d.ts +12 -5
  31. package/dist/index.js +27806 -33937
  32. package/dist/infrastructure/index.js +210 -132
  33. package/dist/infrastructure/provider-cache-ledger.d.ts +1 -1
  34. package/dist/infrastructure/token-counter.d.ts +5 -1
  35. package/dist/kernel/events/file-events.d.ts +6 -0
  36. package/dist/kernel/events/provider-events.d.ts +10 -7
  37. package/dist/kernel/events/session-events.d.ts +4 -4
  38. package/dist/kernel/events/tool-events.d.ts +12 -2
  39. package/dist/plugin/index.js +105 -35
  40. package/dist/security/index.d.ts +1 -0
  41. package/dist/security/index.js +47 -18
  42. package/dist/security/kanban-boundary.d.ts +1 -1
  43. package/dist/security/kanban-governance-port.d.ts +28 -0
  44. package/dist/session-catalog/index.js +2 -3
  45. package/dist/session-catalog/project-server.js +2 -3
  46. package/dist/session-catalog/protocol.d.ts +1 -1
  47. package/dist/session-catalog/registry.d.ts +1 -1
  48. package/dist/session-catalog/store-schema.d.ts +1 -1
  49. package/dist/session-catalog/store.d.ts +1 -1
  50. package/dist/storage/annotations-store.d.ts +1 -1
  51. package/dist/storage/board-store-port.d.ts +45 -0
  52. package/dist/storage/completed-work-checkpoint.d.ts +1 -1
  53. package/dist/storage/config-loader/types.d.ts +1 -1
  54. package/dist/storage/event-bus-port.d.ts +27 -0
  55. package/dist/storage/file-session-writer.d.ts +47 -5
  56. package/dist/storage/goal-coordination.d.ts +1 -1
  57. package/dist/storage/goal-store.d.ts +1 -1
  58. package/dist/storage/index.d.ts +3 -4
  59. package/dist/storage/index.js +1063 -1459
  60. package/dist/storage/plan-store.d.ts +1 -1
  61. package/dist/storage/queue-store.d.ts +1 -1
  62. package/dist/storage/replay-log-store.d.ts +1 -1
  63. package/dist/storage/session-recovery.d.ts +10 -0
  64. package/dist/storage/session-resume-validation.d.ts +13 -1
  65. package/dist/storage/session-store/events.d.ts +1 -1
  66. package/dist/storage/session-store/load-session-data.d.ts +1 -1
  67. package/dist/storage/session-store/rename-session.d.ts +1 -1
  68. package/dist/storage/session-store/resume-session.d.ts +3 -1
  69. package/dist/storage/session-store/session-store-index.d.ts +2 -1
  70. package/dist/storage/session-store/types.d.ts +1 -1
  71. package/dist/storage/session-store.d.ts +70 -0
  72. package/dist/storage/session-summary-tracker.d.ts +8 -0
  73. package/dist/storage/session-write-buffer.d.ts +31 -2
  74. package/dist/storage/task-store.d.ts +1 -1
  75. package/dist/storage/todos-checkpoint.d.ts +1 -1
  76. package/dist/storage/tool-audit-log.d.ts +1 -1
  77. package/dist/tasking/index.js +100 -22
  78. package/dist/tasking/task-tracker.d.ts +24 -1
  79. package/dist/types/compactor.d.ts +2 -2
  80. package/dist/types/context.d.ts +212 -0
  81. package/dist/types/conversation-state.d.ts +109 -0
  82. package/dist/types/error-handler.d.ts +2 -2
  83. package/dist/types/file-event-record.d.ts +6 -0
  84. package/dist/types/index.d.ts +3 -3
  85. package/dist/types/index.js +17 -1
  86. package/dist/types/permission.d.ts +3 -3
  87. package/dist/types/plugin.d.ts +3 -3
  88. package/dist/types/provider-runner.d.ts +2 -2
  89. package/dist/types/provider.d.ts +10 -0
  90. package/dist/types/run-env.d.ts +32 -0
  91. package/dist/types/session.d.ts +3 -0
  92. package/dist/types/slash-command.d.ts +2 -2
  93. package/dist/types/token-counter.d.ts +30 -1
  94. package/dist/types/tool-executor.d.ts +2 -2
  95. package/dist/types/tool.d.ts +5 -5
  96. package/dist/utils/context-breakdown.d.ts +2 -2
  97. package/dist/utils/context-evidence.d.ts +12 -12
  98. package/dist/utils/crash-shield.d.ts +9 -0
  99. package/dist/utils/heap-watchdog.js +11 -3
  100. package/dist/utils/index.d.ts +1 -1
  101. package/dist/utils/index.js +72 -156
  102. package/dist/utils/regex-guard.d.ts +7 -30
  103. package/dist/utils/todos-format.d.ts +1 -1
  104. package/dist/utils/tree-kill.d.ts +2 -0
  105. package/dist/utils/tree-kill.js +1 -0
  106. package/instructions/coordination/subagent-baseline.md +10 -0
  107. package/instructions/system-lite.md +2 -0
  108. package/instructions/system-pro.md +40 -0
  109. package/instructions/system.md +15 -0
  110. package/package.json +6 -9
  111. package/dist/defaults/index.d.ts +0 -69
  112. package/dist/defaults/index.js +0 -38133
  113. /package/dist/{fleet-notifier.d.ts → coordination/fleet-notifier.d.ts} +0 -0
  114. /package/dist/{session-registry-atomic-file.d.ts → session-catalog/session-registry-atomic-file.d.ts} +0 -0
  115. /package/dist/{session-registry-types.d.ts → session-catalog/session-registry-types.d.ts} +0 -0
  116. /package/dist/{session-registry.d.ts → session-catalog/session-registry.d.ts} +0 -0
@@ -0,0 +1,30 @@
1
+ /**
2
+ * kanban-dispatch-port — composition seam for the Director's kanban
3
+ * dispatch and assignment operations (Roadmap #11).
4
+ *
5
+ * The coordination layer must not import `@wrongstack/kanban` at runtime;
6
+ * it resolves the concrete operations through this port, wired at the
7
+ * CLI composition root (see `setKanbanDispatch`). Type imports from
8
+ * `@wrongstack/kanban` are erased at compile time and do not create a
9
+ * runtime edge.
10
+ */
11
+ import type { CompleteDispatchInput, CompleteDispatchResult, FailDispatchInput, HeartbeatKanbanTaskAssignmentInput, KanbanAgentAssignment, KanbanAgentRunStatus, KanbanBoard, KanbanEventContext, KanbanSearchInput, KanbanSearchResult, ReserveDispatchInput, ReserveDispatchResult, StartDispatchInput, StartDispatchResult } from '@wrongstack/kanban';
12
+ export interface KanbanDispatchPort {
13
+ getBoard(projectRoot: string, boardId: string): Promise<KanbanBoard | null>;
14
+ listReadyTasks(projectRoot: string, input?: KanbanSearchInput & {
15
+ limit?: number | undefined;
16
+ }): Promise<KanbanSearchResult[]>;
17
+ reserveKanbanDispatch(projectRoot: string, input: ReserveDispatchInput): Promise<ReserveDispatchResult | null>;
18
+ startKanbanDispatch(projectRoot: string, input: StartDispatchInput): Promise<StartDispatchResult | null>;
19
+ completeKanbanDispatch(projectRoot: string, input: CompleteDispatchInput): Promise<CompleteDispatchResult | null>;
20
+ failKanbanDispatch(projectRoot: string, input: FailDispatchInput): Promise<KanbanBoard | null>;
21
+ updateTaskAssignment(projectRoot: string, boardId: string, taskId: string, patch: Partial<KanbanAgentAssignment> & {
22
+ status?: KanbanAgentRunStatus | undefined;
23
+ }, eventContext?: KanbanEventContext): Promise<KanbanBoard | null>;
24
+ heartbeatTaskAssignment(projectRoot: string, boardId: string, taskId: string, input?: HeartbeatKanbanTaskAssignmentInput): Promise<KanbanBoard | null>;
25
+ }
26
+ /** Composition-root hook: register the real kanban-backed implementation. */
27
+ export declare function setKanbanDispatch(impl: KanbanDispatchPort): void;
28
+ /** Runtime accessor used by director-tools; throws loudly when unwired. */
29
+ export declare function kanbanDispatch(): KanbanDispatchPort;
30
+ //# sourceMappingURL=kanban-dispatch-port.d.ts.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Kanban operations port for the coordination area (director queue tools).
3
+ *
4
+ * The director queue helpers previously imported `describeKanbanBoundary`
5
+ * (a pure formatter) directly from `@wrongstack/kanban`, keeping a
6
+ * core → kanban runtime edge (roadmap #11 / report §2.1). This port is
7
+ * the leaf contract; the CLI composition root registers the real
8
+ * implementation at boot alongside the storage BoardStorePort.
9
+ *
10
+ * Type-only imports of kanban's data shapes remain permitted — they erase
11
+ * at compile time and do not create a runtime edge.
12
+ */
13
+ import type { KanbanBoundaryPolicy } from '@wrongstack/kanban';
14
+ export interface KanbanBoundaryOpsPort {
15
+ describeKanbanBoundary(policy: KanbanBoundaryPolicy | undefined): string;
16
+ }
17
+ /** Composition-root hook: register the real kanban-backed implementation. */
18
+ export declare function setKanbanBoundaryOps(impl: KanbanBoundaryOpsPort): void;
19
+ /** Resolve the boundary-ops port (throws when not wired, mirroring BoardStorePort). */
20
+ export declare function kanbanBoundaryOps(): KanbanBoundaryOpsPort;
21
+ //# sourceMappingURL=kanban-ops-port.d.ts.map
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * @module mailbox-hooks
13
13
  */
14
- import type { Mailbox } from '../coordination/mailbox-types.js';
14
+ import type { Mailbox } from './mailbox-types.js';
15
15
  export interface MailboxHooksOptions {
16
16
  /** The mailbox instance. */
17
17
  mailbox: Mailbox;
@@ -384,7 +384,10 @@ var withFileLock = primitives.withFileLock;
384
384
  import * as fs from "node:fs";
385
385
  import * as fsp2 from "node:fs/promises";
386
386
  import * as path3 from "node:path";
387
- import { PerformanceObserver, constants as performanceConstants } from "node:perf_hooks";
387
+ import {
388
+ PerformanceObserver,
389
+ constants as performanceConstants
390
+ } from "node:perf_hooks";
388
391
  import * as v8 from "node:v8";
389
392
 
390
393
  // src/utils/memory-flight-recorder.ts
@@ -690,7 +693,8 @@ function createMemoryFlightRecorder(options = {}) {
690
693
  const now = options.now ?? Date.now;
691
694
  const startedAt2 = now();
692
695
  const root = options.diagnosticsRoot ?? path2.join(wstackGlobalRoot(), "diagnostics", "memory");
693
- const enabled = options.enabled ?? (process.env["WRONGSTACK_MEMORY_FLIGHT_RECORDER"] !== "0" && process.env["NODE_ENV"] !== "test");
696
+ const isBunRuntime = typeof globalThis.Bun !== "undefined";
697
+ const enabled = options.enabled ?? (!isBunRuntime && process.env["WRONGSTACK_MEMORY_FLIGHT_RECORDER"] !== "0" && process.env["NODE_ENV"] !== "test");
694
698
  const writer = options.captureWriter ?? (enabled ? new ContinuousMemoryCaptureWriter(root) : disabledWriter(path2.join(root, `${timestampSlug()}-pid-${process.pid}`)));
695
699
  const observations = [];
696
700
  const windowMs = options.windowMs ?? DEFAULT_WINDOW_MS;
@@ -800,7 +804,11 @@ function defaultHeapLogPath() {
800
804
  function takeHeapSample() {
801
805
  const m = process.memoryUsage();
802
806
  const heap = v8.getHeapStatistics();
803
- const spaces = v8.getHeapSpaceStatistics();
807
+ let spaces = [];
808
+ try {
809
+ spaces = v8.getHeapSpaceStatistics();
810
+ } catch {
811
+ }
804
812
  const spaceUsed = (name) => spaces.find((space) => space.space_name === name)?.space_used_size ?? 0;
805
813
  const limit = heap.heap_size_limit || 0;
806
814
  let activeResources = 0;
@@ -2230,8 +2238,8 @@ function previousCredentialTtlMs(credential) {
2230
2238
 
2231
2239
  // src/coordination/sqlite-mailbox-schema.ts
2232
2240
  import * as fs2 from "node:fs";
2233
- import { createRequire } from "node:module";
2234
2241
  import * as path5 from "node:path";
2242
+ import { loadRuntimeDatabaseSync } from "@wrongstack/persistence";
2235
2243
 
2236
2244
  // src/utils/sqlite-warning.ts
2237
2245
  var SQLITE_EXPERIMENTAL_WARNING_RE = /sqlite is an experimental feature/i;
@@ -2379,8 +2387,7 @@ var DatabaseSyncCtor;
2379
2387
  function loadDatabaseSync() {
2380
2388
  if (DatabaseSyncCtor) return DatabaseSyncCtor;
2381
2389
  return withSqliteExperimentalWarningSuppressed(() => {
2382
- const require2 = createRequire(import.meta.url);
2383
- DatabaseSyncCtor = require2("node:sqlite").DatabaseSync;
2390
+ DatabaseSyncCtor = loadRuntimeDatabaseSync();
2384
2391
  return DatabaseSyncCtor;
2385
2392
  });
2386
2393
  }
@@ -2536,9 +2543,7 @@ function migrateLegacyFiles(ctx) {
2536
2543
  }
2537
2544
  function readLegacyMessages(projectDir2) {
2538
2545
  try {
2539
- return parseMailboxFile(
2540
- fs2.readFileSync(path5.join(projectDir2, GLOBAL_MAILBOX_FILE), "utf8")
2541
- );
2546
+ return parseMailboxFile(fs2.readFileSync(path5.join(projectDir2, GLOBAL_MAILBOX_FILE), "utf8"));
2542
2547
  } catch (error) {
2543
2548
  if (error.code === "ENOENT") return [];
2544
2549
  throw error;
@@ -3,36 +3,14 @@ import type { ContextEvidenceState } from '../types/context-evidence.js';
3
3
  import type { FileEventRecord } from '../types/file-event-record.js';
4
4
  import type { Message } from '../types/messages.js';
5
5
  import type { Provider, Usage } from '../types/provider.js';
6
- import type { SessionWriter } from '../types/session.js';
6
+ import type { SessionEvent, SessionWriter } from '../types/session.js';
7
7
  import type { TokenCounter } from '../types/token-counter.js';
8
8
  import type { Tool } from '../types/tool.js';
9
+ import type { AgentContext, ContextMessageLimits } from '../types/context.js';
10
+ import type { RunEnv } from '../types/run-env.js';
9
11
  import { ConversationState } from './conversation-state.js';
10
- import type { RunEnv } from './run-env.js';
11
- export interface TodoItem {
12
- id: string;
13
- content: string;
14
- status: 'pending' | 'in_progress' | 'completed';
15
- activeForm?: string | undefined;
16
- /** When promoted from a plan item, stores the plan item's id. */
17
- promotedFromPlan?: string | undefined;
18
- /** When promoted from a task, stores the task's id. */
19
- promotedFromTask?: string | undefined;
20
- /** Durable Kanban owner when the todo row is a UI projection of a real card. */
21
- kanbanBoardId?: string | undefined;
22
- /** Durable Kanban card represented by this todo row. */
23
- kanbanTaskId?: string | undefined;
24
- /**
25
- * Titles of the unfinished work this row waits on, derived from the Kanban
26
- * card's dependencies. Always board-derived, never model-supplied.
27
- *
28
- * A blocked item is genuinely `pending` — it has not started — so this is an
29
- * extra field rather than a fourth status. What was missing was never the
30
- * state but the *reason*: the board computes readiness on every mutation,
31
- * then the projection dropped it, leaving blocked work indistinguishable
32
- * from ready work on screen and in the model's context.
33
- */
34
- blockedBy?: string[] | undefined;
35
- }
12
+ import type { TodoItem } from '../types/context.js';
13
+ export type { TodoItem };
36
14
  export interface RunOptions {
37
15
  signal?: AbortSignal | undefined;
38
16
  model?: string | undefined;
@@ -103,7 +81,7 @@ export interface ProviderMemoryEvidence {
103
81
  * the agent and user to navigate within the project without spawning a new
104
82
  * process. All changes must stay inside `projectRoot`.
105
83
  */
106
- export declare class Context implements RunEnv {
84
+ export declare class Context implements RunEnv, AgentContext {
107
85
  messages: Message[];
108
86
  /**
109
87
  * Maximum number of messages retained in the conversation history.
@@ -243,6 +221,10 @@ export declare class Context implements RunEnv {
243
221
  * so storage operations can include it in `storage.*` events.
244
222
  */
245
223
  traceId: string | undefined;
224
+ /** Logical provider request whose response produced the current tool calls. */
225
+ activeLogicalRequestId: string | undefined;
226
+ /** Content-addressed prompt composition for {@link activeLogicalRequestId}. */
227
+ activePromptManifestId: string | undefined;
246
228
  /**
247
229
  * Session id pinned to the currently-executing run. Set by `Agent.run()`
248
230
  * at run start and cleared when the run ends. Event-emission sites must
@@ -266,13 +248,14 @@ export declare class Context implements RunEnv {
266
248
  */
267
249
  eventSessionId(): string;
268
250
  /** Callbacks fired when `setWorkingDir()` changes the working directory. */
269
- private _onWorkingDirChanged;
251
+ /** WorkingDir-change callbacks; public for structural typing (Roadmap 10A). */
252
+ readonly _onWorkingDirChanged: Array<(newDir: string, oldDir: string) => void>;
270
253
  /**
271
254
  * Serializes externally requested provider/model changes. Request creation
272
255
  * waits on this barrier so an automatic continuation cannot capture the old
273
256
  * model while a user-triggered switch is still building its provider.
274
257
  */
275
- private _modelTransition;
258
+ _modelTransition: Promise<void>;
276
259
  runModelTransition<T>(transition: () => T | Promise<T>): Promise<T>;
277
260
  waitForModelTransition(): Promise<void>;
278
261
  /**
@@ -333,23 +316,27 @@ export declare class Context implements RunEnv {
333
316
  * mutations that go through `state.appendMessage()` etc. fire
334
317
  * `onChange`. New code should prefer the wrapper API.
335
318
  */
336
- private _state;
337
- private readonly _conversationJournalQueue;
338
- private _conversationJournalBytes;
339
- private _conversationJournalDrain;
319
+ _state: ConversationState | null;
320
+ readonly _conversationJournalQueue: Array<{
321
+ event: SessionEvent;
322
+ bytes: number;
323
+ writer: SessionWriter;
324
+ }>;
325
+ _conversationJournalBytes: number;
326
+ _conversationJournalDrain: Promise<void> | null;
340
327
  private static readonly CONVERSATION_JOURNAL_MAX_EVENTS;
341
328
  private static readonly CONVERSATION_JOURNAL_MAX_BYTES;
342
329
  private static readonly MAX_FILE_EVENTS;
343
330
  private static readonly MAX_SIDE_EFFECTS;
344
331
  /** Wait until every exact conversation-state event queued so far is in the writer buffer. */
345
332
  flushConversationJournal(): Promise<void>;
346
- private conversationJournalBytes;
347
- private _journalDropCount;
348
- private _journalDropWarnAt;
333
+ conversationJournalBytes(event: SessionEvent): number;
334
+ _journalDropCount: number;
335
+ _journalDropWarnAt: number;
349
336
  /** Throttled notice that a conversation event never reached the journal. */
350
- private warnConversationJournalDrop;
351
- private enqueueConversationJournal;
352
- private startConversationJournalDrain;
337
+ warnConversationJournalDrop(eventType: SessionEvent['type']): void;
338
+ enqueueConversationJournal(event: SessionEvent, writer: SessionWriter): void;
339
+ startConversationJournalDrain(): void;
353
340
  get state(): ConversationState;
354
341
  /**
355
342
  * Register a teardown hook tied to the current run's abort signal.
@@ -364,7 +351,10 @@ export declare class Context implements RunEnv {
364
351
  * For hooks that must survive across run boundaries (mailbox heartbeat,
365
352
  * awareness polling, HQ publisher), prefer `registerAgentHook` instead.
366
353
  */
367
- private abortHooks;
354
+ /** Run-scoped abort hooks (drained by drainAbortHooks). Public for structural typing (Roadmap 10A). */
355
+ readonly abortHooks: Set<() => void | Promise<void>>;
356
+ /** Retention limits honoring runtime subclass overrides of the statics. */
357
+ get messageLimits(): ContextMessageLimits;
368
358
  registerAbortHook(fn: () => void | Promise<void>): () => void;
369
359
  drainAbortHooks(): Promise<void>;
370
360
  /**
@@ -377,7 +367,8 @@ export declare class Context implements RunEnv {
377
367
  * awareness polling interval, HQ publisher connection, and auto-compaction
378
368
  * timer — resources that must survive from the first run to the last.
379
369
  */
380
- private agentHooks;
370
+ /** Session-lifetime teardown hooks (drained by drainAgentHooks). Public for structural typing (Roadmap 10A). */
371
+ readonly agentHooks: Set<() => void | Promise<void>>;
381
372
  registerAgentHook(fn: () => void | Promise<void>): () => void;
382
373
  drainAgentHooks(): Promise<void>;
383
374
  /**
@@ -421,7 +412,7 @@ export declare class Context implements RunEnv {
421
412
  * the tool will re-stat the file and re-populate the entry. The agent
422
413
  * never fails or misbehaves; it just pays one extra stat call.
423
414
  */
424
- private trimTrackedFiles;
415
+ trimTrackedFiles(): void;
425
416
  private static trimSet;
426
417
  private static trimMap;
427
418
  /** Clear accumulated file-read metadata after compaction or at boundaries
@@ -508,5 +499,5 @@ export declare class Context implements RunEnv {
508
499
  * tests and lightweight embedders stub Context with partial objects whose
509
500
  * `session` may be missing despite the non-optional type.
510
501
  */
511
- export declare function resolveEventSessionId(ctx: Context): string;
502
+ export declare function resolveEventSessionId(ctx: AgentContext): string;
512
503
  //# sourceMappingURL=context.d.ts.map
@@ -1,6 +1,9 @@
1
1
  import type { ContentBlock } from '../types/blocks.js';
2
2
  import type { Message } from '../types/messages.js';
3
- import type { Context, TodoItem } from './context.js';
3
+ import type { AgentContext, TodoItem } from '../types/context.js';
4
+ import type { ConversationStateApi, ReadonlyConversationState, StateChange } from '../types/conversation-state.js';
5
+ export type { ConversationStateApi, ReadonlyConversationState, StateChange };
6
+ export type StateChangeHandler = (change: StateChange, state: ConversationStateApi) => void;
4
7
  /**
5
8
  * Observable wrapper for mutable conversation state. Production code should
6
9
  * mutate messages, todos, and meta through this API so subscribers see a
@@ -13,51 +16,13 @@ import type { Context, TodoItem } from './context.js';
13
16
  * that reads `ctx.messages` directly still works — they are NOT safe for
14
17
  * external writes.
15
18
  */
16
- export type StateChange = {
17
- kind: 'message_appended';
18
- message: Message;
19
- } | {
20
- kind: 'messages_replaced';
21
- messages: readonly Message[];
22
- }
23
- /** The oldest `count` messages were evicted; see the `messages_dropped` SessionEvent. */
24
- | {
25
- kind: 'messages_dropped';
26
- count: number;
27
- } | {
28
- kind: 'message_updated';
29
- index: number;
30
- message: Message;
31
- } | {
32
- kind: 'todos_replaced';
33
- todos: readonly TodoItem[];
34
- /**
35
- * Final all-completed snapshot when the tactical list auto-clears.
36
- * Observational mirrors use this to move cards to Done instead of
37
- * interpreting the empty active list as "the work vanished".
38
- */
39
- completedSnapshot?: readonly TodoItem[] | undefined;
40
- } | {
41
- kind: 'meta_set';
42
- key: string;
43
- value: unknown;
44
- } | {
45
- kind: 'meta_deleted';
46
- key: string;
47
- } | {
48
- kind: 'meta_cleared';
49
- };
50
- export type StateChangeHandler = (change: StateChange, state: ConversationState) => void;
51
- export interface ReadonlyConversationState {
52
- readonly messages: readonly Message[];
53
- readonly todos: readonly TodoItem[];
54
- readonly meta: Readonly<Record<string, unknown>>;
55
- }
56
19
  export declare class ConversationState {
57
- private readonly ctx;
58
- private readonly listeners;
59
- private _revision;
60
- constructor(ctx: Context);
20
+ /** Owning context; public for structural typing (Roadmap 10A). */
21
+ readonly ctx: AgentContext;
22
+ /** Subscribers; public for structural typing (Roadmap 10A). */
23
+ readonly listeners: Set<StateChangeHandler>;
24
+ _revision: number;
25
+ constructor(ctx: AgentContext);
61
26
  get messages(): readonly Message[];
62
27
  get todos(): readonly TodoItem[];
63
28
  get meta(): Readonly<Record<string, unknown>>;
@@ -77,16 +42,15 @@ export declare class ConversationState {
77
42
  appendMessage(message: Message): void;
78
43
  /**
79
44
  * How many of the oldest messages must be dropped to satisfy both retention
80
- * caps — count ({@link Context.MAX_MESSAGES}) and size
81
- * ({@link Context.MAX_MESSAGE_TOKENS}). Returns 0 when the history already
82
- * fits, which is the overwhelmingly common case.
45
+ * caps — count and size (see {@link AgentContext.messageLimits}). Returns 0
46
+ * when the history already fits, which is the overwhelmingly common case.
83
47
  *
84
48
  * The size pass reads the per-message `_estTokens` cache populated at
85
49
  * mutation time, so it is a sum over numbers rather than a re-walk of
86
50
  * content blocks. It runs whenever the token cap is enabled; the count
87
51
  * cap determines the starting index for the sum but does not gate it.
88
52
  */
89
- private overflowCount;
53
+ overflowCount(arr: readonly Message[]): number;
90
54
  /**
91
55
  * Front eviction must not retain a `tool_result` after evicting the
92
56
  * immediately preceding assistant `tool_use`. Long tool-heavy sessions sit
@@ -100,7 +64,7 @@ export declare class ConversationState {
100
64
  * the minimum lossless representation; once enough newer messages exist, the
101
65
  * next eviction boundary naturally moves past both halves together.
102
66
  */
103
- private protocolSafeDropCount;
67
+ protocolSafeDropCount(arr: readonly Message[], drop: number): number;
104
68
  /**
105
69
  * Append a content block to the trailing user message's content array.
106
70
  * Mutates only that one message (a single indexed assignment) — avoids
@@ -124,10 +88,10 @@ export declare class ConversationState {
124
88
  * the compatibility arrays are intentionally not observed.
125
89
  */
126
90
  onChange(listener: StateChangeHandler): () => void;
127
- private emit;
91
+ emit(change: StateChange): void;
128
92
  }
129
93
  /**
130
94
  * Convenience constructor. The wrapper holds a reference, not a copy.
131
95
  */
132
- export declare function wrapAsState(ctx: Context): ConversationState;
96
+ export declare function wrapAsState(ctx: AgentContext): ConversationState;
133
97
  //# sourceMappingURL=conversation-state.d.ts.map
@@ -4762,7 +4762,9 @@ function hasToolUseBlock(message) {
4762
4762
  return message?.role === "assistant" && Array.isArray(message.content) && message.content.some((block) => block.type === "tool_use");
4763
4763
  }
4764
4764
  var ConversationState = class {
4765
+ /** Owning context; public for structural typing (Roadmap 10A). */
4765
4766
  ctx;
4767
+ /** Subscribers; public for structural typing (Roadmap 10A). */
4766
4768
  listeners = /* @__PURE__ */ new Set();
4767
4769
  _revision = 0;
4768
4770
  constructor(ctx) {
@@ -4817,9 +4819,8 @@ var ConversationState = class {
4817
4819
  }
4818
4820
  /**
4819
4821
  * How many of the oldest messages must be dropped to satisfy both retention
4820
- * caps — count ({@link Context.MAX_MESSAGES}) and size
4821
- * ({@link Context.MAX_MESSAGE_TOKENS}). Returns 0 when the history already
4822
- * fits, which is the overwhelmingly common case.
4822
+ * caps — count and size (see {@link AgentContext.messageLimits}). Returns 0
4823
+ * when the history already fits, which is the overwhelmingly common case.
4823
4824
  *
4824
4825
  * The size pass reads the per-message `_estTokens` cache populated at
4825
4826
  * mutation time, so it is a sum over numbers rather than a re-walk of
@@ -4827,9 +4828,7 @@ var ConversationState = class {
4827
4828
  * cap determines the starting index for the sum but does not gate it.
4828
4829
  */
4829
4830
  overflowCount(arr) {
4830
- const contextClass = this.ctx.constructor;
4831
- const maxMessages = contextClass.MAX_MESSAGES;
4832
- const maxMessageTokens = contextClass.MAX_MESSAGE_TOKENS;
4831
+ const { maxMessages, maxMessageTokens } = this.ctx.messageLimits;
4833
4832
  let drop = maxMessages > 0 ? Math.max(0, arr.length - maxMessages) : 0;
4834
4833
  if (maxMessageTokens <= 0) return this.protocolSafeDropCount(arr, drop);
4835
4834
  let total = 0;
@@ -5103,6 +5102,10 @@ var Context = class _Context {
5103
5102
  * so storage operations can include it in `storage.*` events.
5104
5103
  */
5105
5104
  traceId;
5105
+ /** Logical provider request whose response produced the current tool calls. */
5106
+ activeLogicalRequestId = void 0;
5107
+ /** Content-addressed prompt composition for {@link activeLogicalRequestId}. */
5108
+ activePromptManifestId = void 0;
5106
5109
  /**
5107
5110
  * Session id pinned to the currently-executing run. Set by `Agent.run()`
5108
5111
  * at run start and cleared when the run ends. Event-emission sites must
@@ -5128,6 +5131,7 @@ var Context = class _Context {
5128
5131
  return resolveEventSessionId(this);
5129
5132
  }
5130
5133
  /** Callbacks fired when `setWorkingDir()` changes the working directory. */
5134
+ /** WorkingDir-change callbacks; public for structural typing (Roadmap 10A). */
5131
5135
  _onWorkingDirChanged = [];
5132
5136
  /**
5133
5137
  * Serializes externally requested provider/model changes. Request creation
@@ -5383,7 +5387,16 @@ var Context = class _Context {
5383
5387
  * For hooks that must survive across run boundaries (mailbox heartbeat,
5384
5388
  * awareness polling, HQ publisher), prefer `registerAgentHook` instead.
5385
5389
  */
5390
+ /** Run-scoped abort hooks (drained by drainAbortHooks). Public for structural typing (Roadmap 10A). */
5386
5391
  abortHooks = /* @__PURE__ */ new Set();
5392
+ /** Retention limits honoring runtime subclass overrides of the statics. */
5393
+ get messageLimits() {
5394
+ const cls = this.constructor;
5395
+ return Object.freeze({
5396
+ maxMessages: cls.MAX_MESSAGES,
5397
+ maxMessageTokens: cls.MAX_MESSAGE_TOKENS
5398
+ });
5399
+ }
5387
5400
  registerAbortHook(fn) {
5388
5401
  this.abortHooks.add(fn);
5389
5402
  return () => this.abortHooks.delete(fn);
@@ -5408,6 +5421,7 @@ var Context = class _Context {
5408
5421
  * awareness polling interval, HQ publisher connection, and auto-compaction
5409
5422
  * timer — resources that must survive from the first run to the last.
5410
5423
  */
5424
+ /** Session-lifetime teardown hooks (drained by drainAgentHooks). Public for structural typing (Roadmap 10A). */
5411
5425
  agentHooks = /* @__PURE__ */ new Set();
5412
5426
  registerAgentHook(fn) {
5413
5427
  this.agentHooks.add(fn);
@@ -5588,6 +5602,9 @@ var Context = class _Context {
5588
5602
  agentName: this.agentName,
5589
5603
  provider: typeof this.provider === "object" ? this.provider.id : String(this.provider),
5590
5604
  model: this.model,
5605
+ ...this.activeLogicalRequestId ? { logicalRequestId: this.activeLogicalRequestId } : {},
5606
+ ...this.activePromptManifestId ? { promptManifestId: this.activePromptManifestId } : {},
5607
+ provenanceConfidence: this.activeLogicalRequestId && this.activePromptManifestId ? "explicit" : "unknown",
5591
5608
  toolName: input.toolName,
5592
5609
  toolUseId: input.toolUseId,
5593
5610
  scope,
@@ -5615,6 +5632,9 @@ var Context = class _Context {
5615
5632
  agentName: this.agentName,
5616
5633
  provider: record.provider,
5617
5634
  model: record.model,
5635
+ ...record.logicalRequestId ? { logicalRequestId: record.logicalRequestId } : {},
5636
+ ...record.promptManifestId ? { promptManifestId: record.promptManifestId } : {},
5637
+ provenanceConfidence: record.provenanceConfidence,
5618
5638
  toolName: input.toolName,
5619
5639
  toolUseId: input.toolUseId,
5620
5640
  scope,
@@ -6772,7 +6792,12 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
6772
6792
  const idVal = ev.id;
6773
6793
  const nameVal = ev.name;
6774
6794
  handleToolUseStart(state, { id: idVal, name: nameVal });
6775
- const emittedPayload = { sessionId, ctx, id: idVal ?? "unknown", name: nameVal ?? "unknown" };
6795
+ const emittedPayload = {
6796
+ sessionId,
6797
+ ctx,
6798
+ id: idVal ?? "unknown",
6799
+ name: nameVal ?? "unknown"
6800
+ };
6776
6801
  events.emit("provider.tool_use_start", emittedPayload);
6777
6802
  break;
6778
6803
  }
@@ -7075,6 +7100,8 @@ async function runProviderWithRetry(opts) {
7075
7100
  const { provider, request, signal, ctx, events, retry, logger, tracer } = opts;
7076
7101
  const logicalRequestId = randomUUID5();
7077
7102
  const promptManifest = createChroniclePromptManifest(request);
7103
+ ctx.activeLogicalRequestId = logicalRequestId;
7104
+ ctx.activePromptManifestId = promptManifest.manifestId;
7078
7105
  let attempt = 0;
7079
7106
  for (; ; ) {
7080
7107
  const attemptId = randomUUID5();
@@ -7085,6 +7112,7 @@ async function runProviderWithRetry(opts) {
7085
7112
  ...ctx.traceId ? { traceId: ctx.traceId } : {},
7086
7113
  ...ctx.agentId ? { agentId: ctx.agentId } : {},
7087
7114
  logicalRequestId,
7115
+ promptManifestId: promptManifest.manifestId,
7088
7116
  attemptId,
7089
7117
  attempt,
7090
7118
  providerId: provider.id,
@@ -7108,15 +7136,18 @@ async function runProviderWithRetry(opts) {
7108
7136
  });
7109
7137
  logger.debug(`Provider attempt ${attempt + 1} starting`, providerLogCtx(provider, request));
7110
7138
  try {
7111
- const res = await runWithNetworkTelemetry({
7112
- events,
7113
- sessionId: resolveEventSessionId(ctx),
7114
- ...ctx.traceId ? { traceId: ctx.traceId } : {},
7115
- ...ctx.agentId ? { agentId: ctx.agentId } : {},
7116
- attemptId,
7117
- initiator: "provider",
7118
- operationName: `${provider.id}.complete`
7119
- }, () => provider.capabilities.streaming ? streamProviderToResponse(provider, request, signal, ctx, events, logger) : provider.complete(request, { signal }));
7139
+ const res = await runWithNetworkTelemetry(
7140
+ {
7141
+ events,
7142
+ sessionId: resolveEventSessionId(ctx),
7143
+ ...ctx.traceId ? { traceId: ctx.traceId } : {},
7144
+ ...ctx.agentId ? { agentId: ctx.agentId } : {},
7145
+ attemptId,
7146
+ initiator: "provider",
7147
+ operationName: `${provider.id}.complete`
7148
+ },
7149
+ () => provider.capabilities.streaming ? streamProviderToResponse(provider, request, signal, ctx, events, logger) : provider.complete(request, { signal })
7150
+ );
7120
7151
  span?.setAttribute("provider.stopReason", res.stopReason);
7121
7152
  span?.setAttribute("provider.usage_in", res.usage.input);
7122
7153
  span?.setAttribute("provider.usage_out", res.usage.output);
@@ -8857,6 +8888,8 @@ function createAgentToolHandler(a) {
8857
8888
  a.events.emit("tool.executed", {
8858
8889
  sessionId: resolveEventSessionId(a.ctx),
8859
8890
  ...a.ctx.traceId ? { traceId: a.ctx.traceId } : {},
8891
+ ...a.ctx.activeLogicalRequestId ? { logicalRequestId: a.ctx.activeLogicalRequestId } : {},
8892
+ ...a.ctx.activePromptManifestId ? { promptManifestId: a.ctx.activePromptManifestId } : {},
8860
8893
  agentId: a.ctx.agentId,
8861
8894
  agentName: a.ctx.agentName,
8862
8895
  id: toolUseId,
@@ -9368,6 +9401,8 @@ var Agent = class {
9368
9401
  const sessionId = sessionWriter.id;
9369
9402
  this.ctx.activeRunSessionWriter = sessionWriter;
9370
9403
  this.ctx.activeRunSessionId = sessionId;
9404
+ this.ctx.activeLogicalRequestId = void 0;
9405
+ this.ctx.activePromptManifestId = void 0;
9371
9406
  controller.onAbort(() => this.ctx.drainAbortHooks());
9372
9407
  controller.onAbort(async () => {
9373
9408
  await sessionWriter.flush().catch(() => {
@@ -9471,6 +9506,8 @@ var Agent = class {
9471
9506
  } finally {
9472
9507
  this.ctx.activeRunSessionId = void 0;
9473
9508
  this.ctx.activeRunSessionWriter = void 0;
9509
+ this.ctx.activeLogicalRequestId = void 0;
9510
+ this.ctx.activePromptManifestId = void 0;
9474
9511
  this._runInProgress = false;
9475
9512
  }
9476
9513
  }
@@ -3,12 +3,12 @@ import type { Logger } from '../types/logger.js';
3
3
  import type { Tracer } from '../types/observability.js';
4
4
  import type { Provider, Request, Response } from '../types/provider.js';
5
5
  import type { RetryPolicy } from '../types/retry-policy.js';
6
- import { type Context } from './context.js';
6
+ import type { AgentContext } from '../types/context.js';
7
7
  export interface RunProviderOptions {
8
8
  provider: Provider;
9
9
  request: Request;
10
10
  signal: AbortSignal;
11
- ctx: Context;
11
+ ctx: AgentContext;
12
12
  events: EventBus;
13
13
  retry: RetryPolicy;
14
14
  logger: Logger;
@@ -1,9 +1,5 @@
1
- import type { TextBlock } from '../types/blocks.js';
2
- import type { Provider } from '../types/provider.js';
3
- import type { SessionWriter } from '../types/session.js';
4
- import type { TokenCounter } from '../types/token-counter.js';
5
- import type { Tool } from '../types/tool.js';
6
1
  import type { Context } from './context.js';
2
+ import type { RunEnv } from '../types/run-env.js';
7
3
  /**
8
4
  * Immutable run environment — the set-once dependencies for an agent run.
9
5
  *
@@ -18,32 +14,15 @@ import type { Context } from './context.js';
18
14
  * references. The opposite direction (set things on Context) still works,
19
15
  * and `extractRunEnv` rebuilds the view if you need a snapshot.
20
16
  *
17
+ * Roadmap 10A: the interface itself now lives in `types/run-env.ts` (a
18
+ * dependency leaf that never imports `core/`), so the type module graph
19
+ * stays acyclic; this module keeps `extractRunEnv` and re-exports the
20
+ * interface for existing import paths.
21
+ *
21
22
  * Migration path: new APIs accept `RunEnv` instead of `Context` when they
22
23
  * only need read access. Existing APIs continue to accept `Context` until
23
24
  * a full split is scheduled.
24
25
  */
25
- export interface RunEnv {
26
- readonly provider: Provider;
27
- readonly session: SessionWriter;
28
- readonly signal: AbortSignal;
29
- readonly tokenCounter: TokenCounter;
30
- readonly cwd: string;
31
- readonly projectRoot: string;
32
- /** Mutable working directory — starts as `cwd`. */
33
- readonly workingDir: string;
34
- readonly model: string;
35
- readonly systemPrompt: readonly TextBlock[];
36
- readonly tools: readonly Tool[];
37
- readonly agentId: string;
38
- readonly agentName: string;
39
- }
40
- /**
41
- * Build a `RunEnv` view from a Context. The returned object is a shallow
42
- * frozen view — mutations to `Context` are visible (it's the same
43
- * references), but the view itself can't be mutated.
44
- *
45
- * Use this in subsystems that want to declare "I only need read access to
46
- * the env" without rewriting their signature to accept the full Context.
47
- */
26
+ export type { RunEnv } from '../types/run-env.js';
48
27
  export declare function extractRunEnv(ctx: Context): RunEnv;
49
28
  //# sourceMappingURL=run-env.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import type { EventBus } from '../kernel/events.js';
2
2
  import type { Logger } from '../types/logger.js';
3
3
  import type { Provider, Request, Response } from '../types/provider.js';
4
- import { type Context } from './context.js';
4
+ import type { AgentContext } from '../types/context.js';
5
5
  interface ThinkingEntry {
6
6
  textBuf: string;
7
7
  signature?: string | undefined;
@@ -73,6 +73,6 @@ export declare function handleMessageStop(state: StreamingState, ev: {
73
73
  stopReason?: Response['stopReason'] | undefined;
74
74
  usage?: Response['usage'] | undefined;
75
75
  }): void;
76
- export declare function streamProviderToResponse(provider: Provider, req: Request, signal: AbortSignal, ctx: Context, events: EventBus, logger: Logger): Promise<Response>;
76
+ export declare function streamProviderToResponse(provider: Provider, req: Request, signal: AbortSignal, ctx: AgentContext, events: EventBus, logger: Logger): Promise<Response>;
77
77
  export {};
78
78
  //# sourceMappingURL=streaming-response-builder.d.ts.map