@astrosheep/keiyaku 2.9.4 → 2.9.5

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 (126) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +1 -0
  3. package/build/agents/harness/execution-handle.js +1 -0
  4. package/build/agents/harness/outcome.js +21 -1
  5. package/build/agents/harness/projection.js +147 -75
  6. package/build/agents/providers/codex-app-server/adapter.js +32 -0
  7. package/build/agents/providers/codex-app-server/events.js +23 -0
  8. package/build/agents/providers/pi/events.js +4 -3
  9. package/build/agents/types.js +2 -0
  10. package/build/cli/commands/akuma/akuma/meta.js +1 -1
  11. package/build/cli/commands/akuma/list/meta-ls.js +1 -1
  12. package/build/cli/commands/akuma/view/meta.js +1 -1
  13. package/build/cli/commands/contract/amend/meta.js +4 -4
  14. package/build/cli/commands/contract/arc/handler.js +1 -1
  15. package/build/cli/commands/contract/arc/meta.js +4 -4
  16. package/build/cli/commands/contract/audit/handler.js +16 -0
  17. package/build/cli/commands/contract/audit/meta.js +16 -0
  18. package/build/cli/commands/contract/bind/meta.js +20 -11
  19. package/build/cli/commands/contract/catalog.js +2 -0
  20. package/build/cli/commands/contract/forfeit/meta.js +1 -1
  21. package/build/cli/commands/contract/log/meta.js +1 -1
  22. package/build/cli/commands/contract/petition/handler.js +1 -2
  23. package/build/cli/commands/contract/petition/meta.js +6 -6
  24. package/build/cli/commands/contract/renew/handler.js +2 -2
  25. package/build/cli/commands/contract/renew/meta.js +4 -4
  26. package/build/cli/commands/metadata.js +3 -2
  27. package/build/cli/commands/projection/call/meta.js +4 -1
  28. package/build/cli/commands/projection/kill/meta.js +1 -1
  29. package/build/cli/commands/projection/revive/meta.js +4 -1
  30. package/build/cli/commands/projection/status/meta.js +1 -1
  31. package/build/cli/commands/projection/tell/handler.js +1 -1
  32. package/build/cli/commands/projection/tell/meta.js +4 -1
  33. package/build/cli/commands/projection/wait/meta.js +1 -1
  34. package/build/cli/commands/skills/install/meta.js +1 -1
  35. package/build/cli/commands/skills/skills/meta.js +1 -1
  36. package/build/cli/commands/system/completion/meta.js +1 -1
  37. package/build/cli/commands/system/dump-env/meta.js +1 -1
  38. package/build/cli/commands/system/guide/meta.js +1 -1
  39. package/build/cli/commands/task/add/handler.js +2 -2
  40. package/build/cli/commands/task/add/meta.js +4 -1
  41. package/build/cli/commands/task/doctor/meta.js +1 -1
  42. package/build/cli/commands/task/done/meta.js +1 -1
  43. package/build/cli/commands/task/drop/meta.js +1 -1
  44. package/build/cli/commands/task/ls/handler.js +10 -4
  45. package/build/cli/commands/task/ls/meta.js +3 -3
  46. package/build/cli/commands/task/show/meta.js +1 -1
  47. package/build/cli/commands/task/start/meta.js +1 -1
  48. package/build/cli/commands/task/stop/meta.js +1 -1
  49. package/build/cli/commands/task/task/meta.js +1 -1
  50. package/build/cli/commands/task/update/handler.js +2 -3
  51. package/build/cli/commands/task/update/meta.js +4 -1
  52. package/build/cli/flags.js +18 -3
  53. package/build/cli/help.js +3 -3
  54. package/build/cli/index.js +59 -75
  55. package/build/cli/output-stream-error-policy.js +19 -5
  56. package/build/cli/parse-flags.js +19 -2
  57. package/build/cli/parse-metadata.js +1 -7
  58. package/build/cli/parse-selectors.js +1 -0
  59. package/build/cli/parse.js +16 -7
  60. package/build/cli/projection-address.js +6 -1
  61. package/build/cli/render/audit.js +121 -0
  62. package/build/cli/render/misc.js +3 -1
  63. package/build/cli/render/projection-activity.js +12 -6
  64. package/build/cli/render/status.js +28 -18
  65. package/build/cli/render/success-response.js +6 -5
  66. package/build/cli/render/task.js +12 -43
  67. package/build/cli/render/terminal-failure.js +7 -2
  68. package/build/cli/render/wait.js +36 -8
  69. package/build/config/env-keys.js +2 -0
  70. package/build/config/env.js +3 -0
  71. package/build/core/amend.js +20 -94
  72. package/build/core/arc.js +14 -6
  73. package/build/core/audit.js +650 -0
  74. package/build/core/bind.js +1 -1
  75. package/build/core/call/execution.js +1 -0
  76. package/build/core/contract-view.js +2 -3
  77. package/build/core/draft.js +33 -20
  78. package/build/core/entry.js +3 -4
  79. package/build/core/markdown/lex.js +2 -1
  80. package/build/core/projection/generation/database.js +41 -16
  81. package/build/core/projection/generation/model.js +22 -2
  82. package/build/core/projection/generation/projection-generation-execution.js +20 -7
  83. package/build/core/projection/generation/projection-generation-launcher.js +4 -4
  84. package/build/core/projection/generation/projection-generation-runner.js +1 -0
  85. package/build/core/projection/generation/store.js +1 -1
  86. package/build/core/projection/generation/transitions.js +19 -3
  87. package/build/core/projection/index.js +2 -2
  88. package/build/core/projection/projection-execution-observer.js +35 -21
  89. package/build/core/projection/projection-life-observer.js +16 -2
  90. package/build/core/projection/projection-status.js +67 -22
  91. package/build/core/projection/projection-terminal-failure.js +2 -0
  92. package/build/core/projection/projection-wait.js +6 -9
  93. package/build/core/projection/projection-wake.js +30 -6
  94. package/build/core/renew.js +41 -22
  95. package/build/core/scope.js +129 -2
  96. package/build/core/seal.js +36 -15
  97. package/build/core/settlement/petition-claim-gates.js +4 -33
  98. package/build/core/settlement/petition-preview.js +1 -1
  99. package/build/core/settlement/petition.js +10 -3
  100. package/build/core/settlement/settlement.js +3 -10
  101. package/build/core/settlement/verification.js +59 -0
  102. package/build/core/status/board.js +48 -28
  103. package/build/core/stored-agent-event.js +3 -2
  104. package/build/core/task/board.js +1 -0
  105. package/build/core/task/commands.js +20 -12
  106. package/build/core/task/document.js +5 -0
  107. package/build/core/task/index.js +1 -1
  108. package/build/core/task/model.js +8 -1
  109. package/build/core/task/query.js +172 -0
  110. package/build/core/task/settlement-git.js +115 -16
  111. package/build/core/task/settlement-policy.js +17 -9
  112. package/build/core/task/source-board.js +1 -0
  113. package/build/core/task/task-contract.js +125 -51
  114. package/build/core/task/task-store-repository.js +1 -0
  115. package/build/core/task/task.js +2 -0
  116. package/build/flow-error.js +2 -4
  117. package/build/generated/version.js +2 -2
  118. package/build/git/diff/parsers.js +23 -13
  119. package/build/git/diff/structured.js +127 -0
  120. package/build/index.js +7 -7
  121. package/build/telemetry/logger.js +4 -2
  122. package/package.json +1 -1
  123. package/skills/keiyaku/SKILL.md +2 -2
  124. package/skills/keiyaku-akuma/SKILL.md +17 -6
  125. package/skills/keiyaku-task/SKILL.md +16 -7
  126. package/skills/keiyaku-workflow/SKILL.md +42 -13
@@ -15,6 +15,7 @@ export function buildCallTerms(snapshot, prompt, cwd, options) {
15
15
  prompt,
16
16
  sessionContinuationPromptPrefix,
17
17
  idleTimeoutMs: options.idleTimeoutMs ?? runtimeConfig.subagent.execIdleTimeoutMs,
18
+ executionTimeoutMs: options.executionTimeoutMs ?? runtimeConfig.subagent.execMaxDurationMs,
18
19
  maxCaptureChars: registration.maxCaptureChars(runtimeConfig),
19
20
  signal: options.signal,
20
21
  ...(options.initialTurn !== undefined ? { initialTurn: options.initialTurn } : {}),
@@ -167,5 +167,6 @@ export function createProjectionDriver(input) {
167
167
  })();
168
168
  await gracefulAbort;
169
169
  },
170
+ ...(input.probe ? { probe: input.probe } : {}),
170
171
  };
171
172
  }
@@ -64,7 +64,15 @@ export function failFromFailEvent(shared, state, failEvent) {
64
64
  exitCode: failEvent.exitCode ?? null,
65
65
  }, state.finalMessageCandidate, failEvent.diagnostics ?? diagnostics);
66
66
  }
67
- export function classifyFailure(shared, state, error, idleTimedOut) {
67
+ export function classifyFailure(shared, state, error, idleTimedOut, transportFailure, policyBudgetExceeded = false) {
68
+ if (policyBudgetExceeded) {
69
+ return failWithError(shared, state, {
70
+ code: "SUBAGENT_POLICY_BUDGET",
71
+ message: `${shared.terms.provider} execution policy budget expired`,
72
+ timeoutMs: shared.terms.executionTimeoutMs,
73
+ exitCode: null,
74
+ }, state.finalMessageCandidate);
75
+ }
68
76
  if (shared.dismissed || shared.externallyAborted) {
69
77
  return mintOutcome({
70
78
  status: "dismissed",
@@ -75,6 +83,18 @@ export function classifyFailure(shared, state, error, idleTimedOut) {
75
83
  warnings: [...state.warnings],
76
84
  });
77
85
  }
86
+ if (transportFailure) {
87
+ const message = transportFailure instanceof Error ? transportFailure.message : `${shared.terms.provider} session transport probe failed`;
88
+ const timeoutMs = isProviderTerminalError(transportFailure) && transportFailure.fact.kind === "failed"
89
+ ? transportFailure.fact.timeoutMs
90
+ : shared.terms.idleTimeoutMs;
91
+ return failWithError(shared, state, {
92
+ code: "SUBAGENT_TRANSPORT_ERROR",
93
+ message,
94
+ timeoutMs,
95
+ exitCode: null,
96
+ }, state.finalMessageCandidate);
97
+ }
78
98
  if (idleTimedOut) {
79
99
  return failWithError(shared, state, {
80
100
  code: "SUBAGENT_TIMEOUT",
@@ -5,6 +5,7 @@ import { overflowMarkerFromDropped, prepareAgentEventForPersistence, } from "./e
5
5
  import { classifyFailure, classifySettlementFailure, completeOutcome, createOutcomeState, failFromFailEvent, failWithError, processEvent, renderOutcomeCapturedText, } from "./outcome.js";
6
6
  import { startLiveProjection } from "./router.js";
7
7
  import { createAbortError } from "./runtime.js";
8
+ const POLICY_BUDGET_CLEANUP_GRACE_MS = 5_000;
8
9
  export function call(terms, options) {
9
10
  return createProjection(terms, undefined, options);
10
11
  }
@@ -154,7 +155,21 @@ function requestControlledGracefulAbort(shared, reason) {
154
155
  shared.gracefulAbort.catch(() => undefined);
155
156
  return shared.gracefulAbort;
156
157
  }
157
- function deliverDriverEvent(shared, state, live, event, refreshIdleTimeout, observerEnabled) {
158
+ function requestPolicyBudgetCleanup(shared, reason) {
159
+ const graceful = requestControlledGracefulAbort(shared, reason);
160
+ const force = () => {
161
+ void shared.live?.abort("force").catch(() => undefined);
162
+ };
163
+ const cleanupTimeout = setTimeout(force, POLICY_BUDGET_CLEANUP_GRACE_MS);
164
+ cleanupTimeout.unref?.();
165
+ void graceful.then(() => clearTimeout(cleanupTimeout), () => {
166
+ clearTimeout(cleanupTimeout);
167
+ force();
168
+ });
169
+ }
170
+ function deliverDriverEvent(shared, state, live, event, refreshIdleTimeout, knownActiveTools, observerEnabled) {
171
+ if (shared.terminal)
172
+ return;
158
173
  const prepared = prepareAgentEventForPersistence(event);
159
174
  if (observerEnabled.value && shared.observer) {
160
175
  try {
@@ -173,24 +188,63 @@ function deliverDriverEvent(shared, state, live, event, refreshIdleTimeout, obse
173
188
  }
174
189
  }
175
190
  shared.publicEvents.push(event);
191
+ if (event.type === "activity") {
192
+ if (event.phase === "started")
193
+ knownActiveTools.add(event.id);
194
+ if (event.phase === "completed")
195
+ knownActiveTools.delete(event.id);
196
+ }
176
197
  refreshIdleTimeout();
177
198
  processEvent(state, event);
178
199
  }
179
200
  async function settleProjection(shared) {
180
201
  const state = createOutcomeState(shared.terms.maxCaptureChars);
181
202
  let idleTimedOut = false;
203
+ let transportFailure;
204
+ let policyBudgetExceeded = false;
205
+ const knownActiveTools = new Set();
182
206
  let abortRequested = false;
183
207
  const observerEnabled = { value: shared.observer !== undefined };
184
208
  let idleTimeout;
209
+ let executionTimeout;
210
+ let liveDriver;
211
+ let probeInFlight;
212
+ let resolvePolicyBudget;
213
+ const policyBudget = new Promise((resolve) => {
214
+ resolvePolicyBudget = resolve;
215
+ });
185
216
  const refreshIdleTimeout = () => {
217
+ if (shared.terminal)
218
+ return;
186
219
  if (idleTimeout)
187
220
  clearTimeout(idleTimeout);
188
221
  idleTimeout = setTimeout(() => {
222
+ if (knownActiveTools.size > 0) {
223
+ if (!liveDriver?.probe || probeInFlight)
224
+ return;
225
+ probeInFlight = liveDriver.probe()
226
+ .then(() => refreshIdleTimeout())
227
+ .catch((error) => {
228
+ transportFailure = error;
229
+ return requestControlledGracefulAbort(shared, error);
230
+ })
231
+ .finally(() => {
232
+ probeInFlight = undefined;
233
+ });
234
+ probeInFlight.catch(() => undefined);
235
+ return;
236
+ }
189
237
  idleTimedOut = true;
190
238
  requestControlledGracefulAbort(shared, createAbortError(`${shared.terms.provider} exec idle timed out`));
191
239
  }, shared.terms.idleTimeoutMs);
192
240
  idleTimeout.unref?.();
193
241
  };
242
+ executionTimeout = setTimeout(() => {
243
+ policyBudgetExceeded = true;
244
+ requestPolicyBudgetCleanup(shared, createAbortError(`${shared.terms.provider} execution policy budget expired`));
245
+ resolvePolicyBudget();
246
+ }, shared.terms.executionTimeoutMs);
247
+ executionTimeout.unref?.();
194
248
  refreshIdleTimeout();
195
249
  const requestLiveAbort = () => {
196
250
  if (shared.explicitlyAborted)
@@ -201,90 +255,108 @@ async function settleProjection(shared) {
201
255
  requestControlledGracefulAbort(shared, shared.lifecycleAbort.signal.reason);
202
256
  };
203
257
  shared.lifecycleAbort.signal.addEventListener("abort", requestLiveAbort, { once: true });
204
- try {
205
- const live = await startLiveProjection(shared.terms, shared.lifecycleAbort.signal, shared.reviveTerms, refreshIdleTimeout, shared.observer);
206
- shared.onLive(live);
207
- resolveForceWaiters(shared, live);
208
- if (shared.lifecycleAbort.signal.aborted) {
209
- requestLiveAbort();
210
- }
258
+ const providerSettlement = (async () => {
211
259
  try {
212
- for await (const event of live.events) {
213
- deliverDriverEvent(shared, state, live, event, refreshIdleTimeout, observerEnabled);
260
+ const live = await startLiveProjection(shared.terms, shared.lifecycleAbort.signal, shared.reviveTerms, refreshIdleTimeout, shared.observer);
261
+ liveDriver = live;
262
+ shared.onLive(live);
263
+ resolveForceWaiters(shared, live);
264
+ if (shared.lifecycleAbort.signal.aborted) {
265
+ requestLiveAbort();
214
266
  }
215
- }
216
- catch (error) {
217
- if (error instanceof Error && error.code === "PROJECTION_STATE_ERROR") {
218
- const event = live.mintEvent({
219
- type: "fail",
220
- code: "PROJECTION_STATE_ERROR",
221
- message: error.message,
222
- });
223
- if (event)
224
- deliverDriverEvent(shared, state, live, event, refreshIdleTimeout, observerEnabled);
267
+ try {
268
+ for await (const event of live.events) {
269
+ deliverDriverEvent(shared, state, live, event, refreshIdleTimeout, knownActiveTools, observerEnabled);
270
+ }
225
271
  }
226
- else {
227
- const event = live.mintEvent({
228
- type: "diagnostic",
229
- text: `pump error: ${String(error)}`,
230
- });
231
- if (event)
232
- deliverDriverEvent(shared, state, live, event, refreshIdleTimeout, observerEnabled);
272
+ catch (error) {
273
+ if (error instanceof Error && error.code === "PROJECTION_STATE_ERROR") {
274
+ const event = live.mintEvent({
275
+ type: "fail",
276
+ code: "PROJECTION_STATE_ERROR",
277
+ message: error.message,
278
+ });
279
+ if (event)
280
+ deliverDriverEvent(shared, state, live, event, refreshIdleTimeout, knownActiveTools, observerEnabled);
281
+ }
282
+ else {
283
+ const event = live.mintEvent({
284
+ type: "diagnostic",
285
+ text: `pump error: ${String(error)}`,
286
+ });
287
+ if (event)
288
+ deliverDriverEvent(shared, state, live, event, refreshIdleTimeout, knownActiveTools, observerEnabled);
289
+ }
233
290
  }
291
+ const completionResult = await live.completion.then((value) => ({ ok: true, value }), (error) => ({ ok: false, error }));
292
+ const completionOutcome = completionResult.ok ? completionResult.value : undefined;
293
+ // Outcome classification is the terminal publication boundary. A
294
+ // controlled abort may let the provider run settle before its graceful
295
+ // cleanup promise does, so prove that shared settlement before publishing
296
+ // dismissed, failed-for-another-reason, or completed truth.
297
+ const gracefulResult = shared.gracefulAbort
298
+ ? await shared.gracefulAbort.then(() => ({ ok: true }), (error) => ({ ok: false, error }))
299
+ : undefined;
300
+ if (gracefulResult && !gracefulResult.ok && !transportFailure) {
301
+ return classifySettlementFailure(shared, state, gracefulResult.error);
302
+ }
303
+ if (policyBudgetExceeded || transportFailure) {
304
+ return classifyFailure(shared, state, completionResult.ok ? undefined : completionResult.error, idleTimedOut, transportFailure, policyBudgetExceeded);
305
+ }
306
+ if (state.failEvent) {
307
+ return failFromFailEvent(shared, state, state.failEvent);
308
+ }
309
+ if (!completionResult.ok) {
310
+ return classifyFailure(shared, state, completionResult.error, idleTimedOut, transportFailure, policyBudgetExceeded);
311
+ }
312
+ if (shared.lifecycleAbort.signal.aborted) {
313
+ return classifyFailure(shared, state, shared.lifecycleAbort.signal.reason, idleTimedOut, transportFailure, policyBudgetExceeded);
314
+ }
315
+ if (!state.doneEvent && !completionOutcome) {
316
+ return failWithError(shared, state, {
317
+ code: "SUBAGENT_EXEC_ERROR",
318
+ message: `${shared.terms.provider} exec ended before completion`,
319
+ timeoutMs: null,
320
+ exitCode: null,
321
+ }, state.finalMessageCandidate);
322
+ }
323
+ const finalMessage = state.doneEvent?.finalMessage ??
324
+ completionOutcome?.finalMessage ??
325
+ state.finalMessageCandidate ??
326
+ renderOutcomeCapturedText(state.outputBuffer).trim();
327
+ const session = state.doneEvent?.session ?? completionOutcome?.session;
328
+ if (!session) {
329
+ return failWithError(shared, state, {
330
+ code: "SUBAGENT_EXEC_ERROR",
331
+ message: `${shared.terms.provider} exec ended without a session handle`,
332
+ timeoutMs: null,
333
+ exitCode: null,
334
+ }, finalMessage);
335
+ }
336
+ return completeOutcome(shared, state, finalMessage, session);
234
337
  }
235
- const completionResult = await live.completion.then((value) => ({ ok: true, value }), (error) => ({ ok: false, error }));
236
- const completionOutcome = completionResult.ok ? completionResult.value : undefined;
237
- // Outcome classification is the terminal publication boundary. A
238
- // controlled abort may let the provider run settle before its graceful
239
- // cleanup promise does, so prove that shared settlement before publishing
240
- // dismissed, failed-for-another-reason, or completed truth.
241
- const gracefulResult = shared.gracefulAbort
242
- ? await shared.gracefulAbort.then(() => ({ ok: true }), (error) => ({ ok: false, error }))
243
- : undefined;
244
- if (gracefulResult && !gracefulResult.ok) {
245
- return classifySettlementFailure(shared, state, gracefulResult.error);
246
- }
247
- if (state.failEvent) {
248
- return failFromFailEvent(shared, state, state.failEvent);
249
- }
250
- if (!completionResult.ok) {
251
- return classifyFailure(shared, state, completionResult.error, idleTimedOut);
252
- }
253
- if (shared.lifecycleAbort.signal.aborted) {
254
- return classifyFailure(shared, state, shared.lifecycleAbort.signal.reason, idleTimedOut);
255
- }
256
- if (!state.doneEvent && !completionOutcome) {
257
- return failWithError(shared, state, {
258
- code: "SUBAGENT_EXEC_ERROR",
259
- message: `${shared.terms.provider} exec ended before completion`,
260
- timeoutMs: null,
261
- exitCode: null,
262
- }, state.finalMessageCandidate);
263
- }
264
- const finalMessage = state.doneEvent?.finalMessage ??
265
- completionOutcome?.finalMessage ??
266
- state.finalMessageCandidate ??
267
- renderOutcomeCapturedText(state.outputBuffer).trim();
268
- const session = state.doneEvent?.session ?? completionOutcome?.session;
269
- if (!session) {
270
- return failWithError(shared, state, {
271
- code: "SUBAGENT_EXEC_ERROR",
272
- message: `${shared.terms.provider} exec ended without a session handle`,
273
- timeoutMs: null,
274
- exitCode: null,
275
- }, finalMessage);
276
- }
277
- return completeOutcome(shared, state, finalMessage, session);
278
- }
279
- catch (error) {
280
- if (error instanceof FlowError) {
281
- throw error;
338
+ catch (error) {
339
+ if (error instanceof FlowError) {
340
+ throw error;
341
+ }
342
+ return classifyFailure(shared, state, error, idleTimedOut, transportFailure, policyBudgetExceeded);
282
343
  }
283
- return classifyFailure(shared, state, error, idleTimedOut);
344
+ })();
345
+ providerSettlement.catch(() => undefined);
346
+ try {
347
+ const settled = await Promise.race([
348
+ providerSettlement.then((outcome) => ({ kind: "provider", outcome })),
349
+ policyBudget.then(() => ({ kind: "policy-budget" })),
350
+ ]);
351
+ return settled.kind === "provider"
352
+ ? settled.outcome
353
+ : classifyFailure(shared, state, undefined, idleTimedOut, transportFailure, true);
284
354
  }
285
355
  finally {
286
356
  if (idleTimeout)
287
357
  clearTimeout(idleTimeout);
358
+ if (executionTimeout)
359
+ clearTimeout(executionTimeout);
288
360
  shared.lifecycleAbort.signal.removeEventListener("abort", requestLiveAbort);
289
361
  shared.terminal = true;
290
362
  resolveForceWaiters(shared, undefined);
@@ -13,6 +13,7 @@ function toCodexAppServerEffort(effort) {
13
13
  }
14
14
  export { codexAppServerSessionSchema } from "./session-schema.js";
15
15
  const MINIMUM_CODEX_APP_SERVER_VERSION = { major: 0, minor: 143, patch: 0 };
16
+ const CODEX_APP_SERVER_PROBE_TIMEOUT_MS = 1_000;
16
17
  /** Non-interactive defaults for turn/start (faye #316 policy A). */
17
18
  export const CODEX_APP_SERVER_TURN_APPROVAL_POLICY = "never";
18
19
  export { buildCodexAppServerWorkspaceWriteSandboxPolicy, configOwnsApprovalPolicy, configOwnsSandboxPolicy, } from "./policy.js";
@@ -172,10 +173,41 @@ export async function startCodexAppServer(prompt, cwd, options = {}) {
172
173
  void drainPendingTells();
173
174
  });
174
175
  };
176
+ const probe = async () => {
177
+ if (!server || !activeThreadId) {
178
+ throw createProviderFailure(`${CODEX_APP_SERVER_PROVIDER} session probe has no active thread`, {
179
+ code: "SUBAGENT_TRANSPORT_ERROR",
180
+ timeoutMs: CODEX_APP_SERVER_PROBE_TIMEOUT_MS,
181
+ });
182
+ }
183
+ let timer;
184
+ const deadline = new Promise((_, reject) => {
185
+ timer = setTimeout(() => reject(new Error("session probe timed out")), CODEX_APP_SERVER_PROBE_TIMEOUT_MS);
186
+ timer.unref?.();
187
+ });
188
+ try {
189
+ await Promise.race([
190
+ server.request("thread/read", { threadId: activeThreadId }),
191
+ deadline,
192
+ ]);
193
+ }
194
+ catch (error) {
195
+ throw createProviderFailure(`${CODEX_APP_SERVER_PROVIDER} session transport probe failed`, {
196
+ code: "SUBAGENT_TRANSPORT_ERROR",
197
+ timeoutMs: CODEX_APP_SERVER_PROBE_TIMEOUT_MS,
198
+ cause: error,
199
+ });
200
+ }
201
+ finally {
202
+ if (timer)
203
+ clearTimeout(timer);
204
+ }
205
+ };
175
206
  return createProjectionDriver({
176
207
  steerMode: "mid-execution",
177
208
  initialTurn: options.initialTurn,
178
209
  enqueue,
210
+ probe,
179
211
  onIngress: options.onIngress,
180
212
  async abort(mode) {
181
213
  interruptRequested = true;
@@ -22,6 +22,11 @@ export function codexAppServerNotificationDisposition(method, params) {
22
22
  return "acknowledged-unpersisted";
23
23
  if (CODEX_APP_SERVER_NOTIFICATION_MAPPED.has(method))
24
24
  return "mapped";
25
+ if (method === "item/started") {
26
+ const item = appServerRecord(appServerRecord(params)?.item);
27
+ if (item && typeof item.type === "string" && isCodexAppServerActivityItemType(item.type))
28
+ return "mapped";
29
+ }
25
30
  if (method === "account/rateLimits/updated" && codexAppServerRateLimitCause(params))
26
31
  return "mapped";
27
32
  return "unknown";
@@ -303,6 +308,24 @@ export function createCodexAppServerTurnMonitor(options) {
303
308
  });
304
309
  }
305
310
  }
311
+ if (notification.method === "item/started") {
312
+ const item = notification.params?.item;
313
+ if (!item)
314
+ throw new Error(`${CODEX_APP_SERVER_PROVIDER} item/started lacked required item`);
315
+ const id = coerceString(item.id);
316
+ const itemType = coerceString(item.type);
317
+ if (!id || !itemType)
318
+ throw new Error(`${CODEX_APP_SERVER_PROVIDER} item/started lacked required item id or type`);
319
+ if (isCodexAppServerActivityItemType(itemType)) {
320
+ builder.emit({
321
+ type: "activity",
322
+ phase: "started",
323
+ id,
324
+ tool: itemType,
325
+ call: appServerActivityCall(itemType, item),
326
+ });
327
+ }
328
+ }
306
329
  if (notification.method === "turn/started") {
307
330
  const turn = notification.params?.turn;
308
331
  const turnId = recordActiveTurnId(state, turn?.id, "notification");
@@ -142,14 +142,15 @@ function piUsageBody(event) {
142
142
  if (message?.role !== "assistant")
143
143
  return undefined;
144
144
  const usage = message.usage;
145
- if (!usage || typeof usage.input !== "number" || typeof usage.output !== "number")
145
+ if (!usage || typeof usage.input !== "number" || !Number.isFinite(usage.input)
146
+ || typeof usage.output !== "number" || !Number.isFinite(usage.output))
146
147
  return undefined;
147
148
  return {
148
149
  type: "usage",
149
150
  inputTokens: usage.input,
150
151
  outputTokens: usage.output,
151
- ...(typeof usage.cacheRead === "number" ? { cachedInputTokens: usage.cacheRead } : {}),
152
- ...(typeof usage.cost?.total === "number" ? { costUsd: usage.cost.total } : {}),
152
+ ...(typeof usage.cacheRead === "number" && Number.isFinite(usage.cacheRead) ? { cachedInputTokens: usage.cacheRead } : {}),
153
+ ...(typeof usage.cost?.total === "number" && Number.isFinite(usage.cost.total) ? { costUsd: usage.cost.total } : {}),
153
154
  ...(typeof message.responseModel === "string"
154
155
  ? { model: message.responseModel }
155
156
  : typeof message.model === "string"
@@ -1,5 +1,7 @@
1
1
  export const SUBAGENT_FAILURE_CODES = [
2
2
  "SUBAGENT_TIMEOUT",
3
+ "SUBAGENT_TRANSPORT_ERROR",
4
+ "SUBAGENT_POLICY_BUDGET",
3
5
  "SUBAGENT_NON_ZERO_EXIT",
4
6
  "SUBAGENT_SPAWN_ERROR",
5
7
  "SUBAGENT_EXEC_ERROR",
@@ -2,7 +2,7 @@ export const meta = {
2
2
  command: "akuma",
3
3
  summary: "Inspect Akuma profiles",
4
4
  usage: ["keiyaku akuma ls|view"],
5
- stdin: "none",
5
+ stdin: { mode: "argument" },
6
6
  flags: [],
7
7
  purpose: "Inspect configured helper profiles.",
8
8
  subcommands: ["ls", "view"],
@@ -2,7 +2,7 @@ export const meta = {
2
2
  command: "akuma ls",
3
3
  summary: "List Akuma profiles",
4
4
  usage: ["keiyaku akuma ls [-C|--cwd DIR] [--repo DIR]"],
5
- stdin: "none",
5
+ stdin: { mode: "argument" },
6
6
  flags: ["cwd", "repo"],
7
7
  purpose: "List configured helper profiles.",
8
8
  notes: ["Project profiles override user profiles, which override builtins."],
@@ -2,7 +2,7 @@ export const meta = {
2
2
  command: "akuma view",
3
3
  summary: "View one Akuma profile",
4
4
  usage: ["keiyaku akuma view NAME [-C|--cwd DIR] [--repo DIR]"],
5
- stdin: "none",
5
+ stdin: { mode: "argument" },
6
6
  flags: ["cwd", "repo"],
7
7
  purpose: "Read one configured helper profile and its current nonterminal projections.",
8
8
  };
@@ -2,11 +2,11 @@ export const meta = {
2
2
  command: "amend",
3
3
  summary: "Record an amendment",
4
4
  usage: [
5
- "keiyaku amend [-C|--cwd DIR] [--repo DIR] [--contract ADDR] < amendment.md",
6
- "keiyaku @addr amend [-C|--cwd DIR] [--repo DIR] < amendment.md",
5
+ "keiyaku amend [-C|--cwd DIR] [--repo DIR] [--contract ADDR] - < amendment.md",
6
+ "keiyaku @addr amend [-C|--cwd DIR] [--repo DIR] - < amendment.md",
7
7
  ],
8
- stdin: "required",
8
+ stdin: { mode: "document", selector: { kind: "positional", index: 0, required: true, rejectBlank: true } },
9
9
  flags: ["cwd", "repo", "contract"],
10
- purpose: "Append a prose amendment and optional structured scope delta.",
10
+ purpose: "Append a prose amendment and optional Scope Append gitignore-pattern delta.",
11
11
  completionRank: 8,
12
12
  };
@@ -4,7 +4,7 @@ import { buildArcResponse } from "../../../render/arc.js";
4
4
  import { renderCoreResult } from "../../../render/shared.js";
5
5
  import { effectiveDirectory, repositoryDirectory } from "../../shared.js";
6
6
  export const handler = async (stdin, flags) => {
7
- const parsed = buildArcInput(stdin, effectiveDirectory(flags), flags.waivePaths, flags.contractId, flags.contractAddressSource);
7
+ const parsed = buildArcInput(stdin, effectiveDirectory(flags), flags.appendScope, flags.contractId, flags.contractAddressSource);
8
8
  const cwd = await repositoryDirectory(flags, "arc");
9
9
  const address = await resolveExistingContractAddress(cwd, flags.contractId, flags.contractAddressSource, effectiveDirectory(flags));
10
10
  return renderCoreResult(arcKeiyaku({
@@ -2,11 +2,11 @@ export const meta = {
2
2
  command: "arc",
3
3
  summary: "Optional iteration; seals current intent first",
4
4
  usage: [
5
- "keiyaku arc [-C|--cwd DIR] [--repo DIR] [--contract ADDR] [--waive PATH] < arc.md",
6
- "keiyaku @addr arc [-C|--cwd DIR] [--repo DIR] [--waive PATH] < arc.md",
5
+ "keiyaku arc [-C|--cwd DIR] [--repo DIR] [--contract ADDR] [--append-scope PATTERN] - < arc.md",
6
+ "keiyaku @addr arc [-C|--cwd DIR] [--repo DIR] [--append-scope PATTERN] - < arc.md",
7
7
  ],
8
- stdin: "required",
9
- flags: ["cwd", "repo", "contract", "waive"],
8
+ stdin: { mode: "document", selector: { kind: "positional", index: 0, required: true, rejectBlank: true } },
9
+ flags: ["cwd", "repo", "contract", "append-scope"],
10
10
  purpose: "Optional iteration boundary: record the next title, objective, and brief, and seal the current intent first.",
11
11
  input: [
12
12
  "# <arc title>",
@@ -0,0 +1,16 @@
1
+ import { auditContract } from "../../../../core/audit.js";
2
+ import { resolveContractAddress, withResolvedAddress } from "../../../../core/addressing.js";
3
+ import { buildAuditConstructionFailure, buildAuditResponse } from "../../../render/audit.js";
4
+ import { effectiveDirectory, repositoryDirectory } from "../../shared.js";
5
+ export const handler = async (_stdin, flags) => {
6
+ try {
7
+ const cwd = await repositoryDirectory(flags, "audit");
8
+ const address = await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource, effectiveDirectory(flags));
9
+ return await withResolvedAddress(address, async () => {
10
+ return buildAuditResponse(await auditContract({ cwd, address, diffBudgetBytes: flags.auditDiffBudgetBytes }), flags.json === true);
11
+ });
12
+ }
13
+ catch (error) {
14
+ return buildAuditConstructionFailure(error, flags.json === true);
15
+ }
16
+ };
@@ -0,0 +1,16 @@
1
+ export const meta = {
2
+ command: "audit",
3
+ summary: "Read settlement facts from one pinned delivery candidate",
4
+ usage: [
5
+ "keiyaku audit [-C|--cwd DIR] [--repo DIR] [--contract ADDR] [--diff-budget BYTES] [--json]",
6
+ "keiyaku @<id> audit [--diff-budget BYTES] [--json]",
7
+ ],
8
+ stdin: { mode: "argument" },
9
+ flags: ["cwd", "repo", "contract", "diff-budget", "json"],
10
+ purpose: "Inspect one pinned delivery candidate, report settlement facts, and recheck its coordinates without mutating the source repository.",
11
+ notes: [
12
+ "Audit never invokes an Akuma or settles the contract.",
13
+ "Exit 0 means the report was constructed; exit 1 carries a typed construction cause.",
14
+ ],
15
+ completionRank: 9,
16
+ };
@@ -2,14 +2,14 @@ export const meta = {
2
2
  command: "bind",
3
3
  summary: "Create a contract ledger and workspace files",
4
4
  usage: [
5
- "keiyaku bind [-C|--cwd DIR] [--repo DIR] [--task ID]... <<'KEIYAKU'",
5
+ "keiyaku bind [-C|--cwd DIR] [--repo DIR] [--task ID]... [- < contract.md]",
6
6
  "keiyaku bind [-C|--cwd DIR] [--repo DIR] [--task ID]... --place NAME --objective TEXT --scope GLOB --checks CHECK [--dry-run]",
7
7
  ],
8
- stdin: "optional",
8
+ stdin: { mode: "document", selector: { kind: "positional", index: 0, required: false, rejectBlank: false } },
9
9
  flags: ["cwd", "repo", "after", "exclusive", "draft-only", "place", "objective", "scope", "checks", "task", "dry-run"],
10
10
  // contract is parseable only so the mouth can reject it with bind-specific copy.
11
11
  parseFlags: ["cwd", "repo", "contract", "after", "exclusive", "draft-only", "place", "objective", "scope", "checks", "task", "dry-run"],
12
- purpose: "Bind a Keiyaku from contract Markdown on stdin.",
12
+ purpose: "Bind a Keiyaku from contract Markdown selected by literal -.",
13
13
  input: [
14
14
  "# <name>",
15
15
  "",
@@ -17,36 +17,45 @@ export const meta = {
17
17
  "<what the commission should accomplish>",
18
18
  "",
19
19
  "## Scope",
20
- "- <pattern>",
20
+ "```",
21
+ "<pattern>",
22
+ "<another-pattern>",
23
+ "```",
21
24
  "",
22
25
  "## Checks",
23
26
  "- <verification>",
24
27
  ],
25
28
  notes: [
26
29
  "bind creates a contract and does not accept @addr or --contract.",
27
- "Primary form is stdin Markdown (full document below).",
28
- "With --task, title and body supply name/objective defaults; stdin may send only the missing sections and any present section overrides the task default.",
29
- "Domain flags remain available as a full-document alternative; they cannot mix with stdin Markdown.",
30
+ "Primary document form is literal - followed by Markdown on stdin (full document below).",
31
+ "With --task, title and body supply name/objective defaults; literal - may send only the missing sections and any present section overrides the task default.",
32
+ "Domain flags remain available as a full-document alternative; they cannot mix with Markdown selected by literal -.",
30
33
  ],
31
34
  examples: [
32
35
  [
33
- "keiyaku bind <<'KEIYAKU'",
36
+ "keiyaku bind - <<'KEIYAKU'",
34
37
  "# <name>",
35
38
  "",
36
39
  "## Objective",
37
40
  "<what the commission should accomplish>",
38
41
  "",
39
42
  "## Scope",
40
- "- <pattern>",
43
+ "```",
44
+ "<pattern>",
45
+ "<another-pattern>",
46
+ "```",
41
47
  "",
42
48
  "## Checks",
43
49
  "- <verification>",
44
50
  "KEIYAKU",
45
51
  ].join("\n"),
46
52
  [
47
- "keiyaku bind --task fix-flaky-pump <<'KEIYAKU'",
53
+ "keiyaku bind --task fix-flaky-pump - <<'KEIYAKU'",
48
54
  "## Scope",
49
- "- <pattern>",
55
+ "```",
56
+ "<pattern>",
57
+ "<another-pattern>",
58
+ "```",
50
59
  "",
51
60
  "## Checks",
52
61
  "- <verification>",
@@ -2,6 +2,7 @@ import { meta as bindMeta } from "./bind/meta.js";
2
2
  import { meta as arcMeta } from "./arc/meta.js";
3
3
  import { meta as logMeta } from "./log/meta.js";
4
4
  import { meta as amendMeta } from "./amend/meta.js";
5
+ import { meta as auditMeta } from "./audit/meta.js";
5
6
  import { meta as renewMeta } from "./renew/meta.js";
6
7
  import { meta as petitionMeta } from "./petition/meta.js";
7
8
  import { meta as forfeitMeta } from "./forfeit/meta.js";
@@ -10,6 +11,7 @@ export const contractCatalog = [
10
11
  { meta: arcMeta, load: () => import("./arc/handler.js") },
11
12
  { meta: logMeta, load: () => import("./log/handler.js") },
12
13
  { meta: amendMeta, load: () => import("./amend/handler.js") },
14
+ { meta: auditMeta, load: () => import("./audit/handler.js") },
13
15
  { meta: renewMeta, load: () => import("./renew/handler.js") },
14
16
  { meta: petitionMeta, load: () => import("./petition/handler.js") },
15
17
  { meta: forfeitMeta, load: () => import("./forfeit/handler.js") },