@loucompanion/forge-bridge 0.1.3-dev.f66395c3ac21 → 0.1.4-dev.04a1e5b9e484

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 (223) hide show
  1. package/README.md +165 -1
  2. package/dist/bridge/bridge-chat.types.d.ts +111 -0
  3. package/dist/bridge/bridge-chat.types.js +401 -0
  4. package/dist/bridge/bridge.base.d.ts +9 -1
  5. package/dist/bridge/bridge.service.d.ts +129 -3
  6. package/dist/bridge/bridge.service.js +2427 -70
  7. package/dist/bridge/bridge.types.d.ts +218 -2
  8. package/dist/bridge/dispatch-result-outbox.d.ts +25 -0
  9. package/dist/bridge/dispatch-result-outbox.js +62 -0
  10. package/dist/bridge/dispatch-result-worker.d.ts +2 -0
  11. package/dist/bridge/dispatch-result-worker.js +122 -0
  12. package/dist/bridge/internals.d.ts +2 -0
  13. package/dist/bridge/internals.js +1 -0
  14. package/dist/bridge/readiness-result-outbox.d.ts +21 -0
  15. package/dist/bridge/readiness-result-outbox.js +54 -0
  16. package/dist/bridge/readiness-result-worker.d.ts +2 -0
  17. package/dist/bridge/readiness-result-worker.js +121 -0
  18. package/dist/bridge/runner-host-capabilities.d.ts +15 -0
  19. package/dist/bridge/runner-host-capabilities.js +93 -0
  20. package/dist/cleanup/cleanup.service.d.ts +14 -2
  21. package/dist/cleanup/cleanup.service.js +496 -3
  22. package/dist/cleanup/cleanup.types.d.ts +10 -0
  23. package/dist/cleanup/index.d.ts +1 -1
  24. package/dist/cleanup/index.js +1 -1
  25. package/dist/cleanup/internals.d.ts +2 -2
  26. package/dist/cleanup/internals.js +1 -1
  27. package/dist/cli/cli.help.d.ts +2 -0
  28. package/dist/cli/cli.help.js +217 -0
  29. package/dist/cli/cli.service.d.ts +55 -7
  30. package/dist/cli/cli.service.js +429 -219
  31. package/dist/cli/local-config.d.ts +7 -0
  32. package/dist/cli/local-config.js +51 -3
  33. package/dist/runtime/aspnet-runtime-prebuild.d.ts +36 -0
  34. package/dist/runtime/aspnet-runtime-prebuild.js +341 -0
  35. package/dist/runtime/attempt-database-broker.d.ts +89 -0
  36. package/dist/runtime/attempt-database-broker.js +390 -0
  37. package/dist/runtime/attempt-node-runtime-launcher.d.ts +18 -0
  38. package/dist/runtime/attempt-node-runtime-launcher.js +140 -0
  39. package/dist/runtime/attempt-path-operation.d.ts +21 -0
  40. package/dist/runtime/attempt-path-operation.js +94 -0
  41. package/dist/runtime/attempt-private-directory.d.ts +6 -0
  42. package/dist/runtime/attempt-private-directory.js +54 -0
  43. package/dist/runtime/attempt-process-supervisor-registry.d.ts +20 -0
  44. package/dist/runtime/attempt-process-supervisor-registry.js +64 -0
  45. package/dist/runtime/attempt-process-supervisor.d.ts +102 -0
  46. package/dist/runtime/attempt-process-supervisor.js +421 -0
  47. package/dist/runtime/canonical-source-file.d.ts +3 -0
  48. package/dist/runtime/canonical-source-file.js +54 -0
  49. package/dist/runtime/docker-engine-probe.d.ts +90 -0
  50. package/dist/runtime/docker-engine-probe.js +333 -0
  51. package/dist/runtime/generated-output-reset.d.ts +20 -0
  52. package/dist/runtime/generated-output-reset.js +157 -0
  53. package/dist/runtime/host-database-capability-boundary.d.ts +16 -0
  54. package/dist/runtime/host-database-capability-boundary.js +62 -0
  55. package/dist/runtime/host-diagnostics.d.ts +11 -0
  56. package/dist/runtime/host-diagnostics.js +192 -0
  57. package/dist/runtime/host-execution-root.d.ts +4 -0
  58. package/dist/runtime/host-execution-root.js +39 -0
  59. package/dist/runtime/host-listener-inspector.d.ts +51 -0
  60. package/dist/runtime/host-listener-inspector.js +385 -0
  61. package/dist/runtime/host-port-broker.d.ts +76 -0
  62. package/dist/runtime/host-port-broker.js +333 -0
  63. package/dist/runtime/host-runtime-contract.d.ts +58 -0
  64. package/dist/runtime/host-runtime-contract.js +813 -0
  65. package/dist/runtime/host-runtime-lease.d.ts +217 -0
  66. package/dist/runtime/host-runtime-lease.js +2487 -0
  67. package/dist/runtime/host-runtime-promotion.d.ts +32 -0
  68. package/dist/runtime/host-runtime-promotion.js +206 -0
  69. package/dist/runtime/memory-capacity.d.ts +70 -0
  70. package/dist/runtime/memory-capacity.js +123 -0
  71. package/dist/runtime/native-test-postgres-fallback.d.ts +25 -0
  72. package/dist/runtime/native-test-postgres-fallback.js +91 -0
  73. package/dist/runtime/node-host-runtime-adapter.d.ts +54 -0
  74. package/dist/runtime/node-host-runtime-adapter.js +286 -0
  75. package/dist/runtime/pinned-directory-process.d.ts +9 -0
  76. package/dist/runtime/pinned-directory-process.js +61 -0
  77. package/dist/runtime/pinned-source-directory.d.ts +17 -0
  78. package/dist/runtime/pinned-source-directory.js +94 -0
  79. package/dist/runtime/playwright-host-runtime-adapter.d.ts +21 -0
  80. package/dist/runtime/playwright-host-runtime-adapter.js +268 -0
  81. package/dist/runtime/prepared-runtime-identity.d.ts +6 -0
  82. package/dist/runtime/prepared-runtime-identity.js +218 -0
  83. package/dist/runtime/prepared-runtime-readiness.d.ts +30 -0
  84. package/dist/runtime/prepared-runtime-readiness.js +285 -0
  85. package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
  86. package/dist/runtime/runtime-isolation-lease.js +161 -0
  87. package/dist/runtime/workspace-resource-estimator.d.ts +17 -0
  88. package/dist/runtime/workspace-resource-estimator.js +174 -0
  89. package/dist/sandbox/command-runner.d.ts +4 -0
  90. package/dist/sandbox/command-runner.js +47 -0
  91. package/dist/sandbox/contract.d.ts +5 -0
  92. package/dist/sandbox/contract.js +276 -0
  93. package/dist/sandbox/event-budget.d.ts +11 -0
  94. package/dist/sandbox/event-budget.js +27 -0
  95. package/dist/sandbox/image-policy.d.ts +35 -0
  96. package/dist/sandbox/image-policy.js +247 -0
  97. package/dist/sandbox/index.d.ts +5 -0
  98. package/dist/sandbox/index.js +5 -0
  99. package/dist/sandbox/lease-watchdog.d.ts +18 -0
  100. package/dist/sandbox/lease-watchdog.js +67 -0
  101. package/dist/sandbox/preflight.d.ts +84 -0
  102. package/dist/sandbox/preflight.js +521 -0
  103. package/dist/sandbox/process.d.ts +20 -0
  104. package/dist/sandbox/process.js +209 -0
  105. package/dist/sandbox/provider-auth/codex.d.ts +26 -0
  106. package/dist/sandbox/provider-auth/codex.js +212 -0
  107. package/dist/sandbox/repository-lock.d.ts +6 -0
  108. package/dist/sandbox/repository-lock.js +129 -0
  109. package/dist/sandbox/runtime.d.ts +302 -0
  110. package/dist/sandbox/runtime.js +2368 -0
  111. package/dist/sandbox/sandbox.types.d.ts +93 -0
  112. package/dist/sandbox/sandbox.types.js +1 -0
  113. package/dist/sandbox/smoke.d.ts +76 -0
  114. package/dist/sandbox/smoke.js +380 -0
  115. package/dist/sandbox/worker-supervisor.d.ts +13 -0
  116. package/dist/sandbox/worker-supervisor.js +36 -0
  117. package/dist/sandbox/worker.d.ts +1 -0
  118. package/dist/sandbox/worker.js +225 -0
  119. package/dist/sandbox/workspace.service.d.ts +13 -0
  120. package/dist/sandbox/workspace.service.js +74 -0
  121. package/dist/session/managed-attempt-tools.d.ts +28 -0
  122. package/dist/session/managed-attempt-tools.js +1016 -0
  123. package/dist/session/managed-build-coordinator.d.ts +5 -0
  124. package/dist/session/managed-build-coordinator.js +106 -0
  125. package/dist/session/managed-build-receipt.d.ts +21 -0
  126. package/dist/session/managed-build-receipt.js +337 -0
  127. package/dist/session/managed-verification-timeouts.d.ts +8 -0
  128. package/dist/session/managed-verification-timeouts.js +17 -0
  129. package/dist/session/phase-completion.artifacts.d.ts +27 -0
  130. package/dist/session/phase-completion.artifacts.js +829 -0
  131. package/dist/session/phase-completion.d.ts +53 -42
  132. package/dist/session/phase-completion.helpers.d.ts +117 -0
  133. package/dist/session/phase-completion.helpers.js +707 -0
  134. package/dist/session/phase-completion.js +125 -3007
  135. package/dist/session/phase-completion.prompts.d.ts +14 -0
  136. package/dist/session/phase-completion.prompts.js +233 -0
  137. package/dist/session/phase-completion.runtime.d.ts +49 -0
  138. package/dist/session/phase-completion.runtime.js +840 -0
  139. package/dist/session/phase-completion.schemas.d.ts +3 -0
  140. package/dist/session/phase-completion.schemas.js +859 -0
  141. package/dist/session/phase-completion.validation.d.ts +38 -0
  142. package/dist/session/phase-completion.validation.js +918 -0
  143. package/dist/session/pre-publication-validation.d.ts +75 -0
  144. package/dist/session/pre-publication-validation.js +661 -0
  145. package/dist/session/provider-cli/bridge-cli.conformance-fixtures.d.ts +72 -0
  146. package/dist/session/provider-cli/bridge-cli.conformance-fixtures.js +462 -0
  147. package/dist/session/provider-cli/ci-repair-readiness.d.ts +16 -0
  148. package/dist/session/provider-cli/ci-repair-readiness.js +110 -0
  149. package/dist/session/provider-cli/provider-cli.artifacts.d.ts +95 -0
  150. package/dist/session/provider-cli/provider-cli.artifacts.js +812 -0
  151. package/dist/session/provider-cli/provider-cli.base.d.ts +1 -0
  152. package/dist/session/provider-cli/provider-cli.deterministic-payloads.d.ts +12 -0
  153. package/dist/session/provider-cli/provider-cli.deterministic-payloads.js +279 -0
  154. package/dist/session/provider-cli/provider-cli.deterministic.d.ts +14 -0
  155. package/dist/session/provider-cli/provider-cli.deterministic.js +407 -0
  156. package/dist/session/provider-cli/provider-cli.process.d.ts +99 -0
  157. package/dist/session/provider-cli/provider-cli.process.js +874 -0
  158. package/dist/session/provider-cli/provider-cli.prompts.d.ts +52 -0
  159. package/dist/session/provider-cli/provider-cli.prompts.js +715 -0
  160. package/dist/session/provider-cli/provider-cli.qa-sidecars.d.ts +11 -0
  161. package/dist/session/provider-cli/provider-cli.qa-sidecars.js +66 -0
  162. package/dist/session/provider-cli/provider-cli.repair-epoch.d.ts +6 -0
  163. package/dist/session/provider-cli/provider-cli.repair-epoch.js +11 -0
  164. package/dist/session/provider-cli/provider-cli.runtime.d.ts +22 -0
  165. package/dist/session/provider-cli/provider-cli.runtime.js +92 -0
  166. package/dist/session/provider-cli/provider-cli.service.d.ts +47 -53
  167. package/dist/session/provider-cli/provider-cli.service.js +1258 -2497
  168. package/dist/session/provider-cli/provider-cli.types.d.ts +8 -0
  169. package/dist/session/provider-cli/provider-output-state.d.ts +32 -0
  170. package/dist/session/provider-cli/provider-output-state.js +817 -0
  171. package/dist/session/provider-cli/provider-usage.d.ts +7 -0
  172. package/dist/session/provider-cli/provider-usage.js +28 -5
  173. package/dist/session/session-redaction.d.ts +3 -0
  174. package/dist/session/session-redaction.js +448 -0
  175. package/dist/session/session.base.d.ts +6 -1
  176. package/dist/session/session.service.d.ts +8 -2
  177. package/dist/session/session.service.js +586 -59
  178. package/dist/session/session.types.d.ts +324 -3
  179. package/dist/session/session.types.js +939 -10
  180. package/dist/session/unreferenced-exports.d.ts +12 -0
  181. package/dist/session/unreferenced-exports.js +59 -0
  182. package/dist/shared/format/format-bytes.d.ts +1 -0
  183. package/dist/shared/format/format-bytes.js +9 -0
  184. package/dist/shared/git/git.js +2 -1
  185. package/dist/shared/path/path.d.ts +1 -0
  186. package/dist/shared/path/path.js +21 -1
  187. package/dist/shared/process/dotnet-toolchain.d.ts +17 -0
  188. package/dist/shared/process/dotnet-toolchain.js +307 -0
  189. package/dist/shared/process/host-process-recovery.d.ts +12 -0
  190. package/dist/shared/process/host-process-recovery.js +79 -0
  191. package/dist/shared/process/host-process-supervisor.d.ts +59 -0
  192. package/dist/shared/process/host-process-supervisor.js +331 -0
  193. package/dist/shared/process/process.d.ts +3 -0
  194. package/dist/shared/process/process.js +13 -7
  195. package/dist/shared/process/windows-launch.d.ts +8 -0
  196. package/dist/shared/process/windows-launch.js +41 -0
  197. package/dist/shared/provider-capabilities.d.ts +4 -0
  198. package/dist/shared/provider-capabilities.js +88 -0
  199. package/dist/shared/redaction/redaction.d.ts +1 -0
  200. package/dist/shared/redaction/redaction.js +47 -4
  201. package/dist/shared/time/monotonic.d.ts +1 -0
  202. package/dist/shared/time/monotonic.js +8 -0
  203. package/dist/version.d.ts +1 -1
  204. package/dist/version.js +1 -1
  205. package/dist/worktree/artifact-injector.d.ts +3 -2
  206. package/dist/worktree/artifact-injector.js +82 -10
  207. package/dist/worktree/dependency-warmer.d.ts +53 -1
  208. package/dist/worktree/dependency-warmer.js +619 -11
  209. package/dist/worktree/index.d.ts +2 -1
  210. package/dist/worktree/index.js +1 -0
  211. package/dist/worktree/prior-phase-artifact-hydrator.d.ts +2 -0
  212. package/dist/worktree/prior-phase-artifact-hydrator.js +136 -0
  213. package/dist/worktree/story-qa-reference-hydrator.d.ts +4 -0
  214. package/dist/worktree/story-qa-reference-hydrator.js +253 -0
  215. package/dist/worktree/worktree-paths.d.ts +4 -0
  216. package/dist/worktree/worktree-paths.js +30 -0
  217. package/dist/worktree/worktree.base.d.ts +3 -0
  218. package/dist/worktree/worktree.service.d.ts +16 -3
  219. package/dist/worktree/worktree.service.js +334 -54
  220. package/dist/worktree/worktree.types.d.ts +9 -1
  221. package/package.json +12 -7
  222. package/runtime/windows-job-supervisor.ps1 +722 -0
  223. package/runtime/windows-listener-owner.ps1 +33 -0
@@ -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,448 @@
1
+ import { redactText, shouldRedactJsonProperty } from "../shared/redaction/redaction.js";
2
+ import { TextDecoder } from "node:util";
3
+ const integrityProtectedEventDataTypes = new Set(["sandbox_started"]);
4
+ const structuralSecretMessage = "runner result structural metadata contains protected secret material and cannot be redacted without changing protocol identity.";
5
+ const sidecarSecretMessage = "runner result sidecar contains protected secret material and cannot be redacted without invalidating integrity metadata.";
6
+ const artifactJsonMessage = "runner result artifact JSON is invalid or exceeds safe inspection limits; transport was rejected.";
7
+ const artifactJsonKeySecretMessage = "runner result artifact JSON property name contains protected secret material and cannot be redacted without changing protocol identity.";
8
+ const artifactJsonPrimitiveSecretMessage = "runner result artifact JSON primitive contains protected secret material and cannot be redacted without changing protocol identity.";
9
+ const maximumIntegrityJsonBytes = 8 * 1024 * 1024;
10
+ const maximumIntegrityJsonDepth = 128;
11
+ const maximumIntegrityJsonTokens = 262_144;
12
+ export function redactRunnerDispatchEvent(event, exactSecrets = []) {
13
+ assertStructuralFieldsDoNotContainSecrets([
14
+ event.role,
15
+ event.kind,
16
+ event.eventType,
17
+ event.wait?.kind,
18
+ event.wait?.scope,
19
+ event.wait?.wakeCondition,
20
+ event.wait?.contractOrigin,
21
+ ...(event.wait?.resourceKeys ?? []),
22
+ ], exactSecrets);
23
+ if (event.data !== undefined
24
+ && event.data !== null
25
+ && integrityProtectedEventDataTypes.has(event.eventType)) {
26
+ // Integrity-protected data cannot be rewritten because the runtime lease repeats the
27
+ // dispatch fence. Check the complete serialized payload so current and future resource
28
+ // identity fields cannot carry pending secret material across this boundary.
29
+ assertSerializedJsonDoesNotContainSecrets(event.data, exactSecrets, structuralSecretMessage);
30
+ }
31
+ // sandbox_started data is runner-authored protocol metadata. Its runtime
32
+ // lease repeats the dispatch fence so the API can validate ownership. The
33
+ // transcript redactor treats any serialized `*Token` field as a secret and
34
+ // must not rewrite that identity before transport or result persistence.
35
+ return {
36
+ ...event,
37
+ content: redactContent(event.content, exactSecrets),
38
+ ...(event.wait === undefined
39
+ ? {}
40
+ : { wait: event.wait === null ? null : redactWait(event.wait, exactSecrets) }),
41
+ ...(event.data === undefined || event.data === null
42
+ ? {}
43
+ : {
44
+ data: integrityProtectedEventDataTypes.has(event.eventType)
45
+ ? event.data
46
+ : redactContent(event.data, exactSecrets),
47
+ }),
48
+ };
49
+ }
50
+ function redactWait(wait, exactSecrets) {
51
+ return {
52
+ ...wait,
53
+ ...(wait.blockerTitle === undefined || wait.blockerTitle === null
54
+ ? {}
55
+ : { blockerTitle: redactContent(wait.blockerTitle, exactSecrets).slice(0, 500) }),
56
+ };
57
+ }
58
+ export function redactRunOnceResultPacket(result, exactSecrets = []) {
59
+ assertStructuralFieldsDoNotContainSecrets([result.resultCommitSha, result.publishedCommitSha], exactSecrets);
60
+ return {
61
+ ...result,
62
+ summary: redactContent(result.summary, exactSecrets),
63
+ ...(result.failure !== undefined
64
+ ? { failure: result.failure === null ? null : redactFailure(result.failure, exactSecrets) }
65
+ : {}),
66
+ ...(result.spec !== undefined
67
+ ? { spec: result.spec === null ? null : redactSpec(result.spec, exactSecrets) }
68
+ : {}),
69
+ ...(result.artifact !== undefined
70
+ ? { artifact: result.artifact === null ? null : redactArtifact(result.artifact, exactSecrets) }
71
+ : {}),
72
+ ...(result.artifacts !== undefined
73
+ ? {
74
+ artifacts: result.artifacts === null
75
+ ? null
76
+ : result.artifacts.map(item => redactArtifact(item, exactSecrets)),
77
+ }
78
+ : {}),
79
+ events: result.events.map(event => redactRunnerDispatchEvent(event, exactSecrets)),
80
+ };
81
+ }
82
+ function redactFailure(failure, exactSecrets) {
83
+ assertStructuralFieldsDoNotContainSecrets([failure.code, ...failure.causalChain.map(cause => cause.code)], exactSecrets);
84
+ return {
85
+ ...failure,
86
+ message: redactContent(failure.message, exactSecrets),
87
+ causalChain: failure.causalChain.map(cause => ({
88
+ ...cause,
89
+ message: redactContent(cause.message, exactSecrets),
90
+ })),
91
+ };
92
+ }
93
+ function redactSpec(spec, exactSecrets) {
94
+ assertStructuralFieldsDoNotContainSecrets([spec.repoPath, spec.path, spec.producedBy, spec.status], exactSecrets);
95
+ return { ...spec, body: redactContent(spec.body, exactSecrets) };
96
+ }
97
+ function redactArtifact(artifact, exactSecrets) {
98
+ assertStructuralFieldsDoNotContainSecrets([artifact.repoPath, artifact.path, artifact.producedBy, artifact.status, artifact.phase], exactSecrets);
99
+ assertSidecarsDoNotContainSecrets(artifact.sidecars, exactSecrets);
100
+ return {
101
+ ...artifact,
102
+ verdictJson: redactArtifactJson(artifact.verdictJson, exactSecrets),
103
+ ...(artifact.planJson === undefined || artifact.planJson === null
104
+ ? {}
105
+ : { planJson: redactArtifactJson(artifact.planJson, exactSecrets) }),
106
+ ...(artifact.evidenceJson === undefined || artifact.evidenceJson === null
107
+ ? {}
108
+ : { evidenceJson: redactArtifactJson(artifact.evidenceJson, exactSecrets) }),
109
+ ...(artifact.findingsJson === undefined || artifact.findingsJson === null
110
+ ? {}
111
+ : { findingsJson: redactArtifactJson(artifact.findingsJson, exactSecrets) }),
112
+ ...(artifact.implementationPlanJson === undefined || artifact.implementationPlanJson === null
113
+ ? {}
114
+ : { implementationPlanJson: redactArtifactJson(artifact.implementationPlanJson, exactSecrets) }),
115
+ ...(artifact.qaPlanJson === undefined || artifact.qaPlanJson === null
116
+ ? {}
117
+ : { qaPlanJson: redactArtifactJson(artifact.qaPlanJson, exactSecrets) }),
118
+ report: redactContent(artifact.report, exactSecrets),
119
+ // These bytes already passed the QA path/hash/size checks. The generic
120
+ // redactor strips contentBase64 fields and must not corrupt the payload.
121
+ ...(artifact.sidecars !== undefined ? { sidecars: artifact.sidecars } : {}),
122
+ };
123
+ }
124
+ function redactArtifactJson(value, exactSecrets) {
125
+ // Validate the original before redaction. Replacing an invalid value must never
126
+ // turn a malformed evidence bundle into an apparently valid QA artifact.
127
+ inspectJsonStringTokens(value, [], artifactJsonMessage);
128
+ const chunks = [];
129
+ let unchangedStart = 0;
130
+ let index = 0;
131
+ const replace = (start, end, replacement) => {
132
+ if (value.slice(start, end) === replacement)
133
+ return;
134
+ chunks.push(value.slice(unchangedStart, start), replacement);
135
+ unchangedStart = end;
136
+ };
137
+ while (index < value.length) {
138
+ if (value.charCodeAt(index) !== 0x22) {
139
+ const code = value.charCodeAt(index);
140
+ if (isJsonWhitespace(code) || isJsonPunctuation(code)) {
141
+ index += 1;
142
+ }
143
+ else {
144
+ const end = jsonValueEnd(value, index);
145
+ // Numeric credentials and other exact primitive matches are still secrets.
146
+ // Fail closed instead of silently changing evidence types to strings.
147
+ assertJsonStringDoesNotContainSecrets(value.slice(index, end), exactSecrets, artifactJsonPrimitiveSecretMessage);
148
+ index = end;
149
+ }
150
+ continue;
151
+ }
152
+ const token = decodeJsonStringToken(value, index, artifactJsonMessage);
153
+ const afterToken = skipJsonWhitespace(value, token.nextIndex);
154
+ if (value.charCodeAt(afterToken) === 0x3a) {
155
+ assertJsonStringDoesNotContainSecrets(token.decoded, exactSecrets, artifactJsonKeySecretMessage);
156
+ if (redactText(token.decoded, exactSecrets) !== token.decoded)
157
+ throw new Error(artifactJsonKeySecretMessage);
158
+ if (shouldRedactJsonProperty(token.decoded)) {
159
+ const valueStart = skipJsonWhitespace(value, afterToken + 1);
160
+ const valueEnd = jsonValueEnd(value, valueStart);
161
+ if (value[valueStart] !== '"'
162
+ || decodeJsonStringToken(value, valueStart, artifactJsonMessage).decoded !== "[REDACTED]") {
163
+ replace(valueStart, valueEnd, '"[REDACTED]"');
164
+ }
165
+ index = valueEnd;
166
+ continue;
167
+ }
168
+ }
169
+ else {
170
+ const redacted = redactText(token.decoded, exactSecrets);
171
+ if (redacted !== token.decoded)
172
+ replace(index, token.nextIndex, JSON.stringify(redacted));
173
+ }
174
+ index = token.nextIndex;
175
+ }
176
+ if (chunks.length === 0)
177
+ return value;
178
+ chunks.push(value.slice(unchangedStart));
179
+ const redacted = chunks.join("");
180
+ // Retain the same bounded contract even when a short exact secret expands into
181
+ // a longer redaction marker. No raw JSON regex or parse/stringify round-trip:
182
+ // untouched escapes, whitespace, duplicate properties and numeric text survive.
183
+ inspectJsonStringTokens(redacted, [], artifactJsonMessage);
184
+ return redacted;
185
+ }
186
+ function skipJsonWhitespace(value, start) {
187
+ let index = start;
188
+ while (index < value.length && isJsonWhitespace(value.charCodeAt(index)))
189
+ index += 1;
190
+ return index;
191
+ }
192
+ // Called only after bounded JSON validation. Locate a credential property's whole
193
+ // value (including objects/arrays) without decoding or rewriting adjacent data.
194
+ function jsonValueEnd(value, start) {
195
+ if (value.charCodeAt(start) === 0x22) {
196
+ return decodeJsonStringToken(value, start, artifactJsonMessage).nextIndex;
197
+ }
198
+ if (value[start] === "{" || value[start] === "[") {
199
+ let depth = 0;
200
+ for (let index = start; index < value.length; index += 1) {
201
+ if (value[index] === '"') {
202
+ index = decodeJsonStringToken(value, index, artifactJsonMessage).nextIndex - 1;
203
+ }
204
+ else if (value[index] === "{" || value[index] === "[") {
205
+ depth += 1;
206
+ }
207
+ else if (value[index] === "}" || value[index] === "]") {
208
+ depth -= 1;
209
+ if (depth === 0)
210
+ return index + 1;
211
+ }
212
+ }
213
+ throw new Error(artifactJsonMessage);
214
+ }
215
+ let index = start;
216
+ while (index < value.length
217
+ && !isJsonWhitespace(value.charCodeAt(index))
218
+ && value[index] !== "," && value[index] !== "}" && value[index] !== "]")
219
+ index += 1;
220
+ return index;
221
+ }
222
+ function redactContent(value, exactSecrets) {
223
+ return typeof value === "string" ? redactText(value, exactSecrets) : value;
224
+ }
225
+ function assertStructuralFieldsDoNotContainSecrets(fields, exactSecrets) {
226
+ for (const field of fields) {
227
+ if (field === undefined || field === null)
228
+ continue;
229
+ for (const secret of exactSecrets) {
230
+ if (secret.length > 0 && field.includes(secret)) {
231
+ throw new Error(structuralSecretMessage);
232
+ }
233
+ }
234
+ }
235
+ }
236
+ function assertSidecarsDoNotContainSecrets(sidecars, exactSecrets) {
237
+ if (!sidecars || exactSecrets.length === 0)
238
+ return;
239
+ for (const sidecar of sidecars) {
240
+ const decoded = Buffer.from(sidecar.contentBase64, "base64");
241
+ for (const secret of exactSecrets) {
242
+ if (secret.length === 0)
243
+ continue;
244
+ if (sidecar.path.includes(secret)
245
+ || sidecar.contentBase64.includes(secret)
246
+ || sidecar.contentHash.includes(secret)
247
+ || decoded.indexOf(Buffer.from(secret, "utf8")) >= 0) {
248
+ throw new Error(sidecarSecretMessage);
249
+ }
250
+ }
251
+ assertSidecarJsonDoesNotContainSecrets(decoded, sidecar.path, exactSecrets, sidecarSecretMessage);
252
+ }
253
+ }
254
+ function assertSerializedJsonDoesNotContainSecrets(value, exactSecrets, errorMessage) {
255
+ if (exactSecrets.length === 0)
256
+ return;
257
+ assertJsonStringDoesNotContainSecrets(value, exactSecrets, errorMessage);
258
+ inspectJsonStringTokens(value, exactSecrets, errorMessage);
259
+ }
260
+ function assertSidecarJsonDoesNotContainSecrets(value, path, exactSecrets, errorMessage) {
261
+ let decoded;
262
+ try {
263
+ decoded = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(value);
264
+ }
265
+ catch {
266
+ if (path.toLowerCase().endsWith(".json"))
267
+ throw new Error(errorMessage);
268
+ return;
269
+ }
270
+ if (!looksLikeJson(decoded, path))
271
+ return;
272
+ const hasUtf8Bom = value.length >= 3
273
+ && value[0] === 0xef
274
+ && value[1] === 0xbb
275
+ && value[2] === 0xbf;
276
+ if (hasUtf8Bom)
277
+ throw new Error(errorMessage);
278
+ inspectJsonStringTokens(decoded, exactSecrets, errorMessage);
279
+ }
280
+ function inspectJsonStringTokens(value, exactSecrets, errorMessage) {
281
+ if (Buffer.byteLength(value, "utf8") > maximumIntegrityJsonBytes) {
282
+ throw new Error(errorMessage);
283
+ }
284
+ const containers = [];
285
+ let index = 0;
286
+ let tokenCount = 0;
287
+ const countToken = () => {
288
+ tokenCount += 1;
289
+ if (tokenCount > maximumIntegrityJsonTokens)
290
+ throw new Error(errorMessage);
291
+ };
292
+ while (index < value.length) {
293
+ const code = value.charCodeAt(index);
294
+ if (code === 0x22) {
295
+ countToken();
296
+ const token = decodeJsonStringToken(value, index, errorMessage);
297
+ assertJsonStringDoesNotContainSecrets(token.decoded, exactSecrets, errorMessage);
298
+ index = token.nextIndex;
299
+ continue;
300
+ }
301
+ if (code === 0x7b || code === 0x5b) {
302
+ countToken();
303
+ containers.push(code);
304
+ if (containers.length > maximumIntegrityJsonDepth)
305
+ throw new Error(errorMessage);
306
+ index += 1;
307
+ continue;
308
+ }
309
+ if (code === 0x7d || code === 0x5d) {
310
+ countToken();
311
+ const expected = code === 0x7d ? 0x7b : 0x5b;
312
+ if (containers.pop() !== expected)
313
+ throw new Error(errorMessage);
314
+ index += 1;
315
+ continue;
316
+ }
317
+ if (code === 0x2c || code === 0x3a) {
318
+ index += 1;
319
+ continue;
320
+ }
321
+ if (isJsonWhitespace(code)) {
322
+ index += 1;
323
+ continue;
324
+ }
325
+ countToken();
326
+ do {
327
+ index += 1;
328
+ } while (index < value.length
329
+ && !isJsonWhitespace(value.charCodeAt(index))
330
+ && !isJsonPunctuation(value.charCodeAt(index)));
331
+ }
332
+ if (containers.length !== 0)
333
+ throw new Error(errorMessage);
334
+ try {
335
+ JSON.parse(value);
336
+ }
337
+ catch {
338
+ throw new Error(errorMessage);
339
+ }
340
+ }
341
+ function decodeJsonStringToken(value, openingQuoteIndex, errorMessage) {
342
+ const chunks = [];
343
+ let segmentStart = openingQuoteIndex + 1;
344
+ let index = segmentStart;
345
+ while (index < value.length) {
346
+ const code = value.charCodeAt(index);
347
+ if (code === 0x22) {
348
+ chunks.push(value.slice(segmentStart, index));
349
+ const decoded = chunks.join("");
350
+ if (!isWellFormedUnicode(decoded))
351
+ throw new Error(errorMessage);
352
+ return { decoded, nextIndex: index + 1 };
353
+ }
354
+ if (code < 0x20)
355
+ throw new Error(errorMessage);
356
+ if (code !== 0x5c) {
357
+ index += 1;
358
+ continue;
359
+ }
360
+ chunks.push(value.slice(segmentStart, index));
361
+ index += 1;
362
+ if (index >= value.length)
363
+ throw new Error(errorMessage);
364
+ const escaped = value[index];
365
+ switch (escaped) {
366
+ case "\"":
367
+ chunks.push("\"");
368
+ index += 1;
369
+ break;
370
+ case "\\":
371
+ chunks.push("\\");
372
+ index += 1;
373
+ break;
374
+ case "/":
375
+ chunks.push("/");
376
+ index += 1;
377
+ break;
378
+ case "b":
379
+ chunks.push("\b");
380
+ index += 1;
381
+ break;
382
+ case "f":
383
+ chunks.push("\f");
384
+ index += 1;
385
+ break;
386
+ case "n":
387
+ chunks.push("\n");
388
+ index += 1;
389
+ break;
390
+ case "r":
391
+ chunks.push("\r");
392
+ index += 1;
393
+ break;
394
+ case "t":
395
+ chunks.push("\t");
396
+ index += 1;
397
+ break;
398
+ case "u": {
399
+ const hex = value.slice(index + 1, index + 5);
400
+ if (!/^[0-9a-fA-F]{4}$/u.test(hex))
401
+ throw new Error(errorMessage);
402
+ chunks.push(String.fromCharCode(Number.parseInt(hex, 16)));
403
+ index += 5;
404
+ break;
405
+ }
406
+ default: throw new Error(errorMessage);
407
+ }
408
+ segmentStart = index;
409
+ }
410
+ throw new Error(errorMessage);
411
+ }
412
+ function isWellFormedUnicode(value) {
413
+ for (let index = 0; index < value.length; index += 1) {
414
+ const code = value.charCodeAt(index);
415
+ if (code >= 0xd800 && code <= 0xdbff) {
416
+ if (index + 1 >= value.length)
417
+ return false;
418
+ const next = value.charCodeAt(index + 1);
419
+ if (next < 0xdc00 || next > 0xdfff)
420
+ return false;
421
+ index += 1;
422
+ }
423
+ else if (code >= 0xdc00 && code <= 0xdfff) {
424
+ return false;
425
+ }
426
+ }
427
+ return true;
428
+ }
429
+ function looksLikeJson(value, path) {
430
+ if (path.toLowerCase().endsWith(".json"))
431
+ return true;
432
+ const candidate = value.trimStart().replace(/^\uFEFF/u, "").trimStart();
433
+ return candidate.startsWith("{") || candidate.startsWith("[") || candidate.startsWith("\"");
434
+ }
435
+ function isJsonWhitespace(code) {
436
+ return code === 0x20 || code === 0x09 || code === 0x0a || code === 0x0d;
437
+ }
438
+ function isJsonPunctuation(code) {
439
+ return code === 0x7b || code === 0x7d || code === 0x5b || code === 0x5d
440
+ || code === 0x2c || code === 0x3a || code === 0x22;
441
+ }
442
+ function assertJsonStringDoesNotContainSecrets(value, exactSecrets, errorMessage) {
443
+ for (const secret of exactSecrets) {
444
+ if (secret.length > 0 && value.includes(secret)) {
445
+ throw new Error(errorMessage);
446
+ }
447
+ }
448
+ }
@@ -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
  }