@astrosheep/keiyaku 4.5.20 → 4.5.21

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 (154) hide show
  1. package/README.md +4 -4
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +22 -8
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md +237 -0
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +6 -7
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +2 -3
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -3
  8. package/build/src/akuma/akuma-handle.d.ts +5 -1
  9. package/build/src/akuma/akuma-handle.js +4 -1
  10. package/build/src/akuma/akuma-instance.d.ts +7 -2
  11. package/build/src/akuma/akuma-instance.js +14 -7
  12. package/build/src/akuma/akuma-observe.d.ts +11 -0
  13. package/build/src/akuma/akuma-observe.js +22 -0
  14. package/build/src/akuma/akuma-product.d.ts +1 -0
  15. package/build/src/akuma/akuma-product.js +3 -0
  16. package/build/src/akuma/akuma.d.ts +1 -0
  17. package/build/src/akuma/body.d.ts +25 -5
  18. package/build/src/akuma/body.js +65 -8
  19. package/build/src/akuma/call-request.d.ts +2 -0
  20. package/build/src/akuma/call-request.js +2 -0
  21. package/build/src/akuma/fleet-execution.d.ts +28 -0
  22. package/build/src/akuma/fleet-execution.js +36 -4
  23. package/build/src/akuma/fleet-request.d.ts +10 -0
  24. package/build/src/akuma/fleet-request.js +11 -2
  25. package/build/src/akuma/heart/facts.d.ts +2 -0
  26. package/build/src/akuma/heart/index.d.ts +1 -0
  27. package/build/src/akuma/heart/index.js +4 -1
  28. package/build/src/akuma/heart/rows.d.ts +2 -0
  29. package/build/src/akuma/heart/rows.js +7 -5
  30. package/build/src/akuma/heart/schema.d.ts +1 -1
  31. package/build/src/akuma/heart/schema.js +3 -1
  32. package/build/src/akuma/heart/tells.js +6 -5
  33. package/build/src/akuma/heart/timeline.js +1 -1
  34. package/build/src/akuma/provider.d.ts +1 -1
  35. package/build/src/akuma/provider.js +1 -1
  36. package/build/src/akuma/providers/acp/core.js +3 -6
  37. package/build/src/akuma/providers/claude/index.js +9 -12
  38. package/build/src/akuma/providers/codex-app-server/index.js +4 -11
  39. package/build/src/akuma/providers/execution-environment.d.ts +6 -0
  40. package/build/src/akuma/providers/execution-environment.js +24 -0
  41. package/build/src/akuma/providers/opencode-sdk/index.js +30 -23
  42. package/build/src/akuma/providers/opencode-sdk/session.d.ts +8 -1
  43. package/build/src/akuma/providers/opencode-sdk/session.js +6 -5
  44. package/build/src/akuma/providers/pi/events.js +7 -2
  45. package/build/src/akuma/providers/pi/index.js +3 -2
  46. package/build/src/akuma/request-rendezvous.js +8 -3
  47. package/build/src/akuma/turn-drive.d.ts +1 -0
  48. package/build/src/akuma/turn-drive.js +2 -0
  49. package/build/src/akuma-body.js +2 -0
  50. package/build/src/body/amend.js +6 -4
  51. package/build/src/body/decode.d.ts +3 -1
  52. package/build/src/body/decode.js +53 -17
  53. package/build/src/body/region.d.ts +9 -1
  54. package/build/src/body/region.js +87 -12
  55. package/build/src/body/render.js +1 -1
  56. package/build/src/body/verification.d.ts +3 -1
  57. package/build/src/body/verification.js +4 -1
  58. package/build/src/cli/accepted.js +19 -2
  59. package/build/src/cli/commands/akuma-invoke.d.ts +2 -0
  60. package/build/src/cli/commands/akuma-invoke.js +133 -13
  61. package/build/src/cli/commands/akuma.js +11 -4
  62. package/build/src/cli/commands/contract-help.js +17 -4
  63. package/build/src/cli/commands/contract.js +6 -4
  64. package/build/src/cli/commands/status-set.js +15 -24
  65. package/build/src/cli/main.js +5 -0
  66. package/build/src/cli/parse.d.ts +2 -0
  67. package/build/src/cli/parse.js +5 -0
  68. package/build/src/cli/render/akuma-activity.d.ts +99 -2
  69. package/build/src/cli/render/akuma-activity.js +484 -40
  70. package/build/src/cli/render/akuma.d.ts +10 -0
  71. package/build/src/cli/render/akuma.js +28 -21
  72. package/build/src/cli/render/audit.js +1 -1
  73. package/build/src/cli/render/board.js +1 -1
  74. package/build/src/cli/render/catalog.js +16 -13
  75. package/build/src/cli/render/contract-history.js +39 -10
  76. package/build/src/cli/render/contract-observation.js +10 -6
  77. package/build/src/cli/render/contract.js +109 -106
  78. package/build/src/cli/render/execution-progress.d.ts +23 -1
  79. package/build/src/cli/render/execution-progress.js +151 -28
  80. package/build/src/cli/render/kanshi-akuma.js +27 -39
  81. package/build/src/cli/render/kanshi.js +33 -26
  82. package/build/src/cli/render/nuke.js +1 -1
  83. package/build/src/cli/render/receipt.d.ts +1 -1
  84. package/build/src/cli/render/receipt.js +5 -5
  85. package/build/src/cli/render/refusal.js +10 -7
  86. package/build/src/cli/render/region.js +1 -1
  87. package/build/src/cli/render/settings.js +12 -2
  88. package/build/src/cli/render/status-line.d.ts +30 -0
  89. package/build/src/cli/render/status-line.js +58 -0
  90. package/build/src/cli/render/status-set.js +1 -1
  91. package/build/src/cli/render/task.d.ts +2 -0
  92. package/build/src/cli/render/task.js +11 -7
  93. package/build/src/cli/render/terminal.d.ts +0 -1
  94. package/build/src/cli/render/terminal.js +47 -15
  95. package/build/src/cli/result.d.ts +1 -0
  96. package/build/src/cli/runtime.d.ts +2 -0
  97. package/build/src/cli/runtime.js +13 -7
  98. package/build/src/cli/usage.js +2 -2
  99. package/build/src/cli/version.d.ts +1 -0
  100. package/build/src/cli/version.js +25 -0
  101. package/build/src/git/workspace.d.ts +1 -1
  102. package/build/src/git/workspace.js +1 -1
  103. package/build/src/identity/mint.d.ts +13 -0
  104. package/build/src/identity/mint.js +26 -0
  105. package/build/src/identity/normalize.d.ts +4 -0
  106. package/build/src/identity/normalize.js +30 -0
  107. package/build/src/index.d.ts +2 -0
  108. package/build/src/index.js +1 -0
  109. package/build/src/kanshi/read.js +17 -4
  110. package/build/src/kanshi/report.d.ts +1 -0
  111. package/build/src/library/akuma-creation.d.ts +2 -0
  112. package/build/src/library/akuma-creation.js +33 -17
  113. package/build/src/library/bind.js +18 -16
  114. package/build/src/library/contract-bind.d.ts +3 -1
  115. package/build/src/library/contract-bind.js +5 -3
  116. package/build/src/library/contract-operations.d.ts +2 -2
  117. package/build/src/library/contract-operations.js +1 -1
  118. package/build/src/library/contract-types.d.ts +1 -0
  119. package/build/src/library/fleet.d.ts +4 -1
  120. package/build/src/library/fleet.js +32 -8
  121. package/build/src/plugin/akuma-signals.d.ts +6 -0
  122. package/build/src/plugin/akuma-signals.js +8 -0
  123. package/build/src/plugin/public.d.ts +4 -0
  124. package/build/src/plugin/runtime.js +4 -1
  125. package/build/src/protocol/amend.js +4 -3
  126. package/build/src/protocol/attempt.d.ts +2 -0
  127. package/build/src/protocol/bind.js +41 -14
  128. package/build/src/protocol/completion.d.ts +3 -0
  129. package/build/src/protocol/completion.js +7 -1
  130. package/build/src/protocol/deliver.js +165 -92
  131. package/build/src/protocol/execution-observation.d.ts +0 -4
  132. package/build/src/protocol/intent.d.ts +15 -20
  133. package/build/src/protocol/intent.js +29 -17
  134. package/build/src/protocol/operations.d.ts +2 -0
  135. package/build/src/protocol/placement.js +5 -1
  136. package/build/src/protocol/read/status.d.ts +1 -0
  137. package/build/src/protocol/read/status.js +10 -2
  138. package/build/src/protocol/reintegrate.d.ts +1 -1
  139. package/build/src/protocol/reintegrate.js +70 -43
  140. package/build/src/protocol/result-codec.js +9 -7
  141. package/build/src/protocol/review.js +53 -30
  142. package/build/src/protocol/run.d.ts +83 -36
  143. package/build/src/protocol/run.js +90 -75
  144. package/build/src/runtime/proc/windows-launch.exe +0 -0
  145. package/build/src/settings.js +5 -14
  146. package/build/src/task/document.d.ts +5 -1
  147. package/build/src/task/document.js +6 -1
  148. package/build/src/verification/execution.d.ts +0 -1
  149. package/build/src/verification/execution.js +0 -24
  150. package/build/src/verification/observation.d.ts +0 -4
  151. package/build/src/verification/observation.js +1 -2
  152. package/package.json +3 -1
  153. package/build/src/git/verification-environment.d.ts +0 -10
  154. package/build/src/git/verification-environment.js +0 -281
@@ -57,7 +57,7 @@ function decodeTimelineRows(database, rows) {
57
57
  const facts = new Map();
58
58
  const turns = database
59
59
  .prepare(`SELECT sequence, body_sequence, started_at, end_sequence, outcome,
60
- history_id, session_json, answer, answer_json, schema_json, diagnostic, completed_at FROM turns
60
+ history_id, session_json, answer, answer_json, schema_json, initiator, diagnostic, completed_at FROM turns
61
61
  WHERE sequence IN (SELECT value FROM json_each(?)) OR end_sequence IN (SELECT value FROM json_each(?))`)
62
62
  .all(sequences("turn-start"), sequences("turn-end"));
63
63
  for (const row of turns) {
@@ -1,7 +1,7 @@
1
1
  import { decodeResumeCoordinate, encodeResumeCoordinate, type ResumeCoordinate } from "./coordinate.js";
2
2
  import type { ProviderOptions, ReadonlyRestraint } from "./provider-recipe.js";
3
3
  export type { ResumeCoordinate } from "./coordinate.js";
4
- export declare const AKUMA_REQUESTS_ENV = "AKUMA_REQUESTS";
4
+ export { AKUMA_REQUESTS_ENV } from "./providers/execution-environment.js";
5
5
  export declare const AGENT_EVENT_TEXT_LIMIT = 16384;
6
6
  export declare const AGENT_THOUGHT_TEXT_LIMIT = 4000;
7
7
  export declare const AGENT_EVENT_QUEUE_LIMIT = 256;
@@ -1,5 +1,5 @@
1
1
  import { decodeResumeCoordinate, encodeResumeCoordinate } from "./coordinate.js";
2
- export const AKUMA_REQUESTS_ENV = "AKUMA_REQUESTS";
2
+ export { AKUMA_REQUESTS_ENV } from "./providers/execution-environment.js";
3
3
  export const AGENT_EVENT_TEXT_LIMIT = 16_384;
4
4
  export const AGENT_THOUGHT_TEXT_LIMIT = 4_000;
5
5
  export const AGENT_EVENT_QUEUE_LIMIT = 256;
@@ -1,7 +1,8 @@
1
1
  import { Readable, Writable } from "node:stream";
2
2
  import { spawnStdioProcess } from "../../../runtime/proc/stdio.js";
3
3
  import { abortable } from "../../abort.js";
4
- import { AKUMA_REQUESTS_ENV, AgentEventChannel, } from "../../provider.js";
4
+ import { AgentEventChannel, } from "../../provider.js";
5
+ import { akumaExecutionEnvironment } from "../execution-environment.js";
5
6
  import { EMPTY_ACP_EVENT_STATE, flushAcpEvents, mapAcpUpdate, } from "./events.js";
6
7
  function diagnostic(acp, error) {
7
8
  if (!(error instanceof Error))
@@ -156,11 +157,7 @@ export async function startAcpSession(launch, input, dependencies = {}, custody)
156
157
  const child = (dependencies.spawnProcess ?? spawnStdioProcess)({
157
158
  argv: launch.argv,
158
159
  cwd: input.cwd,
159
- env: {
160
- ...globalThis.process.env,
161
- ...launch.env,
162
- ...(input.requests === undefined ? {} : { [AKUMA_REQUESTS_ENV]: input.requests.dir }),
163
- },
160
+ env: akumaExecutionEnvironment(globalThis.process.env, launch.env, input.requests?.dir),
164
161
  });
165
162
  custody?.own({
166
163
  closed: child.exited.then(() => undefined),
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { abortable } from "../../abort.js";
3
- import { AKUMA_REQUESTS_ENV, AgentEventChannel, createProviderAttempt, } from "../../provider.js";
3
+ import { AgentEventChannel, createProviderAttempt, } from "../../provider.js";
4
+ import { akumaExecutionEnvironment } from "../execution-environment.js";
4
5
  import { emitClaudeMessage } from "./events.js";
5
6
  import { claudeUserMessage, createClaudeInput, isClaudeTurnEnded } from "./input.js";
6
7
  export { CLAUDE_MESSAGE_DISPOSITIONS, CLAUDE_SYSTEM_DISPOSITIONS } from "./events.js";
@@ -38,8 +39,11 @@ class ReceiptChannel {
38
39
  }
39
40
  }
40
41
  function admitClaudeOptions(options) {
41
- if (options.sandbox !== undefined) {
42
- return { kind: "refused", diagnostic: "Claude provider does not support the sandbox option" };
42
+ if (options.sandbox === "full-access" && options.readonly === true) {
43
+ return { kind: "refused", diagnostic: "Claude full-access sandbox cannot combine with readonly" };
44
+ }
45
+ if (options.sandbox === "full-access" && options.network === "disabled") {
46
+ return { kind: "refused", diagnostic: "Claude full-access sandbox cannot combine with disabled network" };
43
47
  }
44
48
  if (options.network !== undefined) {
45
49
  return { kind: "refused", diagnostic: "Claude provider does not support the network option" };
@@ -67,18 +71,11 @@ function claudeQueryOptions(input, execution, abortController) {
67
71
  abortController,
68
72
  ...(input.requests === undefined ? {} : { additionalDirectories: [input.requests.dir] }),
69
73
  ...(execution.executable === undefined ? {} : { pathToClaudeCodeExecutable: execution.executable }),
70
- ...(execution.env === undefined && input.requests === undefined
71
- ? {}
72
- : {
73
- env: {
74
- ...process.env,
75
- ...execution.env,
76
- ...(input.requests === undefined ? {} : { [AKUMA_REQUESTS_ENV]: input.requests.dir }),
77
- },
78
- }),
74
+ env: akumaExecutionEnvironment(process.env, execution.env, input.requests?.dir),
79
75
  permissionMode: mode,
80
76
  ...(mode === "bypassPermissions" ? { allowDangerouslySkipPermissions: true } : {}),
81
77
  settingSources: ["user", "project", "local"],
78
+ ...(input.options.sandbox === "full-access" ? { sandbox: { enabled: false } } : {}),
82
79
  ...(input.options.model === undefined ? {} : { model: input.options.model }),
83
80
  ...(input.options.effort === undefined ? {} : { effort: input.options.effort }),
84
81
  ...(input.options.systemPrompt === undefined || input.options.systemPrompt.length === 0
@@ -1,5 +1,6 @@
1
1
  import { LineRpcProcess } from "../../../runtime/proc/line-rpc.js";
2
- import { AgentEventChannel, AKUMA_REQUESTS_ENV, createProviderAttempt, } from "../../provider.js";
2
+ import { AgentEventChannel, createProviderAttempt, } from "../../provider.js";
3
+ import { akumaExecutionEnvironment } from "../execution-environment.js";
3
4
  import { codexNotificationResult, codexObject, codexText } from "./events.js";
4
5
  export { CODEX_ITEM_DISPOSITIONS, CODEX_NOTIFICATION_DISPOSITIONS } from "./events.js";
5
6
  function diagnostic(error) {
@@ -106,7 +107,7 @@ async function forkCodex(execution, input, custody) {
106
107
  const server = new LineRpcProcess({
107
108
  argv: [execution.executable ?? "codex", "app-server", "--listen", "stdio://"],
108
109
  cwd: input.cwd,
109
- ...(execution.env === undefined ? {} : { env: { ...process.env, ...execution.env } }),
110
+ env: akumaExecutionEnvironment(process.env, execution.env),
110
111
  });
111
112
  const closed = new Promise((resolve) => server.onExit(() => resolve()));
112
113
  custody.own({
@@ -137,15 +138,7 @@ function codexServer(execution, input) {
137
138
  return new LineRpcProcess({
138
139
  argv: [execution.executable ?? "codex", "app-server", "--listen", "stdio://"],
139
140
  cwd: input.cwd,
140
- ...(execution.env === undefined && input.requests === undefined
141
- ? {}
142
- : {
143
- env: {
144
- ...process.env,
145
- ...execution.env,
146
- ...(input.requests === undefined ? {} : { [AKUMA_REQUESTS_ENV]: input.requests.dir }),
147
- },
148
- }),
141
+ env: akumaExecutionEnvironment(process.env, execution.env, input.requests?.dir),
149
142
  });
150
143
  }
151
144
  async function startCodex(execution, input, custody) {
@@ -0,0 +1,6 @@
1
+ export declare const AKUMA_REQUESTS_ENV = "AKUMA_REQUESTS";
2
+ declare const PARENT_HARNESS_IDENTITY_KEYS: readonly ["CLAUDE_CODE_SESSION_ID", "CLAUDE_CODE_CHILD_SESSION", "CLAUDECODE", "CODEX_THREAD_ID", "OPENCODE_SESSION_ID", "PI_SESSION_ID", "PI_SESSION_FILE", "PASEO_AGENT_ID", "SQUARE_PARTICIPANT_NAME"];
3
+ type Environment = Readonly<Record<string, string | undefined>>;
4
+ /** Construct the environment for one isolated Akuma execution or native child. */
5
+ export declare function akumaExecutionEnvironment(inherited: Environment, overrides?: Environment, requests?: string, nativeIdentity?: readonly (typeof PARENT_HARNESS_IDENTITY_KEYS)[number][]): Record<string, string>;
6
+ export {};
@@ -0,0 +1,24 @@
1
+ export const AKUMA_REQUESTS_ENV = "AKUMA_REQUESTS";
2
+ const PARENT_HARNESS_IDENTITY_KEYS = [
3
+ "CLAUDE_CODE_SESSION_ID",
4
+ "CLAUDE_CODE_CHILD_SESSION",
5
+ "CLAUDECODE",
6
+ "CODEX_THREAD_ID",
7
+ "OPENCODE_SESSION_ID",
8
+ "PI_SESSION_ID",
9
+ "PI_SESSION_FILE",
10
+ "PASEO_AGENT_ID",
11
+ "SQUARE_PARTICIPANT_NAME",
12
+ ];
13
+ /** Construct the environment for one isolated Akuma execution or native child. */
14
+ export function akumaExecutionEnvironment(inherited, overrides = {}, requests, nativeIdentity = []) {
15
+ const preserved = Object.fromEntries(nativeIdentity.flatMap((key) => (inherited[key] === undefined ? [] : [[key, inherited[key]]])));
16
+ const environment = Object.fromEntries(Object.entries({ ...inherited, ...overrides }).flatMap(([key, value]) => value === undefined ? [] : [[key, value]]));
17
+ for (const key of PARENT_HARNESS_IDENTITY_KEYS)
18
+ delete environment[key];
19
+ Object.assign(environment, preserved);
20
+ delete environment[AKUMA_REQUESTS_ENV];
21
+ if (requests !== undefined)
22
+ environment[AKUMA_REQUESTS_ENV] = requests;
23
+ return environment;
24
+ }
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { AKUMA_REQUESTS_ENV, AgentEventChannel, createProviderAttempt, } from "../../provider.js";
2
+ import { AgentEventChannel, createProviderAttempt, } from "../../provider.js";
3
3
  import { createEventState, mapEvent } from "./events.js";
4
4
  import { coordinate, loadOpencode, OPENCODE_SDK_PROVIDER, parseModel } from "./session.js";
5
5
  function object(value) {
@@ -204,12 +204,13 @@ async function forceDisposeOpencode(abortController, close, finish) {
204
204
  }
205
205
  async function loadDriveRuntime(execution, input, signal, loader, onRuntime) {
206
206
  const runtime = await loadOpencode({
207
- ...execution,
208
- env: {
209
- ...execution.env,
210
- ...(input.requests === undefined ? {} : { [AKUMA_REQUESTS_ENV]: input.requests.dir }),
211
- },
212
- }, input.cwd, signal, loader, onRuntime);
207
+ execution,
208
+ cwd: input.cwd,
209
+ signal,
210
+ ...(loader === undefined ? {} : { loader }),
211
+ ...(onRuntime === undefined ? {} : { onRuntime }),
212
+ ...(input.requests === undefined ? {} : { requests: input.requests.dir }),
213
+ });
213
214
  if (signal.aborted) {
214
215
  await runtime.close();
215
216
  signal.throwIfAborted();
@@ -343,22 +344,28 @@ export function createOpencodeProvider(input = { name: OPENCODE_SDK_PROVIDER, ki
343
344
  fork: (input) => createProviderAttempt(new AbortController().signal, async (custody) => {
344
345
  const sessionId = opencodeSessionId(input.session);
345
346
  let close;
346
- const runtime = await loadOpencode(execution, input.cwd, custody.signal, loader, (ready) => {
347
- let closing;
348
- let settleRuntimeClosed;
349
- let rejectRuntimeClosed;
350
- const runtimeClosed = new Promise((resolve, reject) => {
351
- settleRuntimeClosed = resolve;
352
- rejectRuntimeClosed = reject;
353
- });
354
- close = () => {
355
- if (closing === undefined) {
356
- closing = ready.close();
357
- void closing.then(settleRuntimeClosed, rejectRuntimeClosed);
358
- }
359
- return closing;
360
- };
361
- custody.own({ closed: runtimeClosed, abort: close, forceDispose: close });
347
+ const runtime = await loadOpencode({
348
+ execution,
349
+ cwd: input.cwd,
350
+ signal: custody.signal,
351
+ ...(loader === undefined ? {} : { loader }),
352
+ onRuntime: (ready) => {
353
+ let closing;
354
+ let settleRuntimeClosed;
355
+ let rejectRuntimeClosed;
356
+ const runtimeClosed = new Promise((resolve, reject) => {
357
+ settleRuntimeClosed = resolve;
358
+ rejectRuntimeClosed = reject;
359
+ });
360
+ close = () => {
361
+ if (closing === undefined) {
362
+ closing = ready.close();
363
+ void closing.then(settleRuntimeClosed, rejectRuntimeClosed);
364
+ }
365
+ return closing;
366
+ };
367
+ custody.own({ closed: runtimeClosed, abort: close, forceDispose: close });
368
+ },
362
369
  });
363
370
  try {
364
371
  const result = await runtime.client.session.fork({
@@ -23,5 +23,12 @@ export type OpencodeRuntime = Readonly<{
23
23
  };
24
24
  close: () => Promise<void>;
25
25
  }>;
26
- export declare function loadOpencode(execution: ProviderExecution, cwd: string, signal: AbortSignal, loader?: OpencodeSdkLoader, onRuntime?: (runtime: OpencodeRuntime) => void): Promise<OpencodeRuntime>;
26
+ export declare function loadOpencode(input: Readonly<{
27
+ execution: ProviderExecution;
28
+ cwd: string;
29
+ signal: AbortSignal;
30
+ loader?: OpencodeSdkLoader;
31
+ onRuntime?: (runtime: OpencodeRuntime) => void;
32
+ requests?: string;
33
+ }>): Promise<OpencodeRuntime>;
27
34
  export declare function coordinate(sessionId: string): ResumeCoordinate;
@@ -1,5 +1,6 @@
1
1
  import net from "node:net";
2
2
  import { abortable } from "../../abort.js";
3
+ import { akumaExecutionEnvironment } from "../execution-environment.js";
3
4
  import { spawnDetachedProcess } from "../../../runtime/proc/run.js";
4
5
  export const OPENCODE_SDK_PROVIDER = "opencode-sdk";
5
6
  export function parseModel(model) {
@@ -37,9 +38,10 @@ async function availablePort() {
37
38
  });
38
39
  });
39
40
  }
40
- export async function loadOpencode(execution, cwd, signal, loader, onRuntime) {
41
+ export async function loadOpencode(input) {
42
+ const { execution, cwd, signal, loader, onRuntime, requests } = input;
41
43
  if (loader) {
42
- const loaded = await abortable(loader(cwd, execution, signal), signal);
44
+ const loaded = await abortable(loader(cwd, { ...execution, env: akumaExecutionEnvironment(process.env, execution.env, requests) }, signal), signal);
43
45
  let closing;
44
46
  const runtime = {
45
47
  client: loaded.client,
@@ -64,11 +66,10 @@ export async function loadOpencode(execution, cwd, signal, loader, onRuntime) {
64
66
  const owned = await spawnDetachedProcess({
65
67
  argv: [execution.executable ?? "opencode", "serve", "--hostname", "127.0.0.1", "--port", String(port)],
66
68
  cwd,
67
- env: {
68
- ...process.env,
69
+ env: akumaExecutionEnvironment(process.env, {
69
70
  ...(execution.config === undefined ? {} : { OPENCODE_CONFIG_CONTENT: JSON.stringify(execution.config) }),
70
71
  ...execution.env,
71
- },
72
+ }, requests),
72
73
  log: `${cwd}/.opencode.log`,
73
74
  });
74
75
  const { createOpencodeClient } = await import("@opencode-ai/sdk");
@@ -89,7 +89,9 @@ function searchCall(name, value) {
89
89
  };
90
90
  }
91
91
  function fileChangeCall(name, value) {
92
- if (name !== "edit" || typeof value.path !== "string")
92
+ // Native write cannot prove creation versus overwrite and reports no patch, so
93
+ // it joins the conservative update operation with unknown diffstat.
94
+ if ((name !== "edit" && name !== "write") || typeof value.path !== "string")
93
95
  return undefined;
94
96
  return { kind: "fileChange", changes: [{ op: "update", path: value.path }] };
95
97
  }
@@ -158,7 +160,10 @@ function translateMessage(event, state) {
158
160
  }
159
161
  const text = textContent(message.content);
160
162
  state.answer = text;
161
- if (hasTextContent(message.content)) {
163
+ // Gemini emits an empty text block alongside every tool-use message. It is
164
+ // transport scaffolding, not narration; an ordinary terminal empty answer
165
+ // remains public evidence.
166
+ if (hasTextContent(message.content) && !(message.stopReason === "toolUse" && text.length === 0)) {
162
167
  translated.push({ type: "assistant", text });
163
168
  }
164
169
  return translated;
@@ -1,5 +1,6 @@
1
1
  import { abortable } from "../../abort.js";
2
- import { AKUMA_REQUESTS_ENV, AgentEventChannel, createProviderAttempt, } from "../../provider.js";
2
+ import { akumaExecutionEnvironment } from "../execution-environment.js";
3
+ import { AgentEventChannel, createProviderAttempt, } from "../../provider.js";
3
4
  import { piTerminalFailure, translatePiEvent } from "./events.js";
4
5
  const PI_THINKING_LEVELS = new Set(["minimal", "low", "medium", "high", "xhigh", "max"]);
5
6
  const MODEL_PATTERN = /^[^/\s]+\/[^\s]+$/u;
@@ -56,7 +57,7 @@ async function piCreateOptions(sdk, input) {
56
57
  (await import("@earendil-works/pi-coding-agent")).createBashToolDefinition(input.cwd, {
57
58
  spawnHook: (context) => ({
58
59
  ...context,
59
- env: { ...context.env, [AKUMA_REQUESTS_ENV]: input.requests.dir },
60
+ env: akumaExecutionEnvironment(context.env, {}, input.requests.dir, ["PI_SESSION_ID", "PI_SESSION_FILE"]),
60
61
  }),
61
62
  }),
62
63
  ];
@@ -135,9 +135,14 @@ async function observePublishedRequest(input, id, transportId, path) {
135
135
  }
136
136
  try {
137
137
  for (;;) {
138
- // The service flushes this bounded snapshot before its receipt, including fast commands.
139
- await consumeProgress();
140
- const response = await readRequestReceipt(input, path, id);
138
+ let response;
139
+ try {
140
+ response = await readRequestReceipt(input, path, id);
141
+ }
142
+ finally {
143
+ // A visible receipt guarantees its final progress was already published, even on failure.
144
+ await consumeProgress();
145
+ }
141
146
  if (response !== undefined)
142
147
  return response;
143
148
  if (!(await access(input.directory).then(() => true, () => false))) {
@@ -25,6 +25,7 @@ export type DriveTurnInput = Readonly<{
25
25
  body: string;
26
26
  call?: string;
27
27
  launchTells: readonly TellFact[];
28
+ initiator?: string;
28
29
  schemaJson?: string;
29
30
  supervisor: BodySupervisor;
30
31
  runtimeSpawn(launch: AkumaCallRequestChildLaunch): Promise<OwnedProcess | void>;
@@ -72,7 +72,9 @@ async function startTurnDrive(input) {
72
72
  return { kind: "resume-unsupported" };
73
73
  const boundTells = drainPendingTells(input.launchTells);
74
74
  const schemaJson = input.schemaJson ?? boundTells.find((tell) => tell.schemaJson !== undefined)?.schemaJson;
75
+ const initiator = input.call === undefined ? boundTells[0]?.initiator : input.initiator;
75
76
  const turn = await beginTurn(input.paths, {
77
+ ...(initiator === undefined ? {} : { initiator }),
76
78
  bodySequence: input.bodySequence,
77
79
  startedAt: input.now(),
78
80
  ...(input.call === undefined ? {} : { call: input.call }),
@@ -86,11 +86,13 @@ function fleetRequestPort(world) {
86
86
  body: input.body,
87
87
  tellId: input.tellId,
88
88
  recordedAt: input.recordedAt,
89
+ ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
89
90
  signal: input.signal,
90
91
  }),
91
92
  tellAnswer: async (input) => await PublicAkuma.select(world, input.target).tell(input.body, {
92
93
  schema: Schema.json(JSON.parse(input.schemaJson), (value) => value),
93
94
  ...(input.interrupt === undefined ? {} : { interrupt: input.interrupt }),
95
+ ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
94
96
  }),
95
97
  kill: async (input) => {
96
98
  const result = await executeKillAkuma({ path: world, ids: input.targets, signal: input.signal });
@@ -16,11 +16,13 @@ function sections(document) {
16
16
  function operationSections(document) {
17
17
  if (document.frontmatter !== undefined)
18
18
  refusal("amend operations may not contain frontmatter");
19
- if (indexedHeadings(indexDocument(document), { level: 1 }).length > 0)
19
+ const openedBy = document.children.find((node) => nonblank(document, node));
20
+ const heading = openedBy?.type === "section" && openedBy.level === 1 ? openedBy : undefined;
21
+ if (indexedHeadings(indexDocument(document), { level: 1 }).length > (heading === undefined ? 0 : 1))
20
22
  refusal("amend operations may contain H2 sections only");
21
- if (document.children.some((node) => node.type !== "section" && nonblank(document, node))) {
23
+ const outside = [...document.children, ...(heading?.children ?? [])];
24
+ if (outside.some((node) => node.type !== "section" && nonblank(document, node)))
22
25
  refusal("amend operations contain bytes outside H2 sections");
23
- }
24
26
  return sections(document).map((section) => {
25
27
  const title = section.title.trim();
26
28
  const match = /^(Add|Update|Replace|Append|Remove):[ ]+(.+)$/.exec(title);
@@ -70,7 +72,7 @@ function criteria(document, section) {
70
72
  }
71
73
  function verification(document, section) {
72
74
  try {
73
- return decodeVerificationDeclarations(document, section);
75
+ return decodeVerificationDeclarations(document, section, { requireTimeout: true });
74
76
  }
75
77
  catch (error) {
76
78
  if (error instanceof VerificationDocumentError)
@@ -1,4 +1,6 @@
1
1
  import type { DecodedContractDocument } from "./types.js";
2
2
  import type { VerificationDefinition } from "../verification/declaration.js";
3
- export declare function decodeContractDocument(source: string): DecodedContractDocument;
3
+ export declare function decodeContractDocument(source: string, options?: Readonly<{
4
+ requireTimeout?: boolean;
5
+ }>): DecodedContractDocument;
4
6
  export declare function verificationDefinition(document: DecodedContractDocument): VerificationDefinition | null;
@@ -2,20 +2,60 @@ import { mintDocumentKey, mintDocumentSegmentKey } from "./keys.js";
2
2
  import { decodeDocumentEnvelope } from "./envelope.js";
3
3
  import { directChildren, normalizeTitle, rawSlice, sectionContent } from "../markdown/query.js";
4
4
  import { CONTRACT_SECTIONS, RESERVED_SECTIONS } from "./shape.js";
5
- import { decodeRegion, RegionDocumentError } from "./region.js";
5
+ import { decodeRegion, regionStructure, RegionDocumentError } from "./region.js";
6
6
  import { decodeVerificationDeclarations, VerificationDocumentError } from "./verification.js";
7
7
  function refusal(message) {
8
8
  throw new TypeError(message);
9
9
  }
10
10
  function requireSections(sections) {
11
+ const diagnostics = [];
11
12
  for (const [name, spec] of Object.entries(CONTRACT_SECTIONS)) {
12
13
  if (spec.required && !sections.has(name))
13
- refusal(`contract document is missing ## ${spec.title}`);
14
+ diagnostics.push(`contract document is missing ## ${spec.title}`);
14
15
  }
16
+ return diagnostics;
15
17
  }
16
18
  function requiredSection(sections, name) {
17
19
  return sections.get(name);
18
20
  }
21
+ function criteriaStructure(document, section) {
22
+ const headings = directChildren(section, "heading").filter((heading) => heading.level === 3);
23
+ if (headings.length === 0)
24
+ return "Criteria must contain one or more H3 entries";
25
+ const before = rawSlice(document, { start: section.contentStart, end: headings[0].span.start });
26
+ return before.trim().length === 0 ? null : "Criteria may contain only H3 entries";
27
+ }
28
+ function verificationStructure(document, section) {
29
+ const blocks = directChildren(section, "code_block");
30
+ if (blocks.length === 0)
31
+ return "Verification must contain one or more fenced executor declarations";
32
+ const other = section.children.filter((node) => node.type !== "code_block" && rawSlice(document, node.span).trim().length > 0);
33
+ return other.length === 0 ? null : "Verification may contain only fenced executor declarations";
34
+ }
35
+ // Independent structural rules collect together; each predicate mirrors the
36
+ // fence, heading, or reserved-name check its decoder already enforces so a
37
+ // document with several violations is refused once with every diagnostic.
38
+ function structuralDiagnostics(document, sections) {
39
+ const diagnostics = requireSections(sections);
40
+ for (const name of RESERVED_SECTIONS) {
41
+ if (sections.has(name))
42
+ diagnostics.push(`${name} is not a contract Markdown section`);
43
+ }
44
+ const checks = [
45
+ ["region", (_document, section) => regionStructure(section)],
46
+ ["criteria", criteriaStructure],
47
+ ["verification", verificationStructure],
48
+ ];
49
+ for (const [name, check] of checks) {
50
+ const section = sections.get(name);
51
+ if (section === undefined)
52
+ continue;
53
+ const diagnostic = check(document, section);
54
+ if (diagnostic !== null)
55
+ diagnostics.push(diagnostic);
56
+ }
57
+ return diagnostics;
58
+ }
19
59
  function prose(document, section) {
20
60
  const value = sectionContent(document, section);
21
61
  if (value.trim().length === 0)
@@ -33,12 +73,10 @@ function region(document, section) {
33
73
  }
34
74
  }
35
75
  function criteria(document, section) {
76
+ const structural = criteriaStructure(document, section);
77
+ if (structural !== null)
78
+ refusal(structural);
36
79
  const headings = directChildren(section, "heading").filter((heading) => heading.level === 3);
37
- if (headings.length === 0)
38
- refusal("Criteria must contain one or more H3 entries");
39
- const before = rawSlice(document, { start: section.contentStart, end: headings[0].span.start });
40
- if (before.trim().length > 0)
41
- refusal("Criteria may contain only H3 entries");
42
80
  const seen = new Set();
43
81
  return headings.map((heading, index) => {
44
82
  const title = heading.text.trim();
@@ -55,9 +93,9 @@ function criteria(document, section) {
55
93
  return { title, body };
56
94
  });
57
95
  }
58
- function verification(document, section) {
96
+ function verification(document, section, options) {
59
97
  try {
60
- return decodeVerificationDeclarations(document, section);
98
+ return decodeVerificationDeclarations(document, section, options);
61
99
  }
62
100
  catch (error) {
63
101
  if (error instanceof VerificationDocumentError)
@@ -65,7 +103,7 @@ function verification(document, section) {
65
103
  throw error;
66
104
  }
67
105
  }
68
- export function decodeContractDocument(source) {
106
+ export function decodeContractDocument(source, options = {}) {
69
107
  let envelope;
70
108
  try {
71
109
  envelope = decodeDocumentEnvelope(source, "contract");
@@ -74,11 +112,10 @@ export function decodeContractDocument(source) {
74
112
  refusal(error instanceof Error ? error.message : String(error));
75
113
  }
76
114
  const { document, title, sections, sectionNodes } = envelope;
77
- requireSections(sections);
78
- for (const name of RESERVED_SECTIONS) {
79
- if (sections.has(name))
80
- refusal(`${name} is not a contract Markdown section`);
81
- }
115
+ const structural = structuralDiagnostics(document, sections);
116
+ if (structural.length > 0)
117
+ refusal(structural.join("\n"));
118
+ const verificationSection = sections.get("verification");
82
119
  const extensions = [...sections.entries()]
83
120
  .filter(([name]) => !Object.hasOwn(CONTRACT_SECTIONS, name))
84
121
  .map(([, section]) => {
@@ -87,7 +124,6 @@ export function decodeContractDocument(source) {
87
124
  refusal(`extension '${section.title}' is empty`);
88
125
  return { title: section.title, content };
89
126
  });
90
- const verificationSection = sections.get("verification");
91
127
  const body = {
92
128
  title: title.title,
93
129
  context: prose(document, requiredSection(sections, "context")),
@@ -95,7 +131,7 @@ export function decodeContractDocument(source) {
95
131
  design: prose(document, requiredSection(sections, "design")),
96
132
  region: region(document, requiredSection(sections, "region")),
97
133
  criteria: criteria(document, requiredSection(sections, "criteria")),
98
- verification: verificationSection === undefined ? [] : verification(document, verificationSection),
134
+ verification: verificationSection === undefined ? [] : verification(document, verificationSection, options),
99
135
  extensions,
100
136
  };
101
137
  const segments = sectionNodes.map((section) => mintDocumentSegmentKey(document.source, section.span));
@@ -3,5 +3,13 @@ export declare class RegionDocumentError extends Error {
3
3
  constructor(message: string);
4
4
  }
5
5
  export declare function assertRegionPattern(pattern: string): string;
6
- export declare function decodeRegion(document: DocumentNode, section: SectionNode): readonly string[];
6
+ /**
7
+ * Structural refusals are separate from pattern compilation so a caller may aggregate every independent failure.
8
+ * The three authoring forms — fenced lines, list items, and bare paragraph lines — union freely. Blank lines never
9
+ * count, and an info string beyond the exact `txt` still implies executable content that belongs to Verification.
10
+ */
11
+ export declare function regionStructure(section: SectionNode): string | null;
12
+ /** A whitespace-bearing pattern is almost certainly strayed prose; bind warns and proceeds rather than rejecting. */
13
+ export declare function regionWarnings(patterns: readonly string[]): readonly string[];
14
+ export declare function decodeRegion(_document: DocumentNode, section: SectionNode): readonly string[];
7
15
  export declare function regionsOverlap(mine: readonly string[], theirs: readonly string[]): readonly [string, string][];