@astrosheep/keiyaku 1.0.0 → 1.0.2

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 (123) hide show
  1. package/README.md +73 -56
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/call-terms_v2.js +103 -0
  4. package/build/agents/harness/activity-values.js +54 -0
  5. package/build/agents/harness/event-channel.js +55 -20
  6. package/build/agents/harness/events.js +47 -1
  7. package/build/agents/harness/execution-handle.js +292 -61
  8. package/build/agents/harness/index.js +103 -121
  9. package/build/agents/harness/pump.js +36 -47
  10. package/build/agents/harness/runtime.js +3 -0
  11. package/build/agents/launch-snapshot_v2.js +348 -0
  12. package/build/agents/opencode-sdk.js +42 -169
  13. package/build/agents/providers/claude-agent-sdk.js +240 -79
  14. package/build/agents/providers/codex-app-server.js +280 -72
  15. package/build/agents/providers/codex-sdk.js +159 -41
  16. package/build/agents/providers/event-type-tail.js +16 -0
  17. package/build/agents/providers/opencode-sdk.js +165 -59
  18. package/build/agents/providers/pi.js +254 -42
  19. package/build/agents/selector_v2.js +122 -0
  20. package/build/cli/commands/akuma.js +120 -42
  21. package/build/cli/completion.js +6 -7
  22. package/build/cli/flags.js +36 -10
  23. package/build/cli/help.js +42 -26
  24. package/build/cli/index.js +225 -93
  25. package/build/cli/parse.js +194 -75
  26. package/build/cli/projection-address.js +11 -0
  27. package/build/cli/render/address.js +88 -0
  28. package/build/cli/render/arc.js +10 -10
  29. package/build/cli/render/call.js +59 -0
  30. package/build/cli/render/compact-text.js +3 -0
  31. package/build/cli/render/errors.js +4 -2
  32. package/build/cli/render/format.js +6 -0
  33. package/build/cli/render/line-width.js +70 -0
  34. package/build/cli/render/misc.js +7 -4
  35. package/build/cli/render/petition.js +4 -3
  36. package/build/cli/render/projection-activity.js +201 -0
  37. package/build/cli/render/shared.js +46 -12
  38. package/build/cli/render/status.js +59 -10
  39. package/build/cli/render/tool-ledger-rollup.js +74 -0
  40. package/build/cli/render/tool-presentation.js +97 -0
  41. package/build/cli/render/wait.js +229 -0
  42. package/build/cli/skills-install.js +42 -24
  43. package/build/cli/subagent-guard.js +1 -1
  44. package/build/config/akuma-loader_v2.js +222 -0
  45. package/build/config/env-keys.js +0 -4
  46. package/build/config/env.js +0 -6
  47. package/build/config/settings.js +325 -12
  48. package/build/core/addressing.js +224 -0
  49. package/build/core/amend.js +30 -47
  50. package/build/core/arc.js +68 -60
  51. package/build/core/bind.js +105 -25
  52. package/build/core/call-persist.js +69 -0
  53. package/build/core/call.js +658 -0
  54. package/build/core/claim.js +72 -44
  55. package/build/core/command-io.js +19 -19
  56. package/build/core/context.js +11 -36
  57. package/build/core/draft.js +2 -3
  58. package/build/core/entry.js +101 -6
  59. package/build/core/execution-coordinate.js +62 -0
  60. package/build/core/execution-pact.js +141 -0
  61. package/build/core/forfeit.js +2 -3
  62. package/build/core/hints.js +10 -10
  63. package/build/core/ids.js +6 -0
  64. package/build/core/ledger.js +16 -1
  65. package/build/core/petition-claim-gates.js +14 -12
  66. package/build/core/petition-claim.js +1 -0
  67. package/build/core/petition-run.js +6 -2
  68. package/build/core/petition.js +63 -21
  69. package/build/core/places.js +106 -13
  70. package/build/core/projection/generation.js +412 -0
  71. package/build/core/projection/heart.js +316 -0
  72. package/build/core/projection/identity.js +81 -0
  73. package/build/core/projection/leash.js +87 -0
  74. package/build/core/projection/mint.js +154 -0
  75. package/build/core/projection-activity.js +219 -0
  76. package/build/core/projection-coordinate.js +35 -0
  77. package/build/core/projection-core.js +69 -463
  78. package/build/core/projection-generation-continuation.js +31 -0
  79. package/build/core/projection-generation-execution.js +148 -0
  80. package/build/core/projection-generation-identity.js +17 -0
  81. package/build/core/projection-generation-launcher.js +90 -0
  82. package/build/core/projection-generation-process.js +43 -0
  83. package/build/core/projection-generation-runner.js +144 -0
  84. package/build/core/projection-generation-runtime.js +15 -0
  85. package/build/core/projection-generation-store.js +707 -0
  86. package/build/core/projection-identity.js +11 -0
  87. package/build/core/projection-life-observer.js +87 -0
  88. package/build/core/projection-life-protocol.js +95 -0
  89. package/build/core/projection-mint.js +96 -24
  90. package/build/core/projection-runner-lock.js +230 -0
  91. package/build/core/projection-status.js +207 -107
  92. package/build/core/projection-tells.js +401 -0
  93. package/build/core/projection-wait.js +155 -0
  94. package/build/core/projection-wake.js +109 -231
  95. package/build/core/queue_v2.js +342 -0
  96. package/build/core/registry.js +6 -3
  97. package/build/core/render.js +63 -1
  98. package/build/core/renew.js +80 -53
  99. package/build/core/scope.js +186 -137
  100. package/build/core/seal.js +16 -19
  101. package/build/core/status.js +88 -25
  102. package/build/core/stored-agent-event.js +101 -0
  103. package/build/core/transcripts.js +214 -228
  104. package/build/core/verdict.js +27 -15
  105. package/build/core/worktree-bootstrap.js +187 -0
  106. package/build/core/worktree-path.js +72 -18
  107. package/build/flow-error.js +4 -6
  108. package/build/generated/version.js +1 -1
  109. package/build/git/branches.js +6 -1
  110. package/build/index.js +8 -5
  111. package/build/keiyaku.js +1 -3
  112. package/package.json +6 -5
  113. package/skills/keiyaku/SKILL.md +6 -47
  114. package/skills/keiyaku-akuma/SKILL.md +75 -0
  115. package/build/agents/effective-policy.js +0 -32
  116. package/build/agents/index.js +0 -114
  117. package/build/agents/selector.js +0 -28
  118. package/build/cli/render/summon.js +0 -17
  119. package/build/cli/render/tell.js +0 -11
  120. package/build/config/akuma-loader.js +0 -268
  121. package/build/core/queue.js +0 -73
  122. package/build/core/summon-persist.js +0 -73
  123. package/build/core/summon.js +0 -377
@@ -1,49 +1,291 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { createEventChannel, createEventChannelOverflowError, isEventChannelOverflowError, } from "./event-channel.js";
3
- import { SubagentFailure } from "../types.js";
4
- export function createOverflowFailStream(channel) {
5
- let drained = false;
6
- let resolveDrained;
7
- const drainedPromise = new Promise((resolve) => {
8
- resolveDrained = resolve;
2
+ import { AGENT_ACTIVITY_MAX_CHANGES, AGENT_EVENT_TEXT_MAX_CHARS, AGENT_UNKNOWN_PAYLOAD_MAX_CHARS, } from "./events.js";
3
+ import { createEventChannel, EXEC_EVENT_BUFFER_HARD_CAP, } from "./event-channel.js";
4
+ export function createTruncationMarker(envelope = {
5
+ seq: EXEC_EVENT_BUFFER_HARD_CAP,
6
+ at: new Date().toISOString(),
7
+ turn: 0,
8
+ }) {
9
+ return {
10
+ type: "truncation",
11
+ limit: EXEC_EVENT_BUFFER_HARD_CAP,
12
+ admitted: EXEC_EVENT_BUFFER_HARD_CAP,
13
+ seq: envelope.seq,
14
+ at: envelope.at,
15
+ turn: envelope.turn,
16
+ };
17
+ }
18
+ export function overflowMarkerFromDropped(dropped) {
19
+ if (dropped.type === "truncation")
20
+ return dropped;
21
+ return createTruncationMarker({
22
+ seq: dropped.seq,
23
+ at: dropped.at,
24
+ turn: dropped.turn,
9
25
  });
10
- const markDrained = () => {
11
- if (drained)
12
- return;
13
- drained = true;
14
- resolveDrained();
26
+ }
27
+ function truncateText(value) {
28
+ return {
29
+ value: value.slice(0, AGENT_EVENT_TEXT_MAX_CHARS),
30
+ truncated: value.length > AGENT_EVENT_TEXT_MAX_CHARS,
15
31
  };
32
+ }
33
+ function serializeUnknownPayload(value) {
34
+ let serialized;
35
+ try {
36
+ const encoded = JSON.stringify(value);
37
+ if (encoded === undefined) {
38
+ return {
39
+ payload: "null",
40
+ elided: { reason: "unserializable", retainedChars: 4 },
41
+ };
42
+ }
43
+ serialized = encoded;
44
+ }
45
+ catch {
46
+ serialized = "null";
47
+ return {
48
+ payload: serialized,
49
+ elided: { reason: "unserializable", retainedChars: serialized.length },
50
+ };
51
+ }
52
+ if (serialized.length <= AGENT_UNKNOWN_PAYLOAD_MAX_CHARS)
53
+ return { payload: serialized };
16
54
  return {
17
- events: {
18
- async *[Symbol.asyncIterator]() {
19
- try {
20
- for await (const event of channel.iterable) {
21
- yield event;
22
- }
23
- }
24
- catch (error) {
25
- if (!isEventChannelOverflowError(error)) {
26
- throw error;
55
+ payload: serialized.slice(0, AGENT_UNKNOWN_PAYLOAD_MAX_CHARS),
56
+ elided: {
57
+ reason: "truncated",
58
+ originalChars: serialized.length,
59
+ retainedChars: AGENT_UNKNOWN_PAYLOAD_MAX_CHARS,
60
+ },
61
+ };
62
+ }
63
+ function normalizeBodyForPersistence(body) {
64
+ switch (body.type) {
65
+ case "message": {
66
+ const text = truncateText(body.text);
67
+ return { body: { ...body, text: text.value }, truncated: text.truncated };
68
+ }
69
+ case "unknown": {
70
+ const provider = truncateText(body.provider);
71
+ const nativeType = truncateText(body.nativeType);
72
+ const payload = truncateText(body.payload);
73
+ return {
74
+ body: {
75
+ ...body,
76
+ provider: provider.value,
77
+ nativeType: nativeType.value,
78
+ payload: payload.value,
79
+ ...(payload.truncated && body.elided === undefined
80
+ ? { elided: { reason: "truncated", originalChars: body.payload.length, retainedChars: payload.value.length } }
81
+ : {}),
82
+ },
83
+ truncated: provider.truncated || nativeType.truncated || payload.truncated,
84
+ };
85
+ }
86
+ case "activity": {
87
+ const id = truncateText(body.id);
88
+ const tool = truncateText(body.tool);
89
+ let truncated = id.truncated || tool.truncated;
90
+ const text = (value) => {
91
+ const bounded = truncateText(value);
92
+ truncated ||= bounded.truncated;
93
+ return bounded.value;
94
+ };
95
+ const call = (() => {
96
+ switch (body.call.kind) {
97
+ case "run":
98
+ return {
99
+ ...body.call,
100
+ command: text(body.call.command),
101
+ ...(body.call.description === undefined ? {} : { description: text(body.call.description) }),
102
+ };
103
+ case "read":
104
+ return { ...body.call, path: text(body.call.path) };
105
+ case "search":
106
+ return { ...body.call, query: text(body.call.query) };
107
+ case "fileChange": {
108
+ const kept = body.call.changes.slice(0, AGENT_ACTIVITY_MAX_CHANGES);
109
+ const dropped = body.call.changes.length - kept.length;
110
+ truncated ||= dropped > 0;
111
+ return {
112
+ ...body.call,
113
+ changes: kept.map((change) => ({ ...change, path: text(change.path) })),
114
+ ...((body.call.omittedChanges ?? 0) + dropped > 0
115
+ ? { omittedChanges: (body.call.omittedChanges ?? 0) + dropped }
116
+ : {}),
117
+ };
27
118
  }
28
- yield {
29
- type: "fail",
30
- code: "PROJECTION_STATE_ERROR",
31
- message: error.message,
32
- };
33
- }
34
- finally {
35
- markDrained();
119
+ case "other":
120
+ return { ...body.call, display: text(body.call.display) };
36
121
  }
37
- },
38
- },
39
- whenDrained: () => drainedPromise,
122
+ })();
123
+ const result = body.result === undefined
124
+ ? undefined
125
+ : {
126
+ ...body.result,
127
+ ...(body.result.message === undefined ? {} : { message: text(body.result.message) }),
128
+ };
129
+ return {
130
+ body: {
131
+ ...body,
132
+ id: id.value,
133
+ tool: tool.value,
134
+ call,
135
+ ...(result === undefined ? {} : { result }),
136
+ ...(truncated ? { truncated: true } : {}),
137
+ },
138
+ truncated,
139
+ };
140
+ }
141
+ case "thought":
142
+ case "plan":
143
+ case "truncation":
144
+ case "turn":
145
+ return { body, truncated: false };
146
+ case "usage": {
147
+ const model = body.model === undefined ? undefined : truncateText(body.model);
148
+ return {
149
+ body: { ...body, ...(model ? { model: model.value } : {}) },
150
+ truncated: model?.truncated ?? false,
151
+ };
152
+ }
153
+ case "warning": {
154
+ const originalModel = body.warning.originalModel === undefined ? undefined : truncateText(body.warning.originalModel);
155
+ const fallbackModel = body.warning.fallbackModel === undefined ? undefined : truncateText(body.warning.fallbackModel);
156
+ const direction = body.warning.direction === undefined ? undefined : truncateText(body.warning.direction);
157
+ const requestId = body.warning.requestId === undefined || body.warning.requestId === null
158
+ ? undefined
159
+ : truncateText(body.warning.requestId);
160
+ const signerModel = body.warning.signerModel === undefined ? undefined : truncateText(body.warning.signerModel);
161
+ return {
162
+ body: {
163
+ ...body,
164
+ warning: {
165
+ ...body.warning,
166
+ ...(originalModel ? { originalModel: originalModel.value } : {}),
167
+ ...(fallbackModel ? { fallbackModel: fallbackModel.value } : {}),
168
+ ...(direction ? { direction: direction.value } : {}),
169
+ ...(requestId ? { requestId: requestId.value } : {}),
170
+ ...(signerModel ? { signerModel: signerModel.value } : {}),
171
+ },
172
+ },
173
+ truncated: [originalModel, fallbackModel, direction, requestId, signerModel]
174
+ .some((entry) => entry?.truncated === true),
175
+ };
176
+ }
177
+ case "diagnostic": {
178
+ const text = truncateText(body.text);
179
+ return { body: { ...body, text: text.value }, truncated: text.truncated };
180
+ }
181
+ case "done": {
182
+ const finalMessage = body.finalMessage === undefined ? undefined : truncateText(body.finalMessage);
183
+ return {
184
+ body: { ...body, ...(finalMessage ? { finalMessage: finalMessage.value } : {}) },
185
+ truncated: finalMessage?.truncated ?? false,
186
+ };
187
+ }
188
+ case "fail": {
189
+ const message = truncateText(body.message);
190
+ const diagnostics = body.diagnostics === undefined ? undefined : truncateText(body.diagnostics);
191
+ return {
192
+ body: {
193
+ ...body,
194
+ message: message.value,
195
+ ...(diagnostics ? { diagnostics: diagnostics.value } : {}),
196
+ },
197
+ truncated: message.truncated || (diagnostics?.truncated ?? false),
198
+ };
199
+ }
200
+ }
201
+ }
202
+ export function prepareAgentEventForPersistence(event) {
203
+ const normalized = normalizeBodyForPersistence(event);
204
+ return {
205
+ ...normalized.body,
206
+ seq: event.seq,
207
+ at: event.at,
208
+ turn: event.turn,
209
+ ...(normalized.truncated ? { truncated: true } : {}),
40
210
  };
41
211
  }
42
212
  export function createProjectionDriver(input) {
213
+ if (input.initialTurn !== undefined
214
+ && (!Number.isSafeInteger(input.initialTurn) || input.initialTurn < 0)) {
215
+ throw new Error(`createProjectionDriver initialTurn must be a non-negative safe integer, got ${String(input.initialTurn)}`);
216
+ }
43
217
  const id = input.id ?? randomUUID();
44
218
  const abortController = new AbortController();
45
219
  let finishStarted = false;
46
- let overflowed = false;
220
+ let nextSeq = 0;
221
+ let turn = input.initialTurn ?? 0;
222
+ let ordinaryAdmitted = 0;
223
+ let saturated = false;
224
+ let truncationPublished = false;
225
+ const nowMs = () => (input.now ?? Date.now)();
226
+ const nowIso = () => new Date(nowMs()).toISOString();
227
+ const admit = (body) => {
228
+ // Turn advance owns the builder ordinal for every submitted started body,
229
+ // including bodies that only produce the truncation marker or are dropped.
230
+ if (body.type === "turn" && body.phase === "started") {
231
+ if (turn >= Number.MAX_SAFE_INTEGER) {
232
+ throw new Error(`createProjectionDriver turn overflow: cannot advance past ${Number.MAX_SAFE_INTEGER}`);
233
+ }
234
+ turn += 1;
235
+ }
236
+ if (saturated) {
237
+ return undefined;
238
+ }
239
+ if (ordinaryAdmitted >= EXEC_EVENT_BUFFER_HARD_CAP) {
240
+ if (truncationPublished) {
241
+ saturated = true;
242
+ return undefined;
243
+ }
244
+ saturated = true;
245
+ truncationPublished = true;
246
+ const marker = createTruncationMarker({
247
+ seq: nextSeq,
248
+ at: nowIso(),
249
+ turn,
250
+ });
251
+ nextSeq += 1;
252
+ return marker;
253
+ }
254
+ const event = {
255
+ ...body,
256
+ seq: nextSeq,
257
+ at: nowIso(),
258
+ turn,
259
+ };
260
+ nextSeq += 1;
261
+ ordinaryAdmitted += 1;
262
+ return event;
263
+ };
264
+ const channel = createEventChannel({
265
+ createOverflowMarker: overflowMarkerFromDropped,
266
+ });
267
+ const publish = (event, meta) => {
268
+ channel.push(event, meta);
269
+ };
270
+ const emitBody = (body, meta) => {
271
+ if (meta?.coalesceKey) {
272
+ const prior = channel.peekCoalesced(meta.coalesceKey);
273
+ if (prior !== undefined) {
274
+ const updated = {
275
+ ...body,
276
+ seq: prior.seq,
277
+ at: nowIso(),
278
+ turn: prior.turn,
279
+ };
280
+ publish(updated, meta);
281
+ return;
282
+ }
283
+ }
284
+ const event = admit(body);
285
+ if (event === undefined)
286
+ return;
287
+ publish(event, event.type === "truncation" ? undefined : meta);
288
+ };
47
289
  let resolveCompletion;
48
290
  let rejectCompletion;
49
291
  const completion = new Promise((resolve, reject) => {
@@ -51,47 +293,33 @@ export function createProjectionDriver(input) {
51
293
  rejectCompletion = reject;
52
294
  });
53
295
  completion.catch(() => undefined);
54
- const channel = createEventChannel({
55
- onOverflow: () => {
56
- overflowed = true;
57
- if (!abortController.signal.aborted) {
58
- abortController.abort();
59
- }
60
- void input.abort?.();
61
- },
62
- });
63
- const eventStream = createOverflowFailStream(channel);
64
296
  const finish = (action) => {
65
297
  if (finishStarted)
66
298
  return;
67
299
  finishStarted = true;
68
300
  channel.close();
69
- void eventStream.whenDrained().then(action, action);
301
+ void channel.whenDrained().then(action, action);
70
302
  };
71
- const overflowFailure = () => new SubagentFailure("SUBAGENT_EXEC_ERROR", createEventChannelOverflowError().message, {
72
- diagnostics: "PROJECTION_STATE_ERROR",
73
- });
74
303
  const runPromise = (async () => {
75
304
  try {
76
305
  const outcome = await input.run({
77
- emit(event, meta) {
78
- channel.push(event, meta);
306
+ emit(body, meta) {
307
+ emitBody(body, meta);
308
+ },
309
+ ingress(provider, nativeType, payload, disposition, map) {
310
+ input.onIngress?.();
311
+ if (disposition === "unknown") {
312
+ const bounded = serializeUnknownPayload(payload);
313
+ emitBody({ type: "unknown", provider, nativeType, ...bounded });
314
+ return;
315
+ }
316
+ map();
79
317
  },
80
318
  signal: abortController.signal,
81
319
  });
82
- if (overflowed) {
83
- finish(() => rejectCompletion(overflowFailure()));
84
- return;
85
- }
86
320
  finish(() => resolveCompletion(outcome));
87
321
  }
88
322
  catch (error) {
89
- if (overflowed || isEventChannelOverflowError(error)) {
90
- finish(() => rejectCompletion(error instanceof SubagentFailure && error.diagnostics === "PROJECTION_STATE_ERROR"
91
- ? error
92
- : overflowFailure()));
93
- return;
94
- }
95
323
  finish(() => rejectCompletion(error));
96
324
  }
97
325
  })();
@@ -102,9 +330,12 @@ export function createProjectionDriver(input) {
102
330
  });
103
331
  return {
104
332
  id,
105
- events: eventStream.events,
333
+ events: channel.iterable,
106
334
  completion,
107
335
  steerMode: input.steerMode,
336
+ mintEvent(body) {
337
+ return admit(body);
338
+ },
108
339
  ...(enqueue ? { enqueue } : {}),
109
340
  ...(input.clearQueue ? { clearQueue: input.clearQueue } : {}),
110
341
  async abort() {