@astrosheep/keiyaku 2.8.5 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/README.md +35 -15
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/{call-terms_v2.js → call-terms.js} +12 -8
  4. package/build/agents/harness/control-types.js +1 -0
  5. package/build/agents/harness/execution-handle.js +23 -3
  6. package/build/agents/harness/index.js +6 -607
  7. package/build/agents/harness/outcome.js +141 -0
  8. package/build/agents/harness/projection.js +310 -0
  9. package/build/agents/harness/router.js +201 -0
  10. package/build/agents/harness/runtime.js +2 -1
  11. package/build/agents/{launch-snapshot_v2.js → launch-snapshot/model.js} +69 -74
  12. package/build/agents/launch-snapshot/resolve.js +72 -0
  13. package/build/agents/opencode-sdk.js +24 -3
  14. package/build/agents/providers/{claude-agent-sdk.js → claude-agent-sdk/adapter.js} +46 -368
  15. package/build/agents/providers/claude-agent-sdk/events.js +218 -0
  16. package/build/agents/providers/claude-agent-sdk/session.js +158 -0
  17. package/build/agents/providers/codex-app-server/adapter.js +347 -0
  18. package/build/agents/providers/codex-app-server/events.js +352 -0
  19. package/build/agents/providers/codex-app-server/session.js +266 -0
  20. package/build/agents/providers/codex-sdk.js +23 -5
  21. package/build/agents/providers/opencode-sdk/adapter.js +344 -0
  22. package/build/agents/providers/opencode-sdk/events.js +354 -0
  23. package/build/agents/providers/opencode-sdk/session.js +180 -0
  24. package/build/agents/providers/pi/adapter.js +269 -0
  25. package/build/agents/providers/pi/checkpoint.js +41 -0
  26. package/build/agents/providers/pi/events.js +282 -0
  27. package/build/agents/{selector_v2.js → selector.js} +8 -6
  28. package/build/cli/commands/akuma/akuma/handler.js +4 -0
  29. package/build/cli/commands/akuma/akuma/meta.js +16 -0
  30. package/build/cli/commands/akuma/catalog.js +8 -0
  31. package/build/cli/commands/akuma/list/handler.js +15 -0
  32. package/build/cli/commands/akuma/list/meta-ls.js +9 -0
  33. package/build/cli/commands/akuma/view/handler.js +34 -0
  34. package/build/cli/commands/akuma/view/meta.js +8 -0
  35. package/build/cli/commands/akuma.js +1 -1
  36. package/build/cli/commands/contract/amend/handler.js +10 -0
  37. package/build/cli/commands/contract/amend/meta.js +12 -0
  38. package/build/cli/commands/contract/arc/handler.js +9 -0
  39. package/build/cli/commands/contract/arc/meta.js +26 -0
  40. package/build/cli/commands/contract/bind/handler.js +23 -0
  41. package/build/cli/commands/contract/bind/meta.js +30 -0
  42. package/build/cli/commands/contract/catalog.js +16 -0
  43. package/build/cli/commands/contract/forfeit/handler.js +13 -0
  44. package/build/cli/commands/contract/forfeit/meta.js +12 -0
  45. package/build/cli/commands/contract/log/handler.js +12 -0
  46. package/build/cli/commands/contract/log/meta.js +12 -0
  47. package/build/cli/commands/contract/petition/handler.js +26 -0
  48. package/build/cli/commands/contract/petition/meta.js +23 -0
  49. package/build/cli/commands/contract/renew/handler.js +10 -0
  50. package/build/cli/commands/contract/renew/meta.js +17 -0
  51. package/build/cli/commands/projection/call/handler.js +39 -0
  52. package/build/cli/commands/projection/call/meta.js +9 -0
  53. package/build/cli/commands/projection/catalog.js +14 -0
  54. package/build/cli/commands/projection/kill/handler.js +35 -0
  55. package/build/cli/commands/projection/kill/meta.js +11 -0
  56. package/build/cli/commands/projection/revive/handler.js +53 -0
  57. package/build/cli/commands/projection/revive/meta.js +9 -0
  58. package/build/cli/commands/projection/status/handler.js +23 -0
  59. package/build/cli/commands/projection/status/meta.js +9 -0
  60. package/build/cli/commands/projection/tell/handler.js +34 -0
  61. package/build/cli/commands/projection/tell/meta.js +9 -0
  62. package/build/cli/commands/projection/wait/handler.js +21 -0
  63. package/build/cli/commands/projection/wait/meta.js +11 -0
  64. package/build/cli/commands/registry.js +100 -0
  65. package/build/cli/commands/shared.js +71 -0
  66. package/build/cli/commands/skills/catalog.js +6 -0
  67. package/build/cli/commands/skills/install/handler.js +10 -0
  68. package/build/cli/commands/skills/install/meta.js +9 -0
  69. package/build/cli/commands/skills/skills/handler.js +4 -0
  70. package/build/cli/commands/skills/skills/meta.js +10 -0
  71. package/build/cli/commands/system/catalog.js +8 -0
  72. package/build/cli/commands/system/completion/handler.js +10 -0
  73. package/build/cli/commands/system/completion/meta.js +9 -0
  74. package/build/cli/commands/system/dump-env/handler.js +3 -0
  75. package/build/cli/commands/system/dump-env/meta.js +10 -0
  76. package/build/cli/commands/system/guide/handler.js +4 -0
  77. package/build/cli/commands/system/guide/meta.js +9 -0
  78. package/build/cli/commands/task/add/handler.js +21 -0
  79. package/build/cli/commands/task/add/meta.js +8 -0
  80. package/build/cli/commands/task/catalog.js +22 -0
  81. package/build/cli/commands/task/doctor/handler.js +4 -0
  82. package/build/cli/commands/task/doctor/meta.js +8 -0
  83. package/build/cli/commands/task/done/handler.js +17 -0
  84. package/build/cli/commands/task/done/meta.js +8 -0
  85. package/build/cli/commands/task/drop/handler.js +6 -0
  86. package/build/cli/commands/task/drop/meta.js +8 -0
  87. package/build/cli/commands/task/ls/handler.js +13 -0
  88. package/build/cli/commands/task/ls/meta.js +8 -0
  89. package/build/cli/commands/task/shared.js +131 -0
  90. package/build/cli/commands/task/start/handler.js +6 -0
  91. package/build/cli/commands/task/start/meta.js +8 -0
  92. package/build/cli/commands/task/stop/handler.js +17 -0
  93. package/build/cli/commands/task/stop/meta.js +8 -0
  94. package/build/cli/commands/task/task/handler.js +5 -0
  95. package/build/cli/commands/task/task/meta.js +11 -0
  96. package/build/cli/commands/task/update/handler.js +18 -0
  97. package/build/cli/commands/task/update/meta.js +8 -0
  98. package/build/cli/commands/task/view/handler.js +14 -0
  99. package/build/cli/commands/task/view/meta.js +8 -0
  100. package/build/cli/completion.js +26 -27
  101. package/build/cli/flags.js +79 -2
  102. package/build/cli/help.js +27 -49
  103. package/build/cli/index.js +121 -374
  104. package/build/cli/output-stream-error-policy.js +17 -0
  105. package/build/cli/parse.js +159 -72
  106. package/build/cli/projection-address.js +5 -0
  107. package/build/cli/render/arc.js +1 -1
  108. package/build/cli/render/errors.js +4 -3
  109. package/build/cli/render/format.js +14 -0
  110. package/build/cli/render/kanshi.js +106 -0
  111. package/build/cli/render/petition.js +24 -1
  112. package/build/cli/render/projection-activity.js +5 -1
  113. package/build/cli/render/shared.js +2 -2
  114. package/build/cli/render/status.js +79 -13
  115. package/build/cli/render/task.js +113 -0
  116. package/build/cli/render/wait.js +23 -3
  117. package/build/cli/types.js +1 -1
  118. package/build/config/{akuma-loader_v2.js → akuma-loader.js} +2 -2
  119. package/build/config/env.js +9 -4
  120. package/build/config/settings/disease.js +26 -0
  121. package/build/config/settings/knobs.js +8 -0
  122. package/build/config/settings/loader.js +244 -0
  123. package/build/config/settings/schema.js +276 -0
  124. package/build/core/addressing.js +1 -1
  125. package/build/core/amend.js +3 -3
  126. package/build/core/arc.js +5 -4
  127. package/build/core/bind-workspace.js +123 -0
  128. package/build/core/bind.js +75 -155
  129. package/build/core/call/call.js +224 -0
  130. package/build/core/call/context.js +238 -0
  131. package/build/core/call/execution.js +217 -0
  132. package/build/core/call/prompt.js +42 -0
  133. package/build/core/call/reference.js +24 -0
  134. package/build/core/call-persist.js +1 -1
  135. package/build/core/claim-delivery.js +247 -0
  136. package/build/core/claim.js +39 -277
  137. package/build/core/context.js +2 -1
  138. package/build/core/{render.js → contract-view.js} +12 -140
  139. package/build/core/draft.js +107 -9
  140. package/build/core/entry.js +9 -1
  141. package/build/core/execution-coordinate.js +4 -20
  142. package/build/core/execution-pact.js +16 -31
  143. package/build/core/forfeit.js +35 -22
  144. package/build/core/hints.js +2 -2
  145. package/build/core/log.js +2 -2
  146. package/build/core/petition-claim-gates.js +8 -6
  147. package/build/core/petition-preview.js +97 -0
  148. package/build/core/petition-run.js +3 -2
  149. package/build/core/petition.js +39 -26
  150. package/build/core/projection/generation/database.js +306 -0
  151. package/build/core/projection/generation/identity.js +84 -0
  152. package/build/core/projection/generation/model.js +201 -0
  153. package/build/core/projection/generation/store.js +73 -0
  154. package/build/core/projection/generation/transitions.js +258 -0
  155. package/build/core/projection/heart.js +0 -307
  156. package/build/core/projection/tell/database.js +109 -0
  157. package/build/core/projection/tell/model.js +97 -0
  158. package/build/core/{projection-tells.js → projection/tell/store.js} +3 -158
  159. package/build/core/projection-activity.js +23 -2
  160. package/build/core/projection-core.js +2 -1
  161. package/build/core/projection-execution-observer.js +259 -0
  162. package/build/core/projection-generation-continuation.js +1 -1
  163. package/build/core/projection-generation-execution.js +90 -20
  164. package/build/core/projection-generation-launcher.js +1 -1
  165. package/build/core/projection-generation-runner.js +198 -4
  166. package/build/core/projection-identity.js +1 -1
  167. package/build/core/projection-kill.js +50 -17
  168. package/build/core/projection-life-observer.js +1 -1
  169. package/build/core/projection-life-protocol.js +10 -0
  170. package/build/core/projection-mint.js +1 -1
  171. package/build/core/projection-status.js +45 -32
  172. package/build/core/projection-wait.js +12 -2
  173. package/build/core/projection-wake.js +5 -2
  174. package/build/core/{queue_v2.js → queue.js} +2 -1
  175. package/build/core/registry.js +1 -1
  176. package/build/core/renew.js +7 -7
  177. package/build/core/response-artifact-catalog-sqlite.js +112 -0
  178. package/build/core/response-artifact-catalog.js +7 -0
  179. package/build/core/seal.js +44 -16
  180. package/build/core/status/board.js +286 -0
  181. package/build/core/status/drift.js +99 -0
  182. package/build/core/status/lifecycle.js +84 -0
  183. package/build/core/status/reconciliation.js +131 -0
  184. package/build/core/stored-agent-event.js +28 -4
  185. package/build/core/target-ref.js +10 -0
  186. package/build/core/task/board.js +175 -0
  187. package/build/core/task/commands.js +211 -0
  188. package/build/core/task/document.js +189 -0
  189. package/build/core/task/model.js +1 -0
  190. package/build/core/task/settlement-git.js +207 -0
  191. package/build/core/task/settlement-policy.js +82 -0
  192. package/build/core/task/source-board.js +84 -0
  193. package/build/core/task/source-model.js +1 -0
  194. package/build/core/task/validation.js +44 -0
  195. package/build/core/task-contract.js +224 -0
  196. package/build/core/task-doctor.js +14 -0
  197. package/build/core/task-git-actor.js +38 -0
  198. package/build/core/task-git-runtime.js +34 -0
  199. package/build/core/task-git-store.js +375 -0
  200. package/build/core/task-repository.js +1 -0
  201. package/build/core/task-store-repository.js +86 -0
  202. package/build/core/task-store.js +1 -0
  203. package/build/core/task-worktree.js +51 -0
  204. package/build/core/task.js +4 -0
  205. package/build/core/transcripts.js +138 -56
  206. package/build/core/validation.js +17 -0
  207. package/build/core/verdict.js +2 -2
  208. package/build/core/worktree-path.js +24 -6
  209. package/build/flow-error.js +3 -2
  210. package/build/git/commits.js +3 -0
  211. package/build/git/diff/parsers.js +0 -94
  212. package/build/index.js +2 -0
  213. package/build/keiyaku-constants.js +2 -0
  214. package/build/keiyaku.js +4 -3
  215. package/package.json +3 -2
  216. package/skills/keiyaku/SKILL.md +1 -1
  217. package/skills/keiyaku-akuma/SKILL.md +11 -6
  218. package/build/agents/harness/pump.js +0 -158
  219. package/build/agents/providers/codex-app-server.js +0 -864
  220. package/build/agents/providers/opencode-sdk.js +0 -363
  221. package/build/agents/providers/pi.js +0 -557
  222. package/build/config/settings.js +0 -533
  223. package/build/core/call.js +0 -686
  224. package/build/core/projection/generation.js +0 -412
  225. package/build/core/projection/identity.js +0 -81
  226. package/build/core/projection/mint.js +0 -174
  227. package/build/core/projection-generation-store.js +0 -707
  228. package/build/core/status.js +0 -539
@@ -0,0 +1,269 @@
1
+ import { z } from "zod";
2
+ import { FlowError } from "../../../flow-error.js";
3
+ import { appendDebugLog } from "../../../telemetry/debug-log.js";
4
+ import { createProjectionDriver } from "../../harness/execution-handle.js";
5
+ import { createAbortError, emitProviderEvidence, sha256Utf8 } from "../../harness/runtime.js";
6
+ import { SubagentFailure } from "../../types.js";
7
+ import { createPiCheckpointController, PI_PROVIDER, toPiSdkThinkingLevel, } from "./checkpoint.js";
8
+ import { bindPiSessionEvents } from "./events.js";
9
+ /** Opaque Pi resume handle: exact session file path (UUID alone is ambiguous). */
10
+ export const piSessionSchema = z.object({
11
+ sessionFile: z.string().trim().min(1),
12
+ sessionId: z.string().trim().min(1).optional(),
13
+ });
14
+ /** Exact model shape: `<provider>/<id>` (single slash separating non-empty sides). */
15
+ const PI_MODEL_PATTERN = /^[^/]+\/[^/]+$/;
16
+ const importPiSdk = new Function("specifier", "return import(specifier)");
17
+ async function loadPiSdk(loader) {
18
+ if (loader) {
19
+ return loader();
20
+ }
21
+ if (_piSdkLoader) {
22
+ return _piSdkLoader();
23
+ }
24
+ return (await importPiSdk("@earendil-works/pi-coding-agent"));
25
+ }
26
+ /** @deprecated Use PiOptions.loader instead. Kept for pipeline-based tests pending loader channel. */
27
+ let _piSdkLoader;
28
+ /** @deprecated Use PiOptions.loader instead. */
29
+ export function setPiSdkLoaderForTests(loader) {
30
+ _piSdkLoader = loader;
31
+ }
32
+ /** @deprecated Use PiOptions.loader instead. */
33
+ export function resetPiSdkLoaderForTests() {
34
+ _piSdkLoader = undefined;
35
+ }
36
+ export function parsePiModelRef(model) {
37
+ const trimmed = model.trim();
38
+ if (!PI_MODEL_PATTERN.test(trimmed)) {
39
+ throw new FlowError("INVALID_SETTINGS", `Invalid pi model '${model}'. Expected exact shape '<provider>/<id>'.`);
40
+ }
41
+ const slash = trimmed.indexOf("/");
42
+ return {
43
+ provider: trimmed.slice(0, slash),
44
+ id: trimmed.slice(slash + 1),
45
+ };
46
+ }
47
+ export async function startPi(prompt, cwd, options = {}) {
48
+ const resumeSessionFile = options.sessionFile?.trim();
49
+ appendDebugLog(`startPi: mode=${resumeSessionFile ? "resume" : "fresh"} model=${options.model ?? "default"}`, { cwd, section: PI_PROVIDER });
50
+ const sdk = await loadPiSdk(options.loader);
51
+ const sessionManagerApi = readSessionManager(sdk);
52
+ const sessionManager = resumeSessionFile
53
+ ? sessionManagerApi.open(resumeSessionFile)
54
+ : sessionManagerApi.create(cwd);
55
+ let model;
56
+ let authStorage;
57
+ let modelRegistry;
58
+ if (options.model !== undefined && options.model.trim().length > 0) {
59
+ const original = options.model.trim();
60
+ const ref = parsePiModelRef(original);
61
+ authStorage = sdk.AuthStorage.create();
62
+ modelRegistry = sdk.ModelRegistry.create(authStorage);
63
+ model = modelRegistry.find(ref.provider, ref.id);
64
+ if (!model) {
65
+ throw new SubagentFailure("SUBAGENT_PROVIDER_UNAVAILABLE", `${PI_PROVIDER} model '${original}' is not available`);
66
+ }
67
+ }
68
+ // With an Akuma body, retain the default loader's discovery and override only its append list.
69
+ // The override deliberately suppresses discovered APPEND_SYSTEM.md content.
70
+ if (options.appendSystemPromptOverride && (!sdk.DefaultResourceLoader || !sdk.getAgentDir)) {
71
+ throw new Error(`${PI_PROVIDER} SDK does not expose DefaultResourceLoader and getAgentDir`);
72
+ }
73
+ let emittedAppendEvidence = false;
74
+ let pendingEvidenceDiagnostics = 0;
75
+ let reportEvidenceDiagnostic = () => {
76
+ pendingEvidenceDiagnostics += 1;
77
+ appendDebugLog("provider evidence callback failed before Pi driver start", { cwd, section: PI_PROVIDER });
78
+ };
79
+ const observedAppendOverride = options.appendSystemPromptOverride && options.onEvidence
80
+ ? () => {
81
+ const body = options.appendSystemPromptOverride();
82
+ if (!emittedAppendEvidence) {
83
+ emittedAppendEvidence = true;
84
+ emitProviderEvidence(options.onEvidence, { type: "outbound", provider: PI_PROVIDER, direction: "input", kind: "system-prompt", ...sha256Utf8(body.join("\n")) }, reportEvidenceDiagnostic);
85
+ }
86
+ return body;
87
+ }
88
+ : options.appendSystemPromptOverride;
89
+ const resourceLoader = observedAppendOverride
90
+ ? new sdk.DefaultResourceLoader({
91
+ cwd,
92
+ agentDir: sdk.getAgentDir(),
93
+ appendSystemPromptOverride: observedAppendOverride,
94
+ })
95
+ : undefined;
96
+ await resourceLoader?.reload();
97
+ // Empty bodies retain the SDK's default ResourceLoader path unchanged.
98
+ const created = await sdk.createAgentSession({
99
+ cwd,
100
+ sessionManager,
101
+ ...(options.thinkingLevel ? { thinkingLevel: toPiSdkThinkingLevel(options.thinkingLevel) } : {}),
102
+ ...(model ? { model, authStorage, modelRegistry } : {}),
103
+ ...(resourceLoader ? { resourceLoader } : {}),
104
+ });
105
+ const session = created.session;
106
+ if (options.onEvidence)
107
+ emitProviderEvidence(options.onEvidence, { type: "session", provider: PI_PROVIDER, sessionId: session.sessionId, ...(session.sessionFile ? { sessionFile: session.sessionFile } : {}) }, reportEvidenceDiagnostic);
108
+ session.setSteeringMode("one-at-a-time");
109
+ const checkpointController = createPiCheckpointController(session, {
110
+ onEvidence: options.onEvidence,
111
+ diagnostic: () => reportEvidenceDiagnostic,
112
+ onSyncCheckpoint: options.onSyncCheckpoint,
113
+ });
114
+ let disposed = false;
115
+ let unsub;
116
+ const disposeSession = (propagate = false) => {
117
+ if (disposed)
118
+ return;
119
+ disposed = true;
120
+ unsub?.();
121
+ try {
122
+ session.dispose();
123
+ }
124
+ catch (error) {
125
+ if (propagate)
126
+ throw error;
127
+ }
128
+ };
129
+ let resolveRunSettled;
130
+ const runSettled = new Promise((resolve) => {
131
+ resolveRunSettled = resolve;
132
+ });
133
+ let activePrompt;
134
+ let abortRequested;
135
+ const requestAbort = async (state) => {
136
+ state.abortIntent = true;
137
+ if (abortRequested) {
138
+ await abortRequested;
139
+ return;
140
+ }
141
+ abortRequested = (async () => {
142
+ // Publish abortRequested before clearQueue can synchronously emit queue_update.
143
+ await Promise.resolve();
144
+ try {
145
+ session.clearQueue();
146
+ await session.abort();
147
+ }
148
+ finally {
149
+ if (state.settled) {
150
+ disposeSession();
151
+ }
152
+ }
153
+ })();
154
+ await abortRequested;
155
+ };
156
+ return createProjectionDriver({
157
+ steerMode: "mid-execution",
158
+ initialTurn: options.initialTurn,
159
+ enqueue: checkpointController.enqueue,
160
+ onIngress: options.onIngress,
161
+ async abort(mode) {
162
+ if (mode !== "force") {
163
+ await requestAbort({ abortIntent: true, settled: false });
164
+ return;
165
+ }
166
+ let failure;
167
+ try {
168
+ await requestAbort({ abortIntent: true, settled: false });
169
+ }
170
+ catch (error) {
171
+ failure = error;
172
+ }
173
+ try {
174
+ disposeSession(true);
175
+ }
176
+ catch (error) {
177
+ failure ??= error;
178
+ }
179
+ await activePrompt;
180
+ await runSettled;
181
+ if (failure !== undefined)
182
+ throw failure;
183
+ },
184
+ async run(builder) {
185
+ reportEvidenceDiagnostic = () => builder.emit({ type: "diagnostic", text: "provider evidence callback failed" });
186
+ checkpointController.setTurnStartedEmitter(() => builder.emit({ type: "turn", phase: "started" }));
187
+ while (pendingEvidenceDiagnostics > 0) {
188
+ pendingEvidenceDiagnostics -= 1;
189
+ reportEvidenceDiagnostic();
190
+ }
191
+ const state = {
192
+ abortIntent: false,
193
+ lastAssistantMessage: "",
194
+ settled: false,
195
+ thoughtTurnOrdinal: 0,
196
+ assistantMessageOrdinal: -1,
197
+ activityCalls: new Map(),
198
+ };
199
+ let settleOutcome;
200
+ const outcome = new Promise((resolve, reject) => {
201
+ settleOutcome = {
202
+ resolve(value) {
203
+ if (state.settled)
204
+ return;
205
+ state.settled = true;
206
+ resolve(value);
207
+ },
208
+ reject(error) {
209
+ if (state.settled)
210
+ return;
211
+ state.settled = true;
212
+ reject(error);
213
+ },
214
+ };
215
+ unsub = bindPiSessionEvents(session, builder, checkpointController, state, settleOutcome);
216
+ });
217
+ outcome.catch(() => undefined);
218
+ const onAbort = () => {
219
+ void requestAbort(state)
220
+ .catch((error) => {
221
+ settleOutcome.reject(error);
222
+ })
223
+ .finally(() => {
224
+ if (!state.settled) {
225
+ settleOutcome.reject(createAbortError(`${PI_PROVIDER} exec cancelled by client`));
226
+ }
227
+ });
228
+ };
229
+ builder.signal.addEventListener("abort", onAbort, { once: true });
230
+ options.signal?.addEventListener("abort", onAbort, { once: true });
231
+ try {
232
+ if (builder.signal.aborted || options.signal?.aborted) {
233
+ await requestAbort(state);
234
+ throw createAbortError(`${PI_PROVIDER} exec cancelled by client`);
235
+ }
236
+ activePrompt = session.prompt(prompt).catch((error) => {
237
+ if (state.settled) {
238
+ return;
239
+ }
240
+ settleOutcome.reject(state.abortIntent ? createAbortError(`${PI_PROVIDER} exec cancelled by client`) : error);
241
+ });
242
+ const result = await outcome;
243
+ await activePrompt;
244
+ return result;
245
+ }
246
+ catch (error) {
247
+ if (state.abortIntent) {
248
+ throw createAbortError(`${PI_PROVIDER} exec cancelled by client`);
249
+ }
250
+ throw error;
251
+ }
252
+ finally {
253
+ builder.signal.removeEventListener("abort", onAbort);
254
+ options.signal?.removeEventListener("abort", onAbort);
255
+ await abortRequested?.catch(() => undefined);
256
+ try {
257
+ disposeSession();
258
+ }
259
+ finally {
260
+ resolveRunSettled();
261
+ }
262
+ }
263
+ },
264
+ });
265
+ }
266
+ function readSessionManager(sdk) {
267
+ const store = sdk[`Session${"Man"}${"ager"}`];
268
+ return store;
269
+ }
@@ -0,0 +1,41 @@
1
+ import { emitProviderEvidence, sha256Utf8 } from "../../harness/runtime.js";
2
+ export const PI_PROVIDER = "pi";
3
+ export function toPiSdkThinkingLevel(effort) {
4
+ return effort; // opacity boundary
5
+ }
6
+ export function normalizeClearQueue(result) {
7
+ if (Array.isArray(result))
8
+ return result;
9
+ return [...(result.steering ?? []), ...(result.followUp ?? [])];
10
+ }
11
+ export function createPiCheckpointController(session, options) {
12
+ let submittedTurnOrdinal = 0;
13
+ let emitTurnStarted;
14
+ const enqueue = async (tell) => {
15
+ if (options.onEvidence)
16
+ emitProviderEvidence(options.onEvidence, { type: "outbound", provider: PI_PROVIDER, direction: "input", kind: "steer", ...sha256Utf8(tell.text) }, options.diagnostic());
17
+ if (tell.effort) {
18
+ if (!session.setThinkingLevel)
19
+ throw new Error(`${PI_PROVIDER} session does not support thinking level control`);
20
+ session.setThinkingLevel(toPiSdkThinkingLevel(tell.effort));
21
+ }
22
+ await session.steer(tell.text);
23
+ emitTurnStarted?.();
24
+ if (options.onEvidence)
25
+ emitProviderEvidence(options.onEvidence, { type: "ack", provider: PI_PROVIDER, fence: { kind: "ordinal", ordinal: submittedTurnOrdinal }, outbound: "steer" }, options.diagnostic());
26
+ return { fence: { kind: "ordinal", ordinal: submittedTurnOrdinal } };
27
+ };
28
+ const control = { enqueue };
29
+ return {
30
+ enqueue,
31
+ control,
32
+ setTurnStartedEmitter(emitter) { emitTurnStarted = emitter; },
33
+ completeTurn() {
34
+ submittedTurnOrdinal += 1;
35
+ const checkpoint = { fence: { kind: "ordinal", ordinal: submittedTurnOrdinal }, consumptionEvidence: true };
36
+ if (options.onEvidence)
37
+ emitProviderEvidence(options.onEvidence, { type: "checkpoint", provider: PI_PROVIDER, fence: checkpoint.fence, source: "notification" }, options.diagnostic());
38
+ options.onSyncCheckpoint?.(control, checkpoint);
39
+ },
40
+ };
41
+ }
@@ -0,0 +1,282 @@
1
+ import { SUBAGENT_SESSION_VERSION } from "../../session.js";
2
+ import { activityCallFromNamedTool, boundActivityExcerpt, diffstatFromUnifiedPatch } from "../../harness/activity-values.js";
3
+ import { AGENT_THOUGHT_TEXT_MAX_CHARS } from "../../harness/events.js";
4
+ import { createAbortError } from "../../harness/runtime.js";
5
+ import { SubagentFailure } from "../../types.js";
6
+ import { PI_PROVIDER } from "./checkpoint.js";
7
+ const PI_ACKNOWLEDGED_UNPERSISTED = new Set(["message_update", "queue_update"]);
8
+ const PI_MAPPED = new Set(["agent_start", "agent_end", "turn_start", "turn_end", "message_start", "message_end", "tool_execution_start", "tool_execution_update", "tool_execution_end", "compaction_start", "compaction_end", "session_info_changed", "thinking_level_changed", "auto_retry_start", "auto_retry_end"]);
9
+ function piIngressDisposition(type) {
10
+ if (PI_ACKNOWLEDGED_UNPERSISTED.has(type))
11
+ return "acknowledged-unpersisted";
12
+ if (PI_MAPPED.has(type))
13
+ return "mapped";
14
+ return "unknown";
15
+ }
16
+ function extractAssistantText(event) {
17
+ if (event.type === "message_end") {
18
+ const message = event.message;
19
+ if (message?.role !== "assistant")
20
+ return undefined;
21
+ return extractContentText(message.content);
22
+ }
23
+ return undefined;
24
+ }
25
+ function extractContentText(content) {
26
+ if (typeof content === "string") {
27
+ const trimmed = content.trim();
28
+ return trimmed || undefined;
29
+ }
30
+ if (!Array.isArray(content))
31
+ return undefined;
32
+ const text = content
33
+ .map((block) => {
34
+ if (typeof block === "string")
35
+ return block;
36
+ if (block &&
37
+ typeof block === "object" &&
38
+ block.type === "text") {
39
+ const candidate = block.text;
40
+ return typeof candidate === "string" ? candidate : "";
41
+ }
42
+ return "";
43
+ })
44
+ .join("")
45
+ .trim();
46
+ return text || undefined;
47
+ }
48
+ function emitPiThoughts(builder, event, turnOrdinal, messageOrdinal) {
49
+ if (event.type !== "message_end")
50
+ return;
51
+ const message = event.message;
52
+ if (message?.role !== "assistant" || !Array.isArray(message.content))
53
+ return;
54
+ message.content.forEach((block, blockIndex) => {
55
+ if (!block || typeof block !== "object")
56
+ return;
57
+ const record = block;
58
+ if (record.type !== "thinking" || record.redacted === true)
59
+ return;
60
+ if (typeof record.thinking !== "string" || record.thinking.trim().length === 0)
61
+ return;
62
+ const truncated = record.thinking.length > AGENT_THOUGHT_TEXT_MAX_CHARS;
63
+ builder.emit({
64
+ type: "thought",
65
+ id: `t${turnOrdinal}:m${messageOrdinal}:b${blockIndex}`,
66
+ text: record.thinking.slice(0, AGENT_THOUGHT_TEXT_MAX_CHARS),
67
+ final: true,
68
+ ...(truncated ? { truncated: true } : {}),
69
+ });
70
+ });
71
+ }
72
+ function piRecord(value) {
73
+ return value !== null && typeof value === "object" && !Array.isArray(value)
74
+ ? value
75
+ : undefined;
76
+ }
77
+ function piCallFacts(args) {
78
+ const value = piRecord(args);
79
+ if (!value)
80
+ return {};
81
+ return {
82
+ ...(typeof value.command === "string" ? { command: value.command } : {}),
83
+ ...(typeof value.description === "string" ? { description: value.description } : {}),
84
+ ...(typeof value.path === "string" ? { path: value.path } : {}),
85
+ ...(typeof value.offset === "number" && Number.isFinite(value.offset) ? { offset: value.offset } : {}),
86
+ ...(typeof value.limit === "number" && Number.isFinite(value.limit) ? { limit: value.limit } : {}),
87
+ ...(typeof value.query === "string" ? { query: value.query } : {}),
88
+ };
89
+ }
90
+ function piResult(value, isError) {
91
+ const payload = piRecord(value);
92
+ const content = Array.isArray(payload?.content)
93
+ ? payload.content.flatMap((entry) => {
94
+ const block = piRecord(entry);
95
+ return block?.type === "text" && typeof block.text === "string" ? [block.text] : [];
96
+ }).join("\n")
97
+ : undefined;
98
+ const details = piRecord(payload?.details);
99
+ const diffstat = typeof details?.patch === "string" ? diffstatFromUnifiedPatch(details.patch) : undefined;
100
+ return {
101
+ status: isError ? "error" : "ok",
102
+ ...(content ? { message: boundActivityExcerpt(content) } : {}),
103
+ ...(diffstat ? { diffstat } : {}),
104
+ };
105
+ }
106
+ function piActivityBody(event, calls) {
107
+ if (event.type !== "tool_execution_start" &&
108
+ event.type !== "tool_execution_update" &&
109
+ event.type !== "tool_execution_end") {
110
+ return undefined;
111
+ }
112
+ if (typeof event.toolCallId !== "string" || typeof event.toolName !== "string")
113
+ return undefined;
114
+ const phase = event.type === "tool_execution_start"
115
+ ? "started"
116
+ : event.type === "tool_execution_update"
117
+ ? "updated"
118
+ : "completed";
119
+ if (event.type === "tool_execution_start") {
120
+ calls.set(event.toolCallId, activityCallFromNamedTool(event.toolName, piCallFacts(event.args)));
121
+ }
122
+ const call = calls.get(event.toolCallId) ?? (event.type === "tool_execution_end"
123
+ ? { kind: "other", display: event.toolName }
124
+ : activityCallFromNamedTool(event.toolName, piCallFacts(event.args)));
125
+ if (event.type === "tool_execution_end")
126
+ calls.delete(event.toolCallId);
127
+ return {
128
+ type: "activity",
129
+ phase,
130
+ id: event.toolCallId,
131
+ tool: event.toolName,
132
+ call,
133
+ ...(event.type === "tool_execution_start"
134
+ ? {}
135
+ : { result: piResult(event.type === "tool_execution_update" ? event.partialResult : event.result, event.type === "tool_execution_end" && event.isError) }),
136
+ };
137
+ }
138
+ function piUsageBody(event) {
139
+ if (event.type !== "message_end")
140
+ return undefined;
141
+ const message = event.message;
142
+ if (message?.role !== "assistant")
143
+ return undefined;
144
+ const usage = message.usage;
145
+ if (!usage || typeof usage.input !== "number" || typeof usage.output !== "number")
146
+ return undefined;
147
+ return {
148
+ type: "usage",
149
+ inputTokens: usage.input,
150
+ outputTokens: usage.output,
151
+ ...(typeof usage.cacheRead === "number" ? { cachedInputTokens: usage.cacheRead } : {}),
152
+ ...(typeof usage.cost?.total === "number" ? { costUsd: usage.cost.total } : {}),
153
+ ...(typeof message.responseModel === "string"
154
+ ? { model: message.responseModel }
155
+ : typeof message.model === "string"
156
+ ? { model: message.model }
157
+ : {}),
158
+ };
159
+ }
160
+ /** True when native messages prove the run stopped via abort (stopReason aborted). */
161
+ function agentEndLooksAborted(event) {
162
+ if (event.type !== "agent_end")
163
+ return false;
164
+ const messages = event.messages;
165
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
166
+ const message = messages[i];
167
+ if (!message || message.role !== "assistant")
168
+ continue;
169
+ if (message.stopReason === "aborted")
170
+ return true;
171
+ // Last assistant message is the terminal one for this run.
172
+ break;
173
+ }
174
+ return false;
175
+ }
176
+ /** Returns the terminal Pi provider error, if the final assistant message ended in error. */
177
+ function agentEndProviderError(event) {
178
+ if (event.type !== "agent_end")
179
+ return undefined;
180
+ const messages = event.messages;
181
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
182
+ const message = messages[i];
183
+ if (!message || message.role !== "assistant")
184
+ continue;
185
+ if (message.stopReason !== "error")
186
+ return undefined;
187
+ if (typeof message.errorMessage === "string" && message.errorMessage.trim()) {
188
+ return message.errorMessage;
189
+ }
190
+ return `${PI_PROVIDER} provider error (stopReason error)`;
191
+ }
192
+ return undefined;
193
+ }
194
+ function buildPiSessionOutcome(session, finalMessage) {
195
+ const sessionFile = session.sessionFile;
196
+ if (!sessionFile) {
197
+ return new SubagentFailure("SUBAGENT_EXEC_ERROR", `${PI_PROVIDER} exec ended without a session file`);
198
+ }
199
+ return {
200
+ finalMessage,
201
+ session: {
202
+ provider: "pi",
203
+ version: SUBAGENT_SESSION_VERSION,
204
+ handle: {
205
+ sessionFile,
206
+ sessionId: session.sessionId,
207
+ },
208
+ },
209
+ };
210
+ }
211
+ export function bindPiSessionEvents(session, builder, controller, state, settle) {
212
+ return session.subscribe((event) => {
213
+ builder.ingress(PI_PROVIDER, event.type, event, piIngressDisposition(event.type), () => {
214
+ if (event.type === "agent_start") {
215
+ builder.emit({ type: "turn", phase: "started" });
216
+ }
217
+ if (event.type === "turn_start") {
218
+ state.thoughtTurnOrdinal += 1;
219
+ state.assistantMessageOrdinal = -1;
220
+ state.activeAssistantMessageOrdinal = undefined;
221
+ }
222
+ if (event.type === "message_start") {
223
+ const message = event.message;
224
+ if (message?.role === "assistant") {
225
+ state.assistantMessageOrdinal += 1;
226
+ state.activeAssistantMessageOrdinal = state.assistantMessageOrdinal;
227
+ }
228
+ }
229
+ const activity = piActivityBody(event, state.activityCalls);
230
+ if (activity)
231
+ builder.emit(activity);
232
+ if (event.type === "message_end") {
233
+ const message = event.message;
234
+ if (message?.role === "assistant") {
235
+ if (state.activeAssistantMessageOrdinal === undefined) {
236
+ state.assistantMessageOrdinal += 1;
237
+ state.activeAssistantMessageOrdinal = state.assistantMessageOrdinal;
238
+ }
239
+ const messageId = `pi-assistant-${state.activeAssistantMessageOrdinal}`;
240
+ emitPiThoughts(builder, event, state.thoughtTurnOrdinal, state.activeAssistantMessageOrdinal);
241
+ const assistantText = extractAssistantText(event);
242
+ if (assistantText) {
243
+ state.lastAssistantMessage = assistantText;
244
+ builder.emit({ type: "message", id: messageId, text: assistantText });
245
+ }
246
+ state.activeAssistantMessageOrdinal = undefined;
247
+ }
248
+ }
249
+ const usage = piUsageBody(event);
250
+ if (usage)
251
+ builder.emit(usage);
252
+ if (event.type === "turn_end" && !state.abortIntent)
253
+ controller.completeTurn();
254
+ if (event.type !== "agent_end" || state.settled) {
255
+ return;
256
+ }
257
+ if (state.abortIntent || agentEndLooksAborted(event)) {
258
+ settle.reject(createAbortError(`${PI_PROVIDER} exec cancelled by client`));
259
+ return;
260
+ }
261
+ if (event.willRetry) {
262
+ return;
263
+ }
264
+ const providerError = agentEndProviderError(event);
265
+ if (providerError) {
266
+ const failure = new SubagentFailure("SUBAGENT_EXEC_ERROR", providerError);
267
+ builder.emit({ type: "fail", message: failure.message });
268
+ settle.reject(failure);
269
+ return;
270
+ }
271
+ const outcome = buildPiSessionOutcome(session, state.lastAssistantMessage);
272
+ if (outcome instanceof SubagentFailure) {
273
+ builder.emit({ type: "fail", message: outcome.message });
274
+ settle.reject(outcome);
275
+ return;
276
+ }
277
+ builder.emit({ type: "turn", phase: "completed" });
278
+ builder.emit({ type: "done", finalMessage: outcome.finalMessage, session: outcome.session });
279
+ settle.resolve(outcome);
280
+ });
281
+ });
282
+ }
@@ -1,6 +1,8 @@
1
1
  import { getConfig } from "../config/env.js";
2
- import { loadAkumaCatalogV2, } from "../config/akuma-loader_v2.js";
3
- import { assertSettingsKnobUsable, loadKeiyakuSettings, resolveDefaultAgentName, } from "../config/settings.js";
2
+ import { loadAkumaCatalog, } from "../config/akuma-loader.js";
3
+ import { assertSettingsKnobUsable } from "../config/settings/disease.js";
4
+ import { resolveDefaultAgentName } from "../config/settings/knobs.js";
5
+ import { loadKeiyakuSettings } from "../config/settings/loader.js";
4
6
  import { nativePolicyOwnership } from "../config/provider-policy-ownership.js";
5
7
  import { FlowError } from "../flow-error.js";
6
8
  const ACCESS_VALUES = ["read", "write", "auto"];
@@ -32,7 +34,7 @@ const SUPPORTED_EXPLICIT = {
32
34
  webSearch: new Set(),
33
35
  },
34
36
  };
35
- export function formatAvailableAgentNamesV2(names) {
37
+ export function formatAvailableAgentNames(names) {
36
38
  if (names.length === 0)
37
39
  return "(none configured)";
38
40
  return [...names].sort().join(", ");
@@ -78,7 +80,7 @@ function freezeResolvedProfile(profile) {
78
80
  effortOptions: Object.freeze([...profile.effortOptions]),
79
81
  });
80
82
  }
81
- export async function selectSubagentV2(agentName, cwd) {
83
+ export async function selectSubagent(agentName, cwd) {
82
84
  const loaded = await loadKeiyakuSettings(cwd);
83
85
  assertSettingsKnobUsable(loaded, "agents");
84
86
  const settings = loaded.knobs;
@@ -87,11 +89,11 @@ export async function selectSubagentV2(agentName, cwd) {
87
89
  assertSettingsKnobUsable(loaded, "default");
88
90
  }
89
91
  const selected = requested || getConfig().subagent.nameOverride || resolveDefaultAgentName(settings);
90
- const catalog = await loadAkumaCatalogV2(cwd);
92
+ const catalog = await loadAkumaCatalog(cwd);
91
93
  const availableNames = Object.keys(catalog.selected);
92
94
  const candidate = catalog.selected[selected];
93
95
  if (candidate === undefined) {
94
- throw new FlowError("UNKNOWN_SUBAGENT", `Unknown agent '${selected}'. Expected one of: ${formatAvailableAgentNamesV2(availableNames)}`);
96
+ throw new FlowError("UNKNOWN_SUBAGENT", `Unknown agent '${selected}'. Expected one of: ${formatAvailableAgentNames(availableNames)}`);
95
97
  }
96
98
  if (!isValidAkumaCandidate(candidate)) {
97
99
  throw new FlowError("INVALID_SETTINGS", `Akuma '${selected}' is invalid: ${candidate.reason}`);
@@ -0,0 +1,4 @@
1
+ import { missingSubcommandError } from "../../../help.js";
2
+ export const handler = async () => {
3
+ throw missingSubcommandError("akuma", ["ls", "view"]);
4
+ };
@@ -0,0 +1,16 @@
1
+ export const meta = {
2
+ command: "akuma",
3
+ summary: "Inspect Akuma profiles",
4
+ usage: ["keiyaku akuma ls|view"],
5
+ stdin: "none",
6
+ flags: [],
7
+ purpose: "Inspect configured helper profiles.",
8
+ subcommands: ["ls", "view"],
9
+ notes: [
10
+ "Define user profiles in <KEIYAKU_HOME>/akuma/<name>.md.",
11
+ "Define project profiles in .keiyaku/akuma/<name>.md.",
12
+ "See README Configuration for the Markdown frontmatter format.",
13
+ ],
14
+ examples: ["keiyaku akuma ls", "keiyaku akuma view akuma-codex"],
15
+ completionRank: 6,
16
+ };
@@ -0,0 +1,8 @@
1
+ import { meta as akumaMeta } from "./akuma/meta.js";
2
+ import { meta as lsMeta } from "./list/meta-ls.js";
3
+ import { meta as viewMeta } from "./view/meta.js";
4
+ export const akumaCatalog = [
5
+ { meta: akumaMeta, load: () => import("./akuma/handler.js") },
6
+ { meta: lsMeta, load: () => import("./list/handler.js") },
7
+ { meta: viewMeta, load: () => import("./view/handler.js") },
8
+ ];