@loucompanion/forge-bridge 0.1.3-dev.d1ca67076aa9 → 0.1.3-dev.e7b62d8fdd33

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 (170) hide show
  1. package/README.md +127 -0
  2. package/dist/bridge/bridge.base.d.ts +8 -1
  3. package/dist/bridge/bridge.service.d.ts +93 -3
  4. package/dist/bridge/bridge.service.js +1358 -47
  5. package/dist/bridge/bridge.types.d.ts +202 -1
  6. package/dist/bridge/runner-host-capabilities.d.ts +15 -0
  7. package/dist/bridge/runner-host-capabilities.js +93 -0
  8. package/dist/cleanup/cleanup.service.d.ts +14 -2
  9. package/dist/cleanup/cleanup.service.js +496 -3
  10. package/dist/cleanup/cleanup.types.d.ts +10 -0
  11. package/dist/cleanup/index.d.ts +1 -1
  12. package/dist/cleanup/index.js +1 -1
  13. package/dist/cleanup/internals.d.ts +2 -2
  14. package/dist/cleanup/internals.js +1 -1
  15. package/dist/cli/cli.help.js +47 -1
  16. package/dist/cli/cli.service.d.ts +45 -2
  17. package/dist/cli/cli.service.js +378 -26
  18. package/dist/cli/local-config.d.ts +7 -0
  19. package/dist/cli/local-config.js +51 -3
  20. package/dist/runtime/aspnet-runtime-prebuild.d.ts +36 -0
  21. package/dist/runtime/aspnet-runtime-prebuild.js +340 -0
  22. package/dist/runtime/attempt-node-runtime-launcher.d.ts +17 -0
  23. package/dist/runtime/attempt-node-runtime-launcher.js +105 -0
  24. package/dist/runtime/attempt-process-supervisor-registry.d.ts +20 -0
  25. package/dist/runtime/attempt-process-supervisor-registry.js +64 -0
  26. package/dist/runtime/attempt-process-supervisor.d.ts +97 -0
  27. package/dist/runtime/attempt-process-supervisor.js +386 -0
  28. package/dist/runtime/docker-engine-probe.d.ts +83 -0
  29. package/dist/runtime/docker-engine-probe.js +300 -0
  30. package/dist/runtime/generated-output-reset.d.ts +10 -0
  31. package/dist/runtime/generated-output-reset.js +103 -0
  32. package/dist/runtime/host-listener-inspector.d.ts +46 -0
  33. package/dist/runtime/host-listener-inspector.js +349 -0
  34. package/dist/runtime/host-port-broker.d.ts +76 -0
  35. package/dist/runtime/host-port-broker.js +333 -0
  36. package/dist/runtime/host-runtime-contract.d.ts +47 -0
  37. package/dist/runtime/host-runtime-contract.js +700 -0
  38. package/dist/runtime/host-runtime-lease.d.ts +197 -0
  39. package/dist/runtime/host-runtime-lease.js +2203 -0
  40. package/dist/runtime/host-runtime-promotion.d.ts +23 -0
  41. package/dist/runtime/host-runtime-promotion.js +159 -0
  42. package/dist/runtime/memory-capacity.d.ts +68 -0
  43. package/dist/runtime/memory-capacity.js +121 -0
  44. package/dist/runtime/node-host-runtime-adapter.d.ts +54 -0
  45. package/dist/runtime/node-host-runtime-adapter.js +284 -0
  46. package/dist/runtime/playwright-host-runtime-adapter.d.ts +21 -0
  47. package/dist/runtime/playwright-host-runtime-adapter.js +231 -0
  48. package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
  49. package/dist/runtime/runtime-isolation-lease.js +158 -0
  50. package/dist/runtime/workspace-resource-estimator.d.ts +17 -0
  51. package/dist/runtime/workspace-resource-estimator.js +174 -0
  52. package/dist/sandbox/command-runner.d.ts +4 -0
  53. package/dist/sandbox/command-runner.js +47 -0
  54. package/dist/sandbox/contract.d.ts +5 -0
  55. package/dist/sandbox/contract.js +276 -0
  56. package/dist/sandbox/event-budget.d.ts +11 -0
  57. package/dist/sandbox/event-budget.js +27 -0
  58. package/dist/sandbox/image-policy.d.ts +35 -0
  59. package/dist/sandbox/image-policy.js +247 -0
  60. package/dist/sandbox/index.d.ts +5 -0
  61. package/dist/sandbox/index.js +5 -0
  62. package/dist/sandbox/lease-watchdog.d.ts +18 -0
  63. package/dist/sandbox/lease-watchdog.js +67 -0
  64. package/dist/sandbox/preflight.d.ts +84 -0
  65. package/dist/sandbox/preflight.js +521 -0
  66. package/dist/sandbox/process.d.ts +20 -0
  67. package/dist/sandbox/process.js +209 -0
  68. package/dist/sandbox/provider-auth/codex.d.ts +26 -0
  69. package/dist/sandbox/provider-auth/codex.js +212 -0
  70. package/dist/sandbox/repository-lock.d.ts +5 -0
  71. package/dist/sandbox/repository-lock.js +121 -0
  72. package/dist/sandbox/runtime.d.ts +297 -0
  73. package/dist/sandbox/runtime.js +2338 -0
  74. package/dist/sandbox/sandbox.types.d.ts +93 -0
  75. package/dist/sandbox/sandbox.types.js +1 -0
  76. package/dist/sandbox/smoke.d.ts +76 -0
  77. package/dist/sandbox/smoke.js +380 -0
  78. package/dist/sandbox/worker-supervisor.d.ts +13 -0
  79. package/dist/sandbox/worker-supervisor.js +36 -0
  80. package/dist/sandbox/worker.d.ts +1 -0
  81. package/dist/sandbox/worker.js +225 -0
  82. package/dist/sandbox/workspace.service.d.ts +13 -0
  83. package/dist/sandbox/workspace.service.js +72 -0
  84. package/dist/session/managed-attempt-tools.d.ts +21 -0
  85. package/dist/session/managed-attempt-tools.js +912 -0
  86. package/dist/session/managed-build-coordinator.d.ts +5 -0
  87. package/dist/session/managed-build-coordinator.js +105 -0
  88. package/dist/session/managed-build-receipt.d.ts +21 -0
  89. package/dist/session/managed-build-receipt.js +335 -0
  90. package/dist/session/phase-completion.artifacts.d.ts +17 -0
  91. package/dist/session/phase-completion.artifacts.js +256 -26
  92. package/dist/session/phase-completion.d.ts +12 -2
  93. package/dist/session/phase-completion.helpers.d.ts +5 -0
  94. package/dist/session/phase-completion.helpers.js +51 -10
  95. package/dist/session/phase-completion.js +14 -3
  96. package/dist/session/phase-completion.prompts.d.ts +3 -3
  97. package/dist/session/phase-completion.prompts.js +24 -8
  98. package/dist/session/phase-completion.runtime.d.ts +13 -1
  99. package/dist/session/phase-completion.runtime.js +189 -8
  100. package/dist/session/phase-completion.schemas.js +117 -4
  101. package/dist/session/phase-completion.validation.d.ts +4 -1
  102. package/dist/session/phase-completion.validation.js +153 -22
  103. package/dist/session/pre-publication-validation.d.ts +46 -0
  104. package/dist/session/pre-publication-validation.js +208 -0
  105. package/dist/session/provider-cli/ci-repair-readiness.d.ts +16 -0
  106. package/dist/session/provider-cli/ci-repair-readiness.js +110 -0
  107. package/dist/session/provider-cli/provider-cli.artifacts.d.ts +3 -2
  108. package/dist/session/provider-cli/provider-cli.artifacts.js +65 -7
  109. package/dist/session/provider-cli/provider-cli.base.d.ts +1 -0
  110. package/dist/session/provider-cli/provider-cli.deterministic.d.ts +2 -0
  111. package/dist/session/provider-cli/provider-cli.deterministic.js +68 -9
  112. package/dist/session/provider-cli/provider-cli.process.d.ts +26 -0
  113. package/dist/session/provider-cli/provider-cli.process.js +474 -23
  114. package/dist/session/provider-cli/provider-cli.prompts.d.ts +16 -2
  115. package/dist/session/provider-cli/provider-cli.prompts.js +224 -24
  116. package/dist/session/provider-cli/provider-cli.qa-sidecars.js +1 -0
  117. package/dist/session/provider-cli/provider-cli.repair-epoch.d.ts +6 -0
  118. package/dist/session/provider-cli/provider-cli.repair-epoch.js +11 -0
  119. package/dist/session/provider-cli/provider-cli.runtime.d.ts +1 -1
  120. package/dist/session/provider-cli/provider-cli.runtime.js +6 -3
  121. package/dist/session/provider-cli/provider-cli.service.d.ts +26 -3
  122. package/dist/session/provider-cli/provider-cli.service.js +650 -108
  123. package/dist/session/provider-cli/provider-cli.types.d.ts +6 -0
  124. package/dist/session/provider-cli/provider-output-state.d.ts +2 -0
  125. package/dist/session/provider-cli/provider-output-state.js +91 -16
  126. package/dist/session/provider-cli/provider-usage.d.ts +7 -0
  127. package/dist/session/provider-cli/provider-usage.js +28 -5
  128. package/dist/session/session-redaction.d.ts +3 -0
  129. package/dist/session/session-redaction.js +38 -0
  130. package/dist/session/session.base.d.ts +6 -1
  131. package/dist/session/session.service.d.ts +8 -2
  132. package/dist/session/session.service.js +549 -58
  133. package/dist/session/session.types.d.ts +124 -1
  134. package/dist/session/session.types.js +231 -4
  135. package/dist/shared/format/format-bytes.d.ts +1 -0
  136. package/dist/shared/format/format-bytes.js +9 -0
  137. package/dist/shared/path/path.d.ts +1 -0
  138. package/dist/shared/path/path.js +21 -1
  139. package/dist/shared/process/dotnet-toolchain.d.ts +14 -0
  140. package/dist/shared/process/dotnet-toolchain.js +179 -0
  141. package/dist/shared/process/host-process-recovery.d.ts +12 -0
  142. package/dist/shared/process/host-process-recovery.js +78 -0
  143. package/dist/shared/process/host-process-supervisor.d.ts +59 -0
  144. package/dist/shared/process/host-process-supervisor.js +330 -0
  145. package/dist/shared/process/process.d.ts +1 -0
  146. package/dist/shared/process/process.js +1 -0
  147. package/dist/shared/process/windows-launch.d.ts +1 -1
  148. package/dist/shared/process/windows-launch.js +6 -3
  149. package/dist/shared/provider-capabilities.d.ts +1 -0
  150. package/dist/shared/provider-capabilities.js +3 -0
  151. package/dist/shared/redaction/redaction.js +16 -4
  152. package/dist/version.d.ts +1 -1
  153. package/dist/version.js +1 -1
  154. package/dist/worktree/artifact-injector.d.ts +3 -2
  155. package/dist/worktree/artifact-injector.js +43 -4
  156. package/dist/worktree/dependency-warmer.d.ts +39 -1
  157. package/dist/worktree/dependency-warmer.js +351 -8
  158. package/dist/worktree/index.d.ts +2 -1
  159. package/dist/worktree/index.js +1 -0
  160. package/dist/worktree/prior-phase-artifact-hydrator.d.ts +2 -0
  161. package/dist/worktree/prior-phase-artifact-hydrator.js +136 -0
  162. package/dist/worktree/worktree-paths.d.ts +4 -0
  163. package/dist/worktree/worktree-paths.js +30 -0
  164. package/dist/worktree/worktree.base.d.ts +3 -0
  165. package/dist/worktree/worktree.service.d.ts +13 -2
  166. package/dist/worktree/worktree.service.js +238 -47
  167. package/dist/worktree/worktree.types.d.ts +5 -1
  168. package/package.json +8 -4
  169. package/runtime/windows-job-supervisor.ps1 +444 -0
  170. package/runtime/windows-listener-owner.ps1 +33 -0
@@ -1,13 +1,19 @@
1
1
  import type { RunOnceDispatchPacket, RunnerDispatchArtifact, RunnerDispatchEvent, RunnerDispatchSpec } from "../session.types.js";
2
2
  import type { WorktreeHandle } from "../../worktree/worktree.types.js";
3
+ import type { ProviderProcessRunner } from "./provider-cli.process.js";
3
4
  export interface ProviderCliRunContext {
4
5
  readonly dispatch: RunOnceDispatchPacket;
5
6
  readonly worktree: WorktreeHandle;
7
+ readonly runtimeEnvironment?: Readonly<Record<string, string>>;
8
+ readonly processRunner?: ProviderProcessRunner;
6
9
  readonly signal?: AbortSignal;
7
10
  emit(event: RunnerDispatchEvent): Promise<void>;
8
11
  }
9
12
  export interface ProviderCliDraftContext {
10
13
  readonly dispatch: RunOnceDispatchPacket;
14
+ readonly worktree: WorktreeHandle;
15
+ readonly runtimeEnvironment?: Readonly<Record<string, string>>;
16
+ readonly processRunner?: ProviderProcessRunner;
11
17
  readonly signal?: AbortSignal;
12
18
  emit(event: RunnerDispatchEvent): Promise<void>;
13
19
  }
@@ -5,6 +5,8 @@ export declare function clip(value: string, maxLength: number): string;
5
5
  export interface ProviderOutputState {
6
6
  readonly sessionLabel: string | null;
7
7
  finalMessage: string | null;
8
+ /** True only while the provider has reported a tool/command start without a matching completion. */
9
+ readonly hasActiveToolCall: boolean;
8
10
  /** Accumulated provider-reported token usage for this run, or null when none was reported. */
9
11
  readonly usage: ProviderUsage | null;
10
12
  consume(line: string): readonly RunnerDispatchEvent[];
@@ -25,12 +25,16 @@ class CursorStreamJsonState {
25
25
  finalMessage = null;
26
26
  emittedSession = false;
27
27
  accumulatedUsage = null;
28
+ activeToolCallCount = 0;
28
29
  get sessionLabel() {
29
30
  return this.sessionId ? `session ${this.sessionId}` : null;
30
31
  }
31
32
  get usage() {
32
33
  return this.accumulatedUsage;
33
34
  }
35
+ get hasActiveToolCall() {
36
+ return this.activeToolCallCount > 0;
37
+ }
34
38
  consume(line) {
35
39
  const event = parseJsonLine(line);
36
40
  if (!event) {
@@ -45,7 +49,8 @@ class CursorStreamJsonState {
45
49
  role: "runner",
46
50
  kind: "checkpoint",
47
51
  content: `Cursor session started: ${this.sessionId}.`,
48
- eventType: "provider_thread_started"
52
+ eventType: "provider_thread_started",
53
+ data: JSON.stringify({ threadId: this.sessionId })
49
54
  }];
50
55
  }
51
56
  }
@@ -65,6 +70,7 @@ class CursorStreamJsonState {
65
70
  const subtype = stringField(event, "subtype");
66
71
  const toolCall = objectField(event, "tool_call");
67
72
  if (subtype === "started") {
73
+ this.activeToolCallCount += 1;
68
74
  const summary = formatCursorToolCallStarted(toolCall);
69
75
  if (summary) {
70
76
  return [{
@@ -76,6 +82,7 @@ class CursorStreamJsonState {
76
82
  }
77
83
  }
78
84
  if (subtype === "completed") {
85
+ this.activeToolCallCount = Math.max(0, this.activeToolCallCount - 1);
79
86
  const completed = formatCursorToolCallCompleted(toolCall);
80
87
  if (completed) {
81
88
  return [{
@@ -110,13 +117,19 @@ class CursorStreamJsonState {
110
117
  if (Object.keys(usage).length === 0 && durationMs === 0) {
111
118
  return;
112
119
  }
120
+ const uncachedInputTokens = readUsageNumber(usage, "inputTokens");
121
+ const cacheReadTokens = readUsageNumber(usage, "cacheReadTokens");
122
+ const cacheCreationTokens = readUsageNumber(usage, "cacheWriteTokens");
113
123
  this.accumulatedUsage = addUsage(this.accumulatedUsage ?? emptyUsage("provider_reported"), {
114
- inputTokens: readUsageNumber(usage, "inputTokens"),
124
+ inputTokens: uncachedInputTokens + cacheReadTokens + cacheCreationTokens,
125
+ uncachedInputTokens,
115
126
  outputTokens: readUsageNumber(usage, "outputTokens"),
116
- cacheReadTokens: readUsageNumber(usage, "cacheReadTokens"),
117
- cacheCreationTokens: readUsageNumber(usage, "cacheWriteTokens"),
127
+ cacheReadTokens,
128
+ cacheCreationTokens,
118
129
  providerDurationMs: durationMs > 0 ? durationMs : null,
119
- source: "provider_reported"
130
+ source: "provider_reported",
131
+ inputTokenSemantics: "excludes_cache_tokens",
132
+ rawUsage: [{ provider: "cursor", usage }]
120
133
  });
121
134
  }
122
135
  }
@@ -224,18 +237,23 @@ class CodexJsonState {
224
237
  finalMessage = null;
225
238
  emittedThread = false;
226
239
  accumulatedUsage = null;
240
+ activeToolCallCount = 0;
227
241
  get sessionLabel() {
228
242
  return this.threadId ? `thread ${this.threadId}` : null;
229
243
  }
230
244
  get usage() {
231
245
  return this.accumulatedUsage;
232
246
  }
247
+ get hasActiveToolCall() {
248
+ return this.activeToolCallCount > 0;
249
+ }
233
250
  consume(line) {
234
251
  const event = parseJsonLine(line);
235
252
  if (!event) {
236
253
  return [];
237
254
  }
238
255
  const type = stringField(event, "type") ?? "";
256
+ this.trackToolActivity(type, event);
239
257
  if (type === "thread.started") {
240
258
  this.threadId = stringField(event, "thread_id");
241
259
  if (this.threadId && !this.emittedThread) {
@@ -244,7 +262,8 @@ class CodexJsonState {
244
262
  role: "runner",
245
263
  kind: "checkpoint",
246
264
  content: `Codex thread started: ${this.threadId}.`,
247
- eventType: "provider_thread_started"
265
+ eventType: "provider_thread_started",
266
+ data: JSON.stringify({ threadId: this.threadId })
248
267
  }];
249
268
  }
250
269
  }
@@ -265,7 +284,7 @@ class CodexJsonState {
265
284
  eventType: "provider_cli_error"
266
285
  }];
267
286
  }
268
- if (type === "item.completed" || type === "item.updated") {
287
+ if (type === "item.started" || type === "item.completed" || type === "item.updated") {
269
288
  const item = objectField(event, "item");
270
289
  const itemType = stringField(item, "type");
271
290
  if (itemType === "agent_message") {
@@ -287,7 +306,7 @@ class CodexJsonState {
287
306
  return [{
288
307
  role: "worker",
289
308
  kind: "checkpoint",
290
- content: `Codex command ${status || "completed"}: ${clip(command, 500)}`,
309
+ content: `Codex command ${status || (type === "item.started" ? "started" : "completed")}: ${clip(command, 500)}`,
291
310
  eventType: "provider_command"
292
311
  }];
293
312
  }
@@ -299,17 +318,39 @@ class CodexJsonState {
299
318
  }
300
319
  return [];
301
320
  }
321
+ trackToolActivity(type, event) {
322
+ if (type !== "item.started" && type !== "item.completed") {
323
+ return;
324
+ }
325
+ const item = objectField(event, "item");
326
+ const itemType = (stringField(item, "type") ?? "").toLowerCase();
327
+ const toolName = mcpToolName(item);
328
+ const isTool = itemType === "command_execution"
329
+ || itemType.includes("mcp")
330
+ || Boolean(toolName);
331
+ if (!isTool) {
332
+ return;
333
+ }
334
+ this.activeToolCallCount = type === "item.started"
335
+ ? this.activeToolCallCount + 1
336
+ : Math.max(0, this.activeToolCallCount - 1);
337
+ }
302
338
  accumulateUsage(event) {
303
339
  const usage = objectField(event, "usage");
304
340
  if (Object.keys(usage).length === 0) {
305
341
  return;
306
342
  }
343
+ const inputTokens = readUsageNumber(usage, "input_tokens");
344
+ const cacheReadTokens = readUsageNumber(usage, "cached_input_tokens");
307
345
  this.accumulatedUsage = addUsage(this.accumulatedUsage ?? emptyUsage("provider_reported"), {
308
- inputTokens: readUsageNumber(usage, "input_tokens"),
346
+ inputTokens,
347
+ uncachedInputTokens: Math.max(0, inputTokens - cacheReadTokens),
309
348
  outputTokens: readUsageNumber(usage, "output_tokens"),
310
- cacheReadTokens: readUsageNumber(usage, "cached_input_tokens"),
349
+ cacheReadTokens,
311
350
  reasoningOutputTokens: readUsageNumber(usage, "reasoning_output_tokens"),
312
- source: "provider_reported"
351
+ source: "provider_reported",
352
+ inputTokenSemantics: "includes_cached_input",
353
+ rawUsage: [{ provider: "codex", usage }]
313
354
  });
314
355
  }
315
356
  }
@@ -397,11 +438,45 @@ function mcpErrorSummary(item) {
397
438
  if (direct) {
398
439
  return clip(direct, 600);
399
440
  }
400
- const error = objectField(item, "error");
401
- return stringField(error, "message")
402
- ?? stringField(error, "summary")
403
- ?? stringField(objectField(item, "result"), "error")
404
- ?? null;
441
+ const messages = uniqueErrorMessages([
442
+ item.error,
443
+ item.result,
444
+ item.content,
445
+ ]);
446
+ return messages.length > 0 ? clip(messages.join(" Caused by: "), 600) : null;
447
+ }
448
+ function uniqueErrorMessages(values) {
449
+ const messages = [];
450
+ const seenMessages = new Set();
451
+ const seenObjects = new Set();
452
+ const visit = (value, depth) => {
453
+ if (depth > 8 || value === null || value === undefined)
454
+ return;
455
+ if (typeof value === "string") {
456
+ const message = value.trim();
457
+ if (message.length > 0 && !seenMessages.has(message)) {
458
+ seenMessages.add(message);
459
+ messages.push(message);
460
+ }
461
+ return;
462
+ }
463
+ if (typeof value !== "object" || seenObjects.has(value))
464
+ return;
465
+ seenObjects.add(value);
466
+ if (Array.isArray(value)) {
467
+ for (const entry of value)
468
+ visit(entry, depth + 1);
469
+ return;
470
+ }
471
+ const record = value;
472
+ for (const key of ["message", "summary", "error", "cause", "data", "details", "result", "content"]) {
473
+ if (key in record)
474
+ visit(record[key], depth + 1);
475
+ }
476
+ };
477
+ for (const value of values)
478
+ visit(value, 0);
479
+ return messages;
405
480
  }
406
481
  function safeProviderMcpData(item, serverName, toolName, status) {
407
482
  const elapsedMs = numericField(item, "elapsed_ms") ?? numericField(item, "duration_ms");
@@ -1,17 +1,24 @@
1
1
  import type { RunOnceDispatchPacket, RunnerDispatchEvent } from "../session.types.js";
2
2
  export type UsageSource = "provider_reported" | "cli_reported" | "estimated" | "unknown";
3
+ export type InputTokenSemantics = "includes_cached_input" | "excludes_cache_tokens" | "unknown";
4
+ export declare const PROVIDER_USAGE_SCHEMA_VERSION = 1;
3
5
  /**
4
6
  * Normalized, provider-agnostic token usage accumulated for a dispatch. Cost is never computed
5
7
  * here; the API recomputes cost from these tokens x model rates (ADR-0006).
6
8
  */
7
9
  export interface ProviderUsage {
10
+ /** Normalized total input. Cached input is a subset, never an additive peer. */
8
11
  readonly inputTokens: number;
12
+ readonly uncachedInputTokens: number | null;
9
13
  readonly outputTokens: number;
10
14
  readonly cacheReadTokens: number;
11
15
  readonly cacheCreationTokens: number;
12
16
  readonly reasoningOutputTokens: number;
13
17
  readonly providerDurationMs: number | null;
14
18
  readonly source: UsageSource;
19
+ readonly inputTokenSemantics: InputTokenSemantics;
20
+ /** Exact provider usage objects retained for audit/re-normalization. */
21
+ readonly rawUsage: readonly Record<string, unknown>[];
15
22
  }
16
23
  export declare function emptyUsage(source?: UsageSource): ProviderUsage;
17
24
  export declare function addUsage(base: ProviderUsage, next: Partial<ProviderUsage>): ProviderUsage;
@@ -1,27 +1,38 @@
1
+ export const PROVIDER_USAGE_SCHEMA_VERSION = 1;
1
2
  export function emptyUsage(source = "unknown") {
2
3
  return {
3
4
  inputTokens: 0,
5
+ uncachedInputTokens: 0,
4
6
  outputTokens: 0,
5
7
  cacheReadTokens: 0,
6
8
  cacheCreationTokens: 0,
7
9
  reasoningOutputTokens: 0,
8
10
  providerDurationMs: null,
9
- source
11
+ source,
12
+ inputTokenSemantics: "unknown",
13
+ rawUsage: []
10
14
  };
11
15
  }
12
16
  export function addUsage(base, next) {
17
+ const nextUncached = next.uncachedInputTokens;
18
+ const uncachedInputTokens = base.uncachedInputTokens === null || nextUncached === null
19
+ ? null
20
+ : base.uncachedInputTokens + nonNegative(nextUncached);
13
21
  return {
14
22
  inputTokens: base.inputTokens + nonNegative(next.inputTokens),
23
+ uncachedInputTokens,
15
24
  outputTokens: base.outputTokens + nonNegative(next.outputTokens),
16
25
  cacheReadTokens: base.cacheReadTokens + nonNegative(next.cacheReadTokens),
17
26
  cacheCreationTokens: base.cacheCreationTokens + nonNegative(next.cacheCreationTokens),
18
27
  reasoningOutputTokens: base.reasoningOutputTokens + nonNegative(next.reasoningOutputTokens),
19
28
  providerDurationMs: next.providerDurationMs ?? base.providerDurationMs,
20
- source: next.source ?? base.source
29
+ source: next.source ?? base.source,
30
+ inputTokenSemantics: mergeSemantics(base.inputTokenSemantics, next.inputTokenSemantics),
31
+ rawUsage: [...base.rawUsage, ...(next.rawUsage ?? [])]
21
32
  };
22
33
  }
23
34
  export function totalTokens(usage) {
24
- return usage.inputTokens + usage.outputTokens + usage.cacheReadTokens + usage.cacheCreationTokens;
35
+ return usage.inputTokens + usage.outputTokens;
25
36
  }
26
37
  export function hasUsage(usage) {
27
38
  return usage !== null && totalTokens(usage) > 0;
@@ -37,20 +48,26 @@ export function buildProviderUsageEvent(dispatch, usage) {
37
48
  lane: dispatch.execution.lane,
38
49
  phase: dispatch.cycleRun.phase,
39
50
  attempt: dispatch.cycleRun.attempt,
51
+ usage_schema_version: PROVIDER_USAGE_SCHEMA_VERSION,
52
+ input_token_semantics: usage.inputTokenSemantics,
40
53
  input_tokens: usage.inputTokens,
54
+ uncached_input_tokens: usage.uncachedInputTokens,
41
55
  output_tokens: usage.outputTokens,
42
56
  cache_read_tokens: usage.cacheReadTokens,
43
57
  cache_creation_tokens: usage.cacheCreationTokens,
44
58
  reasoning_output_tokens: usage.reasoningOutputTokens,
45
59
  metered: dispatch.execution.authKind === "api_key",
46
60
  usage_source: usage.source,
47
- provider_duration_ms: usage.providerDurationMs
61
+ provider_duration_ms: usage.providerDurationMs,
62
+ normalized_total_tokens: totalTokens(usage),
63
+ normalized_total_formula: "total_input + output; cached_input is a subset of total_input",
64
+ raw_usage: usage.rawUsage
48
65
  };
49
66
  const total = totalTokens(usage);
50
67
  return {
51
68
  role: "runner",
52
69
  kind: "checkpoint",
53
- content: `Provider usage: ${total} tokens (${usage.inputTokens} in / ${usage.outputTokens} out) for ${dispatch.execution.model}.`,
70
+ content: `Provider usage: ${total} tokens (${usage.inputTokens} total in, including ${usage.cacheReadTokens} cached / ${usage.outputTokens} out) for ${dispatch.execution.model}.`,
54
71
  eventType: "provider_usage",
55
72
  data: JSON.stringify(data)
56
73
  };
@@ -62,3 +79,9 @@ export function readUsageNumber(source, key) {
62
79
  function nonNegative(value) {
63
80
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
64
81
  }
82
+ function mergeSemantics(base, next) {
83
+ if (!next || next === "unknown") {
84
+ return base;
85
+ }
86
+ return base === "unknown" || base === next ? next : "unknown";
87
+ }
@@ -0,0 +1,3 @@
1
+ import type { RunOnceResultPacket, RunnerDispatchEvent } from "./session.types.js";
2
+ export declare function redactRunnerDispatchEvent(event: RunnerDispatchEvent, exactSecrets?: readonly string[]): RunnerDispatchEvent;
3
+ export declare function redactRunOnceResultPacket(result: RunOnceResultPacket, exactSecrets?: readonly string[]): RunOnceResultPacket;
@@ -0,0 +1,38 @@
1
+ import { redactSecrets } from "../shared/redaction/redaction.js";
2
+ const integrityProtectedEventDataTypes = new Set(["sandbox_started"]);
3
+ export function redactRunnerDispatchEvent(event, exactSecrets = []) {
4
+ if (!event.data || !integrityProtectedEventDataTypes.has(event.eventType)) {
5
+ return redactSecrets(event, exactSecrets);
6
+ }
7
+ // sandbox_started data is runner-authored protocol metadata. Its runtime
8
+ // lease repeats the dispatch fence so the API can validate ownership. The
9
+ // transcript redactor treats any serialized `*Token` field as a secret and
10
+ // must not rewrite that identity before transport or result persistence.
11
+ const { data, ...transcript } = event;
12
+ return {
13
+ ...redactSecrets(transcript, exactSecrets),
14
+ data,
15
+ };
16
+ }
17
+ export function redactRunOnceResultPacket(result, exactSecrets = []) {
18
+ const { events, artifact, artifacts, ...payload } = result;
19
+ return {
20
+ ...redactSecrets(payload, exactSecrets),
21
+ ...(artifact !== undefined
22
+ ? { artifact: artifact === null ? null : redactArtifact(artifact, exactSecrets) }
23
+ : {}),
24
+ ...(artifacts !== undefined
25
+ ? { artifacts: artifacts === null ? null : artifacts.map(item => redactArtifact(item, exactSecrets)) }
26
+ : {}),
27
+ events: events.map(event => redactRunnerDispatchEvent(event, exactSecrets)),
28
+ };
29
+ }
30
+ function redactArtifact(artifact, exactSecrets) {
31
+ const { sidecars, ...payload } = artifact;
32
+ return {
33
+ ...redactSecrets(payload, exactSecrets),
34
+ // These bytes already passed the QA path/hash/size checks. The generic
35
+ // redactor strips contentBase64 fields and must not corrupt the payload.
36
+ ...(sidecars !== undefined ? { sidecars } : {}),
37
+ };
38
+ }
@@ -1,7 +1,12 @@
1
- import type { RunOnceDispatchPacket, RunOnceResultPacket, RunnerDispatchEvent } from "./session.types.js";
1
+ import type { HostRuntimeReadinessProbeRequest, HostRuntimeReadinessProbeResult, RunOnceDispatchPacket, RunOnceResultPacket, RunnerDispatchEvent } from "./session.types.js";
2
+ import type { HostRuntimeCapacitySnapshot, HostRuntimeReapOptions, HostRuntimeReapReport } from "../runtime/host-runtime-lease.js";
2
3
  export interface SessionEventSink {
3
4
  emit(event: RunnerDispatchEvent): Promise<void>;
4
5
  }
5
6
  export interface SessionService {
6
7
  runOnce(dispatch: RunOnceDispatchPacket, sink: SessionEventSink, signal?: AbortSignal): Promise<RunOnceResultPacket>;
8
+ updateLease?(dispatchAttemptId: string, fencingToken: string, expiresAt: string): Promise<void>;
9
+ reapHostRuntime?(options: HostRuntimeReapOptions): Promise<HostRuntimeReapReport | null>;
10
+ runtimeCapacity?(): HostRuntimeCapacitySnapshot | null;
11
+ probeHostRuntime?(request: HostRuntimeReadinessProbeRequest, signal?: AbortSignal): Promise<HostRuntimeReadinessProbeResult>;
7
12
  }
@@ -1,10 +1,16 @@
1
1
  import type { ProviderCliService } from "./provider-cli/provider-cli.base.js";
2
2
  import type { SessionEventSink, SessionService } from "./session.base.js";
3
- import type { RunOnceDispatchPacket, RunOnceResultPacket } from "./session.types.js";
3
+ import type { HostRuntimeReadinessProbeRequest, HostRuntimeReadinessProbeResult, RunOnceDispatchPacket, RunOnceResultPacket } from "./session.types.js";
4
4
  import type { WorktreeService } from "../worktree/worktree.base.js";
5
+ import { type HostRuntimeCapacitySnapshot, type HostRuntimeLeaseRuntime, type HostRuntimeReapOptions, type HostRuntimeReapReport } from "../runtime/host-runtime-lease.js";
5
6
  export declare class BridgeSessionService implements SessionService {
6
7
  private readonly worktrees;
7
8
  private readonly providerCli;
8
- constructor(worktrees?: WorktreeService, providerCli?: ProviderCliService);
9
+ private readonly hostRuntime?;
10
+ constructor(worktrees?: WorktreeService, providerCli?: ProviderCliService, hostRuntime?: HostRuntimeLeaseRuntime | undefined);
11
+ updateLease(dispatchAttemptId: string, fencingToken: string, expiresAt: string): Promise<void>;
12
+ reapHostRuntime(options: HostRuntimeReapOptions): Promise<HostRuntimeReapReport | null>;
13
+ runtimeCapacity(): HostRuntimeCapacitySnapshot | null;
14
+ probeHostRuntime(request: HostRuntimeReadinessProbeRequest, signal?: AbortSignal): Promise<HostRuntimeReadinessProbeResult>;
9
15
  runOnce(dispatch: RunOnceDispatchPacket, sink: SessionEventSink, signal?: AbortSignal): Promise<RunOnceResultPacket>;
10
16
  }