@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,222 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { z } from "zod";
4
+ import { parseToAST } from "../core/markdown/parser.js";
5
+ import { isGitRepo } from "../git/branches.js";
6
+ import { BUILTIN_AGENT_PROFILES } from "../keiyaku.js";
7
+ import { getKeiyakuHome } from "./env.js";
8
+ import { agentNameSchema, providerInstanceNameSchema } from "./settings.js";
9
+ const nonblankString = z.string().refine((value) => value.trim().length > 0, { message: "must not be blank" });
10
+ const effortOptionsSchema = z.array(nonblankString).superRefine((values, ctx) => {
11
+ if (new Set(values).size !== values.length) {
12
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not contain duplicate values" });
13
+ }
14
+ });
15
+ const profileSchema = z
16
+ .object({
17
+ provider: providerInstanceNameSchema,
18
+ description: nonblankString.optional(),
19
+ model: nonblankString.optional(),
20
+ effort: nonblankString.optional(),
21
+ effortOptions: effortOptionsSchema.optional(),
22
+ access: z.enum(["read", "write", "auto"]).optional(),
23
+ network: z.enum(["disabled", "enabled"]).optional(),
24
+ webSearch: z.enum(["disabled", "cached", "live"]).optional(),
25
+ })
26
+ .strict();
27
+ function formatIssues(error) {
28
+ return error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
29
+ }
30
+ function stripFrontMatterQuote(rawValue) {
31
+ const value = rawValue.trim();
32
+ if (value.length < 2)
33
+ return value;
34
+ const quote = value[0];
35
+ return (quote === "'" || quote === '"') && value.at(-1) === quote ? value.slice(1, -1) : value;
36
+ }
37
+ function splitFrontMatter(content) {
38
+ const ast = parseToAST(content, { allowSections: false });
39
+ if (ast.frontmatter === undefined) {
40
+ throw new Error("missing leading YAML frontmatter");
41
+ }
42
+ const lines = content.split(/\r?\n/);
43
+ let closingIndex = -1;
44
+ for (let index = 1; index < lines.length; index += 1) {
45
+ if ((lines[index] ?? "").trim() === "---") {
46
+ closingIndex = index;
47
+ break;
48
+ }
49
+ }
50
+ if (closingIndex === -1)
51
+ throw new Error("frontmatter is not closed");
52
+ const entries = [];
53
+ for (let index = 1; index < closingIndex; index += 1) {
54
+ const line = lines[index] ?? "";
55
+ const match = line.trim().match(/^([A-Za-z0-9_.-]+)\s*:\s*(.*)$/);
56
+ if (!match)
57
+ continue;
58
+ const key = match[1] ?? "";
59
+ const value = match[2] ?? "";
60
+ if (key === "effortOptions" && value.trim() === "") {
61
+ const options = [];
62
+ while (index + 1 < closingIndex) {
63
+ const item = lines[index + 1] ?? "";
64
+ if (item.trim().match(/^([A-Za-z0-9_.-]+)\s*:/))
65
+ break;
66
+ const itemMatch = item.match(/^\s*-\s*(.*)$/);
67
+ if (!itemMatch)
68
+ break;
69
+ options.push(stripFrontMatterQuote(itemMatch[1] ?? ""));
70
+ index += 1;
71
+ }
72
+ entries.push([key, options]);
73
+ continue;
74
+ }
75
+ entries.push([key, stripFrontMatterQuote(value)]);
76
+ }
77
+ return { entries, body: lines.slice(closingIndex + 1).join("\n") };
78
+ }
79
+ function freezeProfile(profile) {
80
+ if (profile.effortOptions === undefined)
81
+ return Object.freeze({ ...profile });
82
+ return Object.freeze({
83
+ ...profile,
84
+ effortOptions: Object.freeze([...profile.effortOptions]),
85
+ });
86
+ }
87
+ function freezeCandidate(candidate) {
88
+ if ("reason" in candidate && candidate.reason !== undefined) {
89
+ return Object.freeze({ ...candidate });
90
+ }
91
+ return Object.freeze({
92
+ ...candidate,
93
+ profile: freezeProfile(candidate.profile),
94
+ });
95
+ }
96
+ function decodeProfile(entries) {
97
+ const raw = {};
98
+ for (const [key, value] of entries) {
99
+ if (key === "effortOptions") {
100
+ raw[key] = value;
101
+ continue;
102
+ }
103
+ if (typeof value !== "string")
104
+ throw new Error(`${key} must be a scalar value`);
105
+ raw[key] = value;
106
+ }
107
+ const parsed = profileSchema.safeParse(raw);
108
+ if (!parsed.success)
109
+ throw new Error(formatIssues(parsed.error));
110
+ if (parsed.data.effort !== undefined && parsed.data.effortOptions !== undefined && !parsed.data.effortOptions.includes(parsed.data.effort)) {
111
+ throw new Error("effort must be an exact member of effortOptions");
112
+ }
113
+ return freezeProfile(parsed.data);
114
+ }
115
+ async function readLayer(directory, source) {
116
+ let entries;
117
+ try {
118
+ entries = await fs.readdir(directory, { withFileTypes: true });
119
+ }
120
+ catch (error) {
121
+ if (error.code === "ENOENT")
122
+ return { candidates: [] };
123
+ return {
124
+ candidates: [],
125
+ directoryFailure: {
126
+ path: directory,
127
+ reason: `unable to read akuma directory (${error instanceof Error ? error.message : String(error)})`,
128
+ },
129
+ };
130
+ }
131
+ const candidates = [];
132
+ for (const entry of entries.filter((candidate) => candidate.isFile() && candidate.name.endsWith(".md")).sort((a, b) => a.name.localeCompare(b.name))) {
133
+ const file = path.join(directory, entry.name);
134
+ const name = path.basename(entry.name, ".md");
135
+ const nameResult = agentNameSchema.safeParse(name);
136
+ if (!nameResult.success) {
137
+ candidates.push(freezeCandidate({
138
+ name,
139
+ source,
140
+ path: file,
141
+ reason: `invalid akuma name: ${formatIssues(nameResult.error)}`,
142
+ }));
143
+ continue;
144
+ }
145
+ try {
146
+ const content = await fs.readFile(file, "utf-8");
147
+ const { entries: frontmatter, body } = splitFrontMatter(content);
148
+ candidates.push(freezeCandidate({
149
+ name,
150
+ source,
151
+ path: file,
152
+ body,
153
+ profile: decodeProfile(frontmatter),
154
+ }));
155
+ }
156
+ catch (error) {
157
+ candidates.push(freezeCandidate({
158
+ name,
159
+ source,
160
+ path: file,
161
+ reason: error instanceof Error ? error.message : String(error),
162
+ }));
163
+ }
164
+ }
165
+ return { candidates };
166
+ }
167
+ function foldLayer(selected, shadows, allCandidates, source, layer) {
168
+ if (layer.directoryFailure !== undefined) {
169
+ allCandidates.push(freezeCandidate({
170
+ name: `${source}-akuma-directory`,
171
+ source,
172
+ path: layer.directoryFailure.path,
173
+ reason: layer.directoryFailure.reason,
174
+ }));
175
+ }
176
+ for (const candidate of layer.candidates) {
177
+ allCandidates.push(candidate);
178
+ const prior = selected[candidate.name];
179
+ if (prior !== undefined) {
180
+ shadows.push(Object.freeze({
181
+ name: prior.name,
182
+ source: prior.source,
183
+ path: prior.path,
184
+ shadowedBy: candidate.source,
185
+ }));
186
+ }
187
+ selected[candidate.name] = candidate;
188
+ }
189
+ }
190
+ export async function loadAkumaCatalogV2(cwd) {
191
+ const candidates = [];
192
+ const selected = {};
193
+ const shadows = [];
194
+ for (const [name, profile] of Object.entries(BUILTIN_AGENT_PROFILES)) {
195
+ const candidate = freezeCandidate({
196
+ name,
197
+ source: "builtin",
198
+ path: null,
199
+ body: "",
200
+ profile: freezeProfile({
201
+ provider: profile.provider,
202
+ ...(profile.description !== undefined ? { description: profile.description } : {}),
203
+ ...("model" in profile && profile.model !== undefined ? { model: profile.model } : {}),
204
+ ...("effort" in profile && profile.effort !== undefined ? { effort: profile.effort } : {}),
205
+ ...("effortOptions" in profile && profile.effortOptions !== undefined
206
+ ? { effortOptions: Object.freeze([...profile.effortOptions]) }
207
+ : {}),
208
+ }),
209
+ });
210
+ candidates.push(candidate);
211
+ selected[name] = candidate;
212
+ }
213
+ foldLayer(selected, shadows, candidates, "user", await readLayer(path.join(getKeiyakuHome(), "akuma"), "user"));
214
+ if (await isGitRepo(cwd)) {
215
+ foldLayer(selected, shadows, candidates, "project", await readLayer(path.join(cwd, ".keiyaku", "akuma"), "project"));
216
+ }
217
+ return Object.freeze({
218
+ candidates: Object.freeze(candidates),
219
+ selected: Object.freeze(selected),
220
+ shadows: Object.freeze(shadows),
221
+ });
222
+ }
@@ -9,9 +9,7 @@ export const DISABLE_SUBAGENT_SPAWN_ENV_KEY = "KEIYAKU_DISABLE_SUBAGENT_SPAWN";
9
9
  export const ENV_KEYS = {
10
10
  AGENT_OVERRIDE: "KEIYAKU_AGENT_OVERRIDE",
11
11
  DISABLE_SUBAGENT_SPAWN: DISABLE_SUBAGENT_SPAWN_ENV_KEY,
12
- SUBAGENT_EXEC_TIMEOUT_MS: "KEIYAKU_SUBAGENT_EXEC_TIMEOUT_MS",
13
12
  SUBAGENT_EXEC_IDLE_TIMEOUT_MS: "KEIYAKU_SUBAGENT_EXEC_IDLE_TIMEOUT_MS",
14
- SUBAGENT_PROGRESS_INTERVAL_MIN: "KEIYAKU_SUBAGENT_PROGRESS_INTERVAL_MIN",
15
13
  CODEX_EXEC_MAX_CAPTURE_CHARS: "KEIYAKU_CODEX_EXEC_MAX_CAPTURE_CHARS",
16
14
  CLAUDE_EXEC_MAX_CAPTURE_CHARS: "KEIYAKU_CLAUDE_EXEC_MAX_CAPTURE_CHARS",
17
15
  OPENCODE_EXEC_MAX_CAPTURE_CHARS: "KEIYAKU_OPENCODE_EXEC_MAX_CAPTURE_CHARS",
@@ -27,9 +25,7 @@ export const ENV_KEYS = {
27
25
  const ENV_KEY_ENTRIES = [
28
26
  { key: ENV_KEYS.AGENT_OVERRIDE, defaultValue: "", description: "Override default agent name", category: "subagent" },
29
27
  { key: ENV_KEYS.DISABLE_SUBAGENT_SPAWN, defaultValue: "0", description: "Disable subprocess subagent spawning when set to 1", category: "subagent" },
30
- { key: ENV_KEYS.SUBAGENT_EXEC_TIMEOUT_MS, defaultValue: "2700000", description: "Subagent execution timeout in milliseconds", category: "subagent" },
31
28
  { key: ENV_KEYS.SUBAGENT_EXEC_IDLE_TIMEOUT_MS, defaultValue: "600000", description: "Subagent idle timeout in milliseconds", category: "subagent" },
32
- { key: ENV_KEYS.SUBAGENT_PROGRESS_INTERVAL_MIN, defaultValue: "0", description: "Print subagent tool/activity summaries every N minutes when greater than 0", category: "subagent" },
33
29
  { key: ENV_KEYS.CODEX_EXEC_MAX_CAPTURE_CHARS, defaultValue: "200000", description: "Codex runtime output/diagnostics capture cap", category: "subagent" },
34
30
  { key: ENV_KEYS.CLAUDE_EXEC_MAX_CAPTURE_CHARS, defaultValue: "200000", description: "Claude runtime output/diagnostics capture cap", category: "subagent" },
35
31
  { key: ENV_KEYS.OPENCODE_EXEC_MAX_CAPTURE_CHARS, defaultValue: "200000", description: "OpenCode runtime output/diagnostics capture cap", category: "subagent" },
@@ -138,9 +138,7 @@ function booleanFlagEnv(key) {
138
138
  const subagentSchema = z.object({
139
139
  nameOverride: z.string().default(getEnvDefaultValue(ENV_KEYS.AGENT_OVERRIDE)).transform((value) => value.trim()),
140
140
  disableSpawn: booleanFlagEnv(ENV_KEYS.DISABLE_SUBAGENT_SPAWN),
141
- execTimeoutMs: positiveIntEnv(ENV_KEYS.SUBAGENT_EXEC_TIMEOUT_MS),
142
141
  execIdleTimeoutMs: positiveIntEnv(ENV_KEYS.SUBAGENT_EXEC_IDLE_TIMEOUT_MS),
143
- progressIntervalMin: nonNegativeIntEnv(ENV_KEYS.SUBAGENT_PROGRESS_INTERVAL_MIN),
144
142
  });
145
143
  const codexSchema = z.object({
146
144
  execMaxCaptureChars: positiveIntEnv(ENV_KEYS.CODEX_EXEC_MAX_CAPTURE_CHARS),
@@ -174,9 +172,7 @@ const envSchema = z
174
172
  .object({
175
173
  [ENV_KEYS.AGENT_OVERRIDE]: z.string().optional(),
176
174
  [ENV_KEYS.DISABLE_SUBAGENT_SPAWN]: z.string().optional(),
177
- [ENV_KEYS.SUBAGENT_EXEC_TIMEOUT_MS]: z.string().optional(),
178
175
  [ENV_KEYS.SUBAGENT_EXEC_IDLE_TIMEOUT_MS]: z.string().optional(),
179
- [ENV_KEYS.SUBAGENT_PROGRESS_INTERVAL_MIN]: z.string().optional(),
180
176
  [ENV_KEYS.CODEX_EXEC_MAX_CAPTURE_CHARS]: z.string().optional(),
181
177
  [ENV_KEYS.CLAUDE_EXEC_MAX_CAPTURE_CHARS]: z.string().optional(),
182
178
  [ENV_KEYS.OPENCODE_EXEC_MAX_CAPTURE_CHARS]: z.string().optional(),
@@ -196,9 +192,7 @@ const envSchema = z
196
192
  subagent: subagentSchema.parse({
197
193
  nameOverride: rawEnv[ENV_KEYS.AGENT_OVERRIDE],
198
194
  disableSpawn: rawEnv[ENV_KEYS.DISABLE_SUBAGENT_SPAWN],
199
- execTimeoutMs: rawEnv[ENV_KEYS.SUBAGENT_EXEC_TIMEOUT_MS],
200
195
  execIdleTimeoutMs: rawEnv[ENV_KEYS.SUBAGENT_EXEC_IDLE_TIMEOUT_MS],
201
- progressIntervalMin: rawEnv[ENV_KEYS.SUBAGENT_PROGRESS_INTERVAL_MIN],
202
196
  }),
203
197
  codex: codexSchema.parse({
204
198
  execMaxCaptureChars: rawEnv[ENV_KEYS.CODEX_EXEC_MAX_CAPTURE_CHARS],