@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
@@ -0,0 +1,103 @@
1
+ import { getConfig } from "../config/env.js";
2
+ function maxCaptureCharsForProvider(provider) {
3
+ const config = getConfig();
4
+ switch (provider) {
5
+ case "codex-sdk":
6
+ case "codex-app-server":
7
+ case "pi":
8
+ return config.codex.execMaxCaptureChars;
9
+ case "claude-agent-sdk":
10
+ return config.claude.execMaxCaptureChars;
11
+ case "opencode-sdk":
12
+ return config.opencode.execMaxCaptureChars;
13
+ }
14
+ }
15
+ export function buildCallTermsV2(snapshot, prompt, cwd, options) {
16
+ const runtimeConfig = getConfig();
17
+ const baseTerms = {
18
+ workdir: cwd,
19
+ prompt,
20
+ idleTimeoutMs: options.idleTimeoutMs ?? runtimeConfig.subagent.execIdleTimeoutMs,
21
+ maxCaptureChars: maxCaptureCharsForProvider(snapshot.provider.kind),
22
+ signal: options.signal,
23
+ projectionDir: options.projectionDir,
24
+ projectionExecutionId: options.projectionExecutionId,
25
+ projectionTellFence: options.projectionTellFence,
26
+ ...(options.initialTurn !== undefined ? { initialTurn: options.initialTurn } : {}),
27
+ };
28
+ const body = snapshot.akuma.body;
29
+ const replayPrompt = body
30
+ ? `[Akuma system prompt]\n${body}\n\n[Operator prompt]\n${prompt}`
31
+ : prompt;
32
+ switch (snapshot.provider.kind) {
33
+ case "codex-sdk":
34
+ return {
35
+ ...baseTerms,
36
+ prompt: replayPrompt,
37
+ provider: "codex-sdk",
38
+ ...(snapshot.akuma.model !== undefined ? { model: snapshot.akuma.model } : {}),
39
+ ...(snapshot.provider.profile !== undefined ? { profile: snapshot.provider.profile } : {}),
40
+ ...(snapshot.provider.config !== undefined
41
+ ? { config: snapshot.provider.config }
42
+ : {}),
43
+ threadOptions: snapshot.provider.threadOptions,
44
+ ...(snapshot.provider.executable !== undefined ? { executable: snapshot.provider.executable } : {}),
45
+ };
46
+ case "codex-app-server":
47
+ return {
48
+ ...baseTerms,
49
+ provider: "codex-app-server",
50
+ ...(snapshot.provider.model !== undefined ? { model: snapshot.provider.model } : {}),
51
+ ...(snapshot.provider.effort !== undefined ? { effort: snapshot.provider.effort } : {}),
52
+ ...(snapshot.provider.profile !== undefined ? { profile: snapshot.provider.profile } : {}),
53
+ ...(snapshot.provider.config !== undefined
54
+ ? { config: snapshot.provider.config }
55
+ : {}),
56
+ ...(snapshot.provider.executable !== undefined ? { executable: snapshot.provider.executable } : {}),
57
+ sandboxPolicy: snapshot.provider.sandboxPolicy,
58
+ approvalPolicy: snapshot.provider.approvalPolicy,
59
+ developerInstructions: body || undefined,
60
+ onCheckpoint() { },
61
+ };
62
+ case "claude-agent-sdk":
63
+ return {
64
+ ...baseTerms,
65
+ provider: "claude-agent-sdk",
66
+ ...(snapshot.provider.model !== undefined ? { model: snapshot.provider.model } : {}),
67
+ ...(snapshot.provider.effort !== undefined ? { effort: snapshot.provider.effort } : {}),
68
+ ...(snapshot.provider.executable !== undefined ? { executable: snapshot.provider.executable } : {}),
69
+ ...(snapshot.provider.settingSources !== undefined ? { settingSources: snapshot.provider.settingSources } : {}),
70
+ permissionMode: snapshot.provider.permissionMode,
71
+ ...(snapshot.provider.allowDangerouslySkipPermissions !== undefined
72
+ ? { allowDangerouslySkipPermissions: snapshot.provider.allowDangerouslySkipPermissions }
73
+ : {}),
74
+ ...(snapshot.provider.disallowedTools !== undefined
75
+ ? { disallowedTools: snapshot.provider.disallowedTools }
76
+ : {}),
77
+ systemPrompt: body
78
+ ? { type: "preset", preset: "claude_code", append: body }
79
+ : undefined,
80
+ onCheckpoint() { },
81
+ };
82
+ case "opencode-sdk":
83
+ return {
84
+ ...baseTerms,
85
+ prompt: replayPrompt,
86
+ provider: "opencode-sdk",
87
+ ...(snapshot.provider.model !== undefined ? { model: snapshot.provider.model } : {}),
88
+ ...(snapshot.provider.effort !== undefined ? { effort: snapshot.provider.effort } : {}),
89
+ ...(snapshot.provider.executable !== undefined ? { executable: snapshot.provider.executable } : {}),
90
+ };
91
+ case "pi":
92
+ return {
93
+ ...baseTerms,
94
+ provider: "pi",
95
+ ...(snapshot.provider.model !== undefined ? { model: snapshot.provider.model } : {}),
96
+ ...(snapshot.provider.thinkingLevel !== undefined
97
+ ? { thinkingLevel: snapshot.provider.thinkingLevel }
98
+ : {}),
99
+ appendSystemPromptOverride: body ? () => [body] : undefined,
100
+ onCheckpoint() { },
101
+ };
102
+ }
103
+ }
@@ -0,0 +1,54 @@
1
+ import { AGENT_EVENT_TEXT_MAX_CHARS } from "./events.js";
2
+ function toolKey(tool) {
3
+ return tool.toLowerCase().replace(/[-_\s]/g, "");
4
+ }
5
+ /** Classify already-extracted provider facts. This function never reads a provider payload. */
6
+ export function activityCallFromNamedTool(tool, facts) {
7
+ switch (toolKey(tool)) {
8
+ case "bash":
9
+ case "shell":
10
+ case "commandexecution":
11
+ return facts.command
12
+ ? { kind: "run", command: facts.command, ...(facts.description ? { description: facts.description } : {}) }
13
+ : { kind: "other", display: tool };
14
+ case "read":
15
+ return facts.path
16
+ ? {
17
+ kind: "read",
18
+ path: facts.path,
19
+ ...(facts.offset === undefined && facts.limit === undefined
20
+ ? {}
21
+ : { range: { ...(facts.offset === undefined ? {} : { offset: facts.offset }), ...(facts.limit === undefined ? {} : { limit: facts.limit }) } }),
22
+ }
23
+ : { kind: "other", display: tool };
24
+ case "write":
25
+ return facts.path
26
+ ? { kind: "fileChange", changes: [{ op: "add", path: facts.path }] }
27
+ : { kind: "other", display: tool };
28
+ case "edit":
29
+ return facts.path
30
+ ? { kind: "fileChange", changes: [{ op: "update", path: facts.path }] }
31
+ : { kind: "other", display: tool };
32
+ case "websearch":
33
+ case "search":
34
+ return facts.query
35
+ ? { kind: "search", query: facts.query }
36
+ : { kind: "other", display: tool };
37
+ default:
38
+ return { kind: "other", display: tool };
39
+ }
40
+ }
41
+ export function boundActivityExcerpt(value) {
42
+ return value.slice(-AGENT_EVENT_TEXT_MAX_CHARS);
43
+ }
44
+ export function diffstatFromUnifiedPatch(patch) {
45
+ let additions = 0;
46
+ let deletions = 0;
47
+ for (const line of patch.split("\n")) {
48
+ if (line.startsWith("+") && !line.startsWith("+++"))
49
+ additions += 1;
50
+ if (line.startsWith("-") && !line.startsWith("---"))
51
+ deletions += 1;
52
+ }
53
+ return additions || deletions ? { additions, deletions } : undefined;
54
+ }
@@ -15,7 +15,7 @@ export function isEventChannelOverflowError(error) {
15
15
  /**
16
16
  * Bounded async event channel for adapter -> runtime.
17
17
  * Hard cap is exactly 10_000 (shared across lifecycle + coalesce slots).
18
- * Channel is blind to provider raw shapes; coalesce keys are explicit opaque strings.
18
+ * Consumer-lag overflow saturates observation without failing the producer or consumer.
19
19
  */
20
20
  export function createEventChannel(options = {}) {
21
21
  const queue = [];
@@ -24,6 +24,9 @@ export function createEventChannel(options = {}) {
24
24
  const waiters = [];
25
25
  let closed = false;
26
26
  let failure;
27
+ let overflowed = false;
28
+ let overflowMarker;
29
+ let overflowMarkerPending = false;
27
30
  let drained = false;
28
31
  let resolveDrained;
29
32
  const drainedPromise = new Promise((resolve) => {
@@ -55,8 +58,6 @@ export function createEventChannel(options = {}) {
55
58
  while (waiters.length > 0) {
56
59
  const waiter = waiters.shift();
57
60
  if (failure !== undefined) {
58
- // Rejection path marks drained in the waiter reject wrapper below when next() parks;
59
- // for direct delivery, mark here after reject so whenDrained observes consumer receipt.
60
61
  markDrained();
61
62
  waiter.reject(failure);
62
63
  }
@@ -67,13 +68,40 @@ export function createEventChannel(options = {}) {
67
68
  }
68
69
  };
69
70
  const depth = () => queue.length;
70
- const markOverflow = () => {
71
- const error = createEventChannelOverflowError();
72
- failure = error;
73
- closed = true;
74
- options.onOverflow?.(error);
75
- if (queue.length === 0) {
76
- deliverTerminalToWaiters();
71
+ const storeOverflowMarker = (dropped) => {
72
+ if (overflowMarkerPending)
73
+ return;
74
+ const marker = options.createOverflowMarker ? options.createOverflowMarker(dropped) : dropped;
75
+ overflowMarker = marker;
76
+ overflowMarkerPending = true;
77
+ };
78
+ const takeOverflowMarker = () => {
79
+ if (!overflowMarkerPending || overflowMarker === undefined)
80
+ return undefined;
81
+ const marker = overflowMarker;
82
+ overflowMarker = undefined;
83
+ overflowMarkerPending = false;
84
+ return marker;
85
+ };
86
+ const markOverflow = (dropped) => {
87
+ overflowed = true;
88
+ storeOverflowMarker(dropped);
89
+ const waiter = waiters.shift();
90
+ if (waiter && queue.length === 0) {
91
+ const marker = takeOverflowMarker();
92
+ if (marker !== undefined) {
93
+ waiter.resolve({ value: marker, done: false });
94
+ }
95
+ else if (closed) {
96
+ markDrained();
97
+ if (failure !== undefined)
98
+ waiter.reject(failure);
99
+ else
100
+ waiter.resolve({ value: undefined, done: true });
101
+ }
102
+ else {
103
+ waiters.unshift(waiter);
104
+ }
77
105
  }
78
106
  return "overflow";
79
107
  };
@@ -81,6 +109,8 @@ export function createEventChannel(options = {}) {
81
109
  push(value, meta) {
82
110
  if (closed)
83
111
  return "closed";
112
+ if (overflowed)
113
+ return "overflow";
84
114
  const coalesceKey = meta?.coalesceKey;
85
115
  if (coalesceKey !== undefined) {
86
116
  const existing = coalesceIndex.get(coalesceKey);
@@ -90,11 +120,10 @@ export function createEventChannel(options = {}) {
90
120
  return "accepted";
91
121
  }
92
122
  if (depth() >= EXEC_EVENT_BUFFER_HARD_CAP) {
93
- return markOverflow();
123
+ return markOverflow(value);
94
124
  }
95
125
  const waiter = waiters.shift();
96
126
  if (waiter) {
97
- // Direct delivery to a waiting consumer: nothing remains queued for this key.
98
127
  waiter.resolve({ value, done: false });
99
128
  return "accepted";
100
129
  }
@@ -102,9 +131,9 @@ export function createEventChannel(options = {}) {
102
131
  queue.push({ value, coalesceKey });
103
132
  return "accepted";
104
133
  }
105
- // Lifecycle: never coalesce or drop.
134
+ // Lifecycle: never coalesce.
106
135
  if (depth() >= EXEC_EVENT_BUFFER_HARD_CAP) {
107
- return markOverflow();
136
+ return markOverflow(value);
108
137
  }
109
138
  const waiter = waiters.shift();
110
139
  if (waiter) {
@@ -118,8 +147,7 @@ export function createEventChannel(options = {}) {
118
147
  if (closed)
119
148
  return;
120
149
  closed = true;
121
- // Empty queue + no waiter: leave whenDrained pending until consumer pulls done.
122
- if (queue.length === 0) {
150
+ if (queue.length === 0 && !overflowMarkerPending) {
123
151
  deliverTerminalToWaiters();
124
152
  }
125
153
  },
@@ -128,8 +156,7 @@ export function createEventChannel(options = {}) {
128
156
  return;
129
157
  closed = true;
130
158
  failure = error;
131
- // Empty queue + no waiter: leave whenDrained pending until consumer pulls rejection.
132
- if (queue.length === 0) {
159
+ if (queue.length === 0 && !overflowMarkerPending) {
133
160
  deliverTerminalToWaiters();
134
161
  }
135
162
  },
@@ -137,6 +164,12 @@ export function createEventChannel(options = {}) {
137
164
  return drainedPromise;
138
165
  },
139
166
  depth,
167
+ peekCoalesced(coalesceKey) {
168
+ const index = coalesceIndex.get(coalesceKey);
169
+ if (index === undefined)
170
+ return undefined;
171
+ return queue[index]?.value;
172
+ },
140
173
  iterable: {
141
174
  [Symbol.asyncIterator]() {
142
175
  return {
@@ -144,10 +177,12 @@ export function createEventChannel(options = {}) {
144
177
  if (queue.length > 0) {
145
178
  const entry = queue.shift();
146
179
  reindexCoalesceKeys();
147
- // After the last item leaves under a closed/failed producer, the next next()
148
- // (or a parked waiter) must still receive the terminal signal.
149
180
  return Promise.resolve({ value: entry.value, done: false });
150
181
  }
182
+ const marker = takeOverflowMarker();
183
+ if (marker !== undefined) {
184
+ return Promise.resolve({ value: marker, done: false });
185
+ }
151
186
  if (failure !== undefined) {
152
187
  markDrained();
153
188
  return Promise.reject(failure);
@@ -1 +1,47 @@
1
- export {};
1
+ import { z } from "zod";
2
+ export const AGENT_EVENT_TEXT_MAX_CHARS = 4_096;
3
+ export const AGENT_THOUGHT_TEXT_MAX_CHARS = 4_000;
4
+ export const AGENT_PLAN_MAX_ITEMS = 64;
5
+ export const AGENT_PLAN_ITEM_TEXT_MAX_CHARS = 200;
6
+ export const AGENT_ACTIVITY_MAX_CHANGES = 32;
7
+ export const AGENT_UNKNOWN_PAYLOAD_MAX_CHARS = 4_096;
8
+ export const activityCallSchema = z.discriminatedUnion("kind", [
9
+ z.object({ kind: z.literal("run"), command: z.string(), description: z.string().optional() }).strict(),
10
+ z.object({
11
+ kind: z.literal("read"),
12
+ path: z.string(),
13
+ range: z.object({ offset: z.number().finite().optional(), limit: z.number().finite().optional() }).strict().optional(),
14
+ }).strict(),
15
+ z.object({ kind: z.literal("search"), query: z.string() }).strict(),
16
+ z.object({
17
+ kind: z.literal("fileChange"),
18
+ changes: z.array(z.object({ op: z.enum(["add", "update", "delete"]), path: z.string() }).strict()),
19
+ omittedChanges: z.number().int().nonnegative().optional(),
20
+ }).strict(),
21
+ z.object({ kind: z.literal("other"), display: z.string() }).strict(),
22
+ ]);
23
+ export const activityResultSchema = z.object({
24
+ status: z.enum(["ok", "error"]),
25
+ message: z.string().optional(),
26
+ exitCode: z.number().finite().optional(),
27
+ diffstat: z.object({
28
+ additions: z.number().int().nonnegative(),
29
+ deletions: z.number().int().nonnegative(),
30
+ }).strict().optional(),
31
+ }).strict();
32
+ export const activityEventBodySchema = z.object({
33
+ type: z.literal("activity"),
34
+ phase: z.enum(["started", "updated", "completed"]),
35
+ id: z.string(),
36
+ tool: z.string(),
37
+ call: activityCallSchema,
38
+ result: activityResultSchema.optional(),
39
+ truncated: z.literal(true).optional(),
40
+ }).strict().superRefine((value, context) => {
41
+ if (value.phase === "started" && value.result !== undefined) {
42
+ context.addIssue({ code: "custom", path: ["result"], message: "started activity cannot carry a result" });
43
+ }
44
+ if (value.phase === "completed" && value.result === undefined) {
45
+ context.addIssue({ code: "custom", path: ["result"], message: "completed activity requires a result" });
46
+ }
47
+ });