@astrosheep/keiyaku 2.8.4 → 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.
- package/README.md +35 -15
- package/build/.tsbuildinfo +1 -1
- package/build/agents/{call-terms_v2.js → call-terms.js} +12 -8
- package/build/agents/harness/control-types.js +1 -0
- package/build/agents/harness/execution-handle.js +23 -3
- package/build/agents/harness/index.js +6 -607
- package/build/agents/harness/outcome.js +141 -0
- package/build/agents/harness/projection.js +310 -0
- package/build/agents/harness/router.js +201 -0
- package/build/agents/harness/runtime.js +2 -1
- package/build/agents/{launch-snapshot_v2.js → launch-snapshot/model.js} +69 -74
- package/build/agents/launch-snapshot/resolve.js +72 -0
- package/build/agents/opencode-sdk.js +24 -3
- package/build/agents/providers/{claude-agent-sdk.js → claude-agent-sdk/adapter.js} +46 -368
- package/build/agents/providers/claude-agent-sdk/events.js +218 -0
- package/build/agents/providers/claude-agent-sdk/session.js +158 -0
- package/build/agents/providers/codex-app-server/adapter.js +347 -0
- package/build/agents/providers/codex-app-server/events.js +352 -0
- package/build/agents/providers/codex-app-server/session.js +266 -0
- package/build/agents/providers/codex-sdk.js +23 -5
- package/build/agents/providers/opencode-sdk/adapter.js +344 -0
- package/build/agents/providers/opencode-sdk/events.js +354 -0
- package/build/agents/providers/opencode-sdk/session.js +180 -0
- package/build/agents/providers/pi/adapter.js +269 -0
- package/build/agents/providers/pi/checkpoint.js +41 -0
- package/build/agents/providers/pi/events.js +282 -0
- package/build/agents/{selector_v2.js → selector.js} +8 -6
- package/build/cli/commands/akuma/akuma/handler.js +4 -0
- package/build/cli/commands/akuma/akuma/meta.js +16 -0
- package/build/cli/commands/akuma/catalog.js +8 -0
- package/build/cli/commands/akuma/list/handler.js +15 -0
- package/build/cli/commands/akuma/list/meta-ls.js +9 -0
- package/build/cli/commands/akuma/view/handler.js +34 -0
- package/build/cli/commands/akuma/view/meta.js +8 -0
- package/build/cli/commands/akuma.js +1 -1
- package/build/cli/commands/contract/amend/handler.js +10 -0
- package/build/cli/commands/contract/amend/meta.js +12 -0
- package/build/cli/commands/contract/arc/handler.js +9 -0
- package/build/cli/commands/contract/arc/meta.js +26 -0
- package/build/cli/commands/contract/bind/handler.js +23 -0
- package/build/cli/commands/contract/bind/meta.js +30 -0
- package/build/cli/commands/contract/catalog.js +16 -0
- package/build/cli/commands/contract/forfeit/handler.js +13 -0
- package/build/cli/commands/contract/forfeit/meta.js +12 -0
- package/build/cli/commands/contract/log/handler.js +12 -0
- package/build/cli/commands/contract/log/meta.js +12 -0
- package/build/cli/commands/contract/petition/handler.js +26 -0
- package/build/cli/commands/contract/petition/meta.js +23 -0
- package/build/cli/commands/contract/renew/handler.js +10 -0
- package/build/cli/commands/contract/renew/meta.js +17 -0
- package/build/cli/commands/projection/call/handler.js +39 -0
- package/build/cli/commands/projection/call/meta.js +9 -0
- package/build/cli/commands/projection/catalog.js +14 -0
- package/build/cli/commands/projection/kill/handler.js +35 -0
- package/build/cli/commands/projection/kill/meta.js +11 -0
- package/build/cli/commands/projection/revive/handler.js +53 -0
- package/build/cli/commands/projection/revive/meta.js +9 -0
- package/build/cli/commands/projection/status/handler.js +23 -0
- package/build/cli/commands/projection/status/meta.js +9 -0
- package/build/cli/commands/projection/tell/handler.js +34 -0
- package/build/cli/commands/projection/tell/meta.js +9 -0
- package/build/cli/commands/projection/wait/handler.js +21 -0
- package/build/cli/commands/projection/wait/meta.js +11 -0
- package/build/cli/commands/registry.js +100 -0
- package/build/cli/commands/shared.js +71 -0
- package/build/cli/commands/skills/catalog.js +6 -0
- package/build/cli/commands/skills/install/handler.js +10 -0
- package/build/cli/commands/skills/install/meta.js +9 -0
- package/build/cli/commands/skills/skills/handler.js +4 -0
- package/build/cli/commands/skills/skills/meta.js +10 -0
- package/build/cli/commands/system/catalog.js +8 -0
- package/build/cli/commands/system/completion/handler.js +10 -0
- package/build/cli/commands/system/completion/meta.js +9 -0
- package/build/cli/commands/system/dump-env/handler.js +3 -0
- package/build/cli/commands/system/dump-env/meta.js +10 -0
- package/build/cli/commands/system/guide/handler.js +4 -0
- package/build/cli/commands/system/guide/meta.js +9 -0
- package/build/cli/commands/task/add/handler.js +21 -0
- package/build/cli/commands/task/add/meta.js +8 -0
- package/build/cli/commands/task/catalog.js +22 -0
- package/build/cli/commands/task/doctor/handler.js +4 -0
- package/build/cli/commands/task/doctor/meta.js +8 -0
- package/build/cli/commands/task/done/handler.js +17 -0
- package/build/cli/commands/task/done/meta.js +8 -0
- package/build/cli/commands/task/drop/handler.js +6 -0
- package/build/cli/commands/task/drop/meta.js +8 -0
- package/build/cli/commands/task/ls/handler.js +13 -0
- package/build/cli/commands/task/ls/meta.js +8 -0
- package/build/cli/commands/task/shared.js +131 -0
- package/build/cli/commands/task/start/handler.js +6 -0
- package/build/cli/commands/task/start/meta.js +8 -0
- package/build/cli/commands/task/stop/handler.js +17 -0
- package/build/cli/commands/task/stop/meta.js +8 -0
- package/build/cli/commands/task/task/handler.js +5 -0
- package/build/cli/commands/task/task/meta.js +11 -0
- package/build/cli/commands/task/update/handler.js +18 -0
- package/build/cli/commands/task/update/meta.js +8 -0
- package/build/cli/commands/task/view/handler.js +14 -0
- package/build/cli/commands/task/view/meta.js +8 -0
- package/build/cli/completion.js +26 -27
- package/build/cli/flags.js +79 -2
- package/build/cli/help.js +27 -49
- package/build/cli/index.js +121 -374
- package/build/cli/output-stream-error-policy.js +17 -0
- package/build/cli/parse.js +159 -72
- package/build/cli/projection-address.js +5 -0
- package/build/cli/render/arc.js +1 -1
- package/build/cli/render/errors.js +4 -3
- package/build/cli/render/format.js +14 -0
- package/build/cli/render/kanshi.js +106 -0
- package/build/cli/render/petition.js +24 -1
- package/build/cli/render/projection-activity.js +5 -1
- package/build/cli/render/shared.js +2 -2
- package/build/cli/render/status.js +79 -13
- package/build/cli/render/task.js +113 -0
- package/build/cli/render/wait.js +23 -3
- package/build/cli/types.js +1 -1
- package/build/config/{akuma-loader_v2.js → akuma-loader.js} +2 -2
- package/build/config/env.js +9 -4
- package/build/config/settings/disease.js +26 -0
- package/build/config/settings/knobs.js +8 -0
- package/build/config/settings/loader.js +244 -0
- package/build/config/settings/schema.js +276 -0
- package/build/core/addressing.js +1 -1
- package/build/core/amend.js +3 -3
- package/build/core/arc.js +5 -4
- package/build/core/bind-workspace.js +123 -0
- package/build/core/bind.js +75 -155
- package/build/core/call/call.js +224 -0
- package/build/core/call/context.js +238 -0
- package/build/core/call/execution.js +217 -0
- package/build/core/call/prompt.js +42 -0
- package/build/core/call/reference.js +24 -0
- package/build/core/call-persist.js +1 -1
- package/build/core/claim-delivery.js +247 -0
- package/build/core/claim.js +39 -277
- package/build/core/context.js +2 -1
- package/build/core/{render.js → contract-view.js} +12 -140
- package/build/core/draft.js +107 -9
- package/build/core/entry.js +9 -1
- package/build/core/execution-coordinate.js +4 -20
- package/build/core/execution-pact.js +16 -31
- package/build/core/forfeit.js +35 -22
- package/build/core/hints.js +2 -2
- package/build/core/log.js +2 -2
- package/build/core/petition-claim-gates.js +8 -6
- package/build/core/petition-preview.js +97 -0
- package/build/core/petition-run.js +3 -2
- package/build/core/petition.js +39 -26
- package/build/core/projection/generation/database.js +306 -0
- package/build/core/projection/generation/identity.js +84 -0
- package/build/core/projection/generation/model.js +201 -0
- package/build/core/projection/generation/store.js +73 -0
- package/build/core/projection/generation/transitions.js +258 -0
- package/build/core/projection/heart.js +0 -307
- package/build/core/projection/tell/database.js +109 -0
- package/build/core/projection/tell/model.js +97 -0
- package/build/core/{projection-tells.js → projection/tell/store.js} +3 -158
- package/build/core/projection-activity.js +23 -2
- package/build/core/projection-core.js +2 -1
- package/build/core/projection-execution-observer.js +259 -0
- package/build/core/projection-generation-continuation.js +1 -1
- package/build/core/projection-generation-execution.js +90 -20
- package/build/core/projection-generation-launcher.js +1 -1
- package/build/core/projection-generation-runner.js +198 -4
- package/build/core/projection-identity.js +1 -1
- package/build/core/projection-kill.js +50 -17
- package/build/core/projection-life-observer.js +1 -1
- package/build/core/projection-life-protocol.js +10 -0
- package/build/core/projection-mint.js +1 -1
- package/build/core/projection-status.js +45 -32
- package/build/core/projection-wait.js +12 -2
- package/build/core/projection-wake.js +5 -2
- package/build/core/{queue_v2.js → queue.js} +2 -1
- package/build/core/registry.js +1 -1
- package/build/core/renew.js +7 -7
- package/build/core/response-artifact-catalog-sqlite.js +112 -0
- package/build/core/response-artifact-catalog.js +7 -0
- package/build/core/seal.js +44 -16
- package/build/core/status/board.js +286 -0
- package/build/core/status/drift.js +99 -0
- package/build/core/status/lifecycle.js +84 -0
- package/build/core/status/reconciliation.js +131 -0
- package/build/core/stored-agent-event.js +28 -4
- package/build/core/target-ref.js +10 -0
- package/build/core/task/board.js +175 -0
- package/build/core/task/commands.js +211 -0
- package/build/core/task/document.js +189 -0
- package/build/core/task/model.js +1 -0
- package/build/core/task/settlement-git.js +207 -0
- package/build/core/task/settlement-policy.js +82 -0
- package/build/core/task/source-board.js +84 -0
- package/build/core/task/source-model.js +1 -0
- package/build/core/task/validation.js +44 -0
- package/build/core/task-contract.js +224 -0
- package/build/core/task-doctor.js +14 -0
- package/build/core/task-git-actor.js +38 -0
- package/build/core/task-git-runtime.js +34 -0
- package/build/core/task-git-store.js +375 -0
- package/build/core/task-repository.js +1 -0
- package/build/core/task-store-repository.js +86 -0
- package/build/core/task-store.js +1 -0
- package/build/core/task-worktree.js +51 -0
- package/build/core/task.js +4 -0
- package/build/core/transcripts.js +138 -56
- package/build/core/validation.js +17 -0
- package/build/core/verdict.js +2 -2
- package/build/core/worktree-path.js +24 -6
- package/build/flow-error.js +3 -2
- package/build/generated/version.js +1 -1
- package/build/git/commits.js +3 -0
- package/build/git/diff/parsers.js +0 -94
- package/build/index.js +2 -0
- package/build/keiyaku-constants.js +2 -0
- package/build/keiyaku.js +4 -3
- package/package.json +3 -2
- package/skills/keiyaku/SKILL.md +1 -1
- package/skills/keiyaku-akuma/SKILL.md +11 -6
- package/build/agents/harness/pump.js +0 -158
- package/build/agents/providers/codex-app-server.js +0 -864
- package/build/agents/providers/opencode-sdk.js +0 -363
- package/build/agents/providers/pi.js +0 -557
- package/build/config/settings.js +0 -533
- package/build/core/call.js +0 -686
- package/build/core/projection/generation.js +0 -412
- package/build/core/projection/identity.js +0 -81
- package/build/core/projection/mint.js +0 -174
- package/build/core/projection-generation-store.js +0 -707
- package/build/core/status.js +0 -539
package/build/config/settings.js
DELETED
|
@@ -1,533 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs/promises";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { isErrnoException } from "../errno.js";
|
|
5
|
-
import { FlowError } from "../flow-error.js";
|
|
6
|
-
import { CLAUDE_SETTING_SOURCES, SETTINGS_FILE, } from "../keiyaku.js";
|
|
7
|
-
import { getKeiyakuHome } from "./env.js";
|
|
8
|
-
const tomlValueSchema = z.lazy(() => z.union([
|
|
9
|
-
z.string(),
|
|
10
|
-
z.number(),
|
|
11
|
-
z.boolean(),
|
|
12
|
-
z.array(tomlValueSchema),
|
|
13
|
-
z.record(z.string(), tomlValueSchema),
|
|
14
|
-
]));
|
|
15
|
-
const structuredTomlValueSchema = z.record(z.string(), tomlValueSchema);
|
|
16
|
-
const executableSchema = z.string().superRefine((value, ctx) => {
|
|
17
|
-
if (value.length === 0 || /\s/.test(value) || value.includes("\"") || value.includes("'")) {
|
|
18
|
-
ctx.addIssue({
|
|
19
|
-
code: z.ZodIssueCode.custom,
|
|
20
|
-
message: "must be a single program name token with no spaces or quotes.",
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
const nonblankOptionalString = z
|
|
25
|
-
.string()
|
|
26
|
-
.refine((value) => value.trim().length > 0, { message: "must not be blank." })
|
|
27
|
-
.optional();
|
|
28
|
-
const providerCommonFields = {
|
|
29
|
-
description: nonblankOptionalString,
|
|
30
|
-
};
|
|
31
|
-
const CODEX_SDK_FORBIDDEN_THREAD_OPTIONS_KEYS = [
|
|
32
|
-
"model",
|
|
33
|
-
"modelReasoningEffort",
|
|
34
|
-
"sandboxMode",
|
|
35
|
-
"networkAccessEnabled",
|
|
36
|
-
"webSearchMode",
|
|
37
|
-
"approvalPolicy",
|
|
38
|
-
"workingDirectory",
|
|
39
|
-
"skipGitRepoCheck",
|
|
40
|
-
];
|
|
41
|
-
const FORBIDDEN_CONFIG_KEYS = [
|
|
42
|
-
"model",
|
|
43
|
-
"model_reasoning_effort",
|
|
44
|
-
];
|
|
45
|
-
function isConfigKeyForbidden(key) {
|
|
46
|
-
return FORBIDDEN_CONFIG_KEYS.includes(key);
|
|
47
|
-
}
|
|
48
|
-
const claudeSettingSourceSchema = z.enum(CLAUDE_SETTING_SOURCES);
|
|
49
|
-
const writableRootItemSchema = z.string().superRefine((value, ctx) => {
|
|
50
|
-
if (value.length === 0) {
|
|
51
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not be empty." });
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (value.startsWith("~")) {
|
|
55
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not begin with tilde." });
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
if (value.includes("\0")) {
|
|
59
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not contain NUL byte." });
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (/\$[A-Za-z_]/.test(value) || /\$\{/.test(value)) {
|
|
63
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not contain environment variable references." });
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
export const providerInstanceSchema = z.discriminatedUnion("kind", [
|
|
68
|
-
z
|
|
69
|
-
.object({
|
|
70
|
-
kind: z.literal("codex-sdk"),
|
|
71
|
-
...providerCommonFields,
|
|
72
|
-
executable: executableSchema.optional(),
|
|
73
|
-
profile: z.string().optional(),
|
|
74
|
-
config: structuredTomlValueSchema.optional(),
|
|
75
|
-
threadOptions: structuredTomlValueSchema.optional(),
|
|
76
|
-
})
|
|
77
|
-
.strict()
|
|
78
|
-
.superRefine((data, ctx) => {
|
|
79
|
-
if (data.threadOptions) {
|
|
80
|
-
for (const key of CODEX_SDK_FORBIDDEN_THREAD_OPTIONS_KEYS) {
|
|
81
|
-
if (key in data.threadOptions) {
|
|
82
|
-
ctx.addIssue({
|
|
83
|
-
code: z.ZodIssueCode.custom,
|
|
84
|
-
path: ["threadOptions", key],
|
|
85
|
-
message: `"${key}" is owned by the Akuma or Keiyaku execution, not this provider instance`,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (data.config) {
|
|
91
|
-
for (const key of Object.keys(data.config)) {
|
|
92
|
-
if (isConfigKeyForbidden(key)) {
|
|
93
|
-
ctx.addIssue({
|
|
94
|
-
code: z.ZodIssueCode.custom,
|
|
95
|
-
path: ["config", key],
|
|
96
|
-
message: `"${key}" is owned by the Akuma or Keiyaku execution, not this provider instance`,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}),
|
|
102
|
-
z
|
|
103
|
-
.object({
|
|
104
|
-
kind: z.literal("codex-app-server"),
|
|
105
|
-
...providerCommonFields,
|
|
106
|
-
executable: executableSchema.optional(),
|
|
107
|
-
profile: z.string().optional(),
|
|
108
|
-
config: structuredTomlValueSchema.optional(),
|
|
109
|
-
writableRoots: z.array(writableRootItemSchema).optional(),
|
|
110
|
-
})
|
|
111
|
-
.strict()
|
|
112
|
-
.superRefine((data, ctx) => {
|
|
113
|
-
if (data.config) {
|
|
114
|
-
for (const key of Object.keys(data.config)) {
|
|
115
|
-
if (isConfigKeyForbidden(key)) {
|
|
116
|
-
ctx.addIssue({
|
|
117
|
-
code: z.ZodIssueCode.custom,
|
|
118
|
-
path: ["config", key],
|
|
119
|
-
message: `"${key}" is owned by the Akuma or Keiyaku execution, not this provider instance`,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}),
|
|
125
|
-
z
|
|
126
|
-
.object({
|
|
127
|
-
kind: z.literal("claude-agent-sdk"),
|
|
128
|
-
...providerCommonFields,
|
|
129
|
-
executable: executableSchema.optional(),
|
|
130
|
-
settingSources: z.array(claudeSettingSourceSchema).optional(),
|
|
131
|
-
})
|
|
132
|
-
.strict(),
|
|
133
|
-
z
|
|
134
|
-
.object({
|
|
135
|
-
kind: z.literal("opencode-sdk"),
|
|
136
|
-
...providerCommonFields,
|
|
137
|
-
executable: executableSchema.optional(),
|
|
138
|
-
})
|
|
139
|
-
.strict(),
|
|
140
|
-
z
|
|
141
|
-
.object({
|
|
142
|
-
kind: z.literal("pi"),
|
|
143
|
-
...providerCommonFields,
|
|
144
|
-
})
|
|
145
|
-
.strict(),
|
|
146
|
-
]);
|
|
147
|
-
const PROVIDER_INSTANCE_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
148
|
-
export const providerInstanceNameSchema = z
|
|
149
|
-
.string()
|
|
150
|
-
.max(64, "must contain at most 64 characters.")
|
|
151
|
-
.regex(PROVIDER_INSTANCE_NAME_PATTERN, "must match ^[a-z0-9][a-z0-9-]*$.");
|
|
152
|
-
export const BUILTIN_PROVIDER_INSTANCE_CANDIDATES = {
|
|
153
|
-
"codex-sdk": { name: "codex-sdk", source: "builtin", instance: { kind: "codex-sdk", executable: "codex" } },
|
|
154
|
-
"codex-app-server": {
|
|
155
|
-
name: "codex-app-server",
|
|
156
|
-
source: "builtin",
|
|
157
|
-
instance: { kind: "codex-app-server", executable: "codex" },
|
|
158
|
-
},
|
|
159
|
-
"claude-agent-sdk": {
|
|
160
|
-
name: "claude-agent-sdk",
|
|
161
|
-
source: "builtin",
|
|
162
|
-
instance: { kind: "claude-agent-sdk", executable: "claude", settingSources: ["project"] },
|
|
163
|
-
},
|
|
164
|
-
"opencode-sdk": {
|
|
165
|
-
name: "opencode-sdk",
|
|
166
|
-
source: "builtin",
|
|
167
|
-
instance: { kind: "opencode-sdk", executable: "opencode" },
|
|
168
|
-
},
|
|
169
|
-
pi: { name: "pi", source: "builtin", instance: { kind: "pi" } },
|
|
170
|
-
};
|
|
171
|
-
const AGENT_NAME_PATTERN = /^[a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*$/;
|
|
172
|
-
export const agentNameSchema = z
|
|
173
|
-
.string()
|
|
174
|
-
.min(1, "must not be empty.")
|
|
175
|
-
.regex(AGENT_NAME_PATTERN, "must contain only alphanumeric characters, dots, and hyphens, and must not start or end with a separator.");
|
|
176
|
-
function optionalTrimmedString() {
|
|
177
|
-
return z.string().transform((value) => {
|
|
178
|
-
const trimmed = value.trim();
|
|
179
|
-
return trimmed.length > 0 ? trimmed : undefined;
|
|
180
|
-
}).optional();
|
|
181
|
-
}
|
|
182
|
-
function requiredTrimmedString(message) {
|
|
183
|
-
return z.string().transform((value, ctx) => {
|
|
184
|
-
const trimmed = value.trim();
|
|
185
|
-
if (trimmed.length === 0) {
|
|
186
|
-
ctx.addIssue({
|
|
187
|
-
code: z.ZodIssueCode.custom,
|
|
188
|
-
message,
|
|
189
|
-
});
|
|
190
|
-
return z.NEVER;
|
|
191
|
-
}
|
|
192
|
-
return trimmed;
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
const gateCommandSchema = z
|
|
196
|
-
.object({
|
|
197
|
-
label: requiredTrimmedString("must not be empty."),
|
|
198
|
-
command: requiredTrimmedString("must not be empty.").superRefine((value, ctx) => {
|
|
199
|
-
if (/\s/.test(value) || value.includes("\"") || value.includes("'")) {
|
|
200
|
-
ctx.addIssue({
|
|
201
|
-
code: z.ZodIssueCode.custom,
|
|
202
|
-
message: "must be a single program name token with no spaces or quotes.",
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
}),
|
|
206
|
-
args: z.array(z.string()).optional(),
|
|
207
|
-
})
|
|
208
|
-
.strict()
|
|
209
|
-
.transform((value) => ({
|
|
210
|
-
...value,
|
|
211
|
-
args: value.args ?? [],
|
|
212
|
-
}));
|
|
213
|
-
const gateSchema = z
|
|
214
|
-
.object({
|
|
215
|
-
commands: z.array(gateCommandSchema).optional(),
|
|
216
|
-
})
|
|
217
|
-
.strict();
|
|
218
|
-
const KNOWN_SETTINGS_KNOBS = ["default", "reviewer", "gate", "defaultBranch", "worktreeBootstrap"];
|
|
219
|
-
const KNOWN_SETTINGS_ROOT_KEYS = [...KNOWN_SETTINGS_KNOBS, "providers"];
|
|
220
|
-
const WORKTREE_BOOTSTRAP_DEFAULT_TIMEOUT_MS = 300_000;
|
|
221
|
-
const WORKTREE_BOOTSTRAP_MIN_TIMEOUT_MS = 1;
|
|
222
|
-
const WORKTREE_BOOTSTRAP_MAX_TIMEOUT_MS = 3_600_000;
|
|
223
|
-
export const worktreeBootstrapSchema = z
|
|
224
|
-
.object({
|
|
225
|
-
command: z
|
|
226
|
-
.array(z.string())
|
|
227
|
-
.min(1, "command must be a nonempty argv array.")
|
|
228
|
-
.superRefine((items, ctx) => {
|
|
229
|
-
items.forEach((item, index) => {
|
|
230
|
-
if (item.trim().length === 0) {
|
|
231
|
-
ctx.addIssue({
|
|
232
|
-
code: z.ZodIssueCode.custom,
|
|
233
|
-
path: [index],
|
|
234
|
-
message: "command items must be nonempty after trimming.",
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
}),
|
|
239
|
-
timeoutMs: z
|
|
240
|
-
.number()
|
|
241
|
-
.int("timeoutMs must be an integer.")
|
|
242
|
-
.min(WORKTREE_BOOTSTRAP_MIN_TIMEOUT_MS, `timeoutMs must be >= ${WORKTREE_BOOTSTRAP_MIN_TIMEOUT_MS}.`)
|
|
243
|
-
.max(WORKTREE_BOOTSTRAP_MAX_TIMEOUT_MS, `timeoutMs must be <= ${WORKTREE_BOOTSTRAP_MAX_TIMEOUT_MS}.`)
|
|
244
|
-
.optional(),
|
|
245
|
-
})
|
|
246
|
-
.strict()
|
|
247
|
-
.transform((value) => ({
|
|
248
|
-
command: value.command,
|
|
249
|
-
timeoutMs: value.timeoutMs ?? WORKTREE_BOOTSTRAP_DEFAULT_TIMEOUT_MS,
|
|
250
|
-
}));
|
|
251
|
-
const settingsSchema = z
|
|
252
|
-
.object({
|
|
253
|
-
default: agentNameSchema.optional(),
|
|
254
|
-
reviewer: agentNameSchema.optional(),
|
|
255
|
-
gate: gateSchema.optional(),
|
|
256
|
-
defaultBranch: optionalTrimmedString(),
|
|
257
|
-
worktreeBootstrap: worktreeBootstrapSchema.optional(),
|
|
258
|
-
})
|
|
259
|
-
.strict();
|
|
260
|
-
const knobSchemas = {
|
|
261
|
-
default: agentNameSchema,
|
|
262
|
-
reviewer: agentNameSchema,
|
|
263
|
-
gate: gateSchema,
|
|
264
|
-
defaultBranch: optionalTrimmedString(),
|
|
265
|
-
worktreeBootstrap: worktreeBootstrapSchema,
|
|
266
|
-
};
|
|
267
|
-
function isPlainObject(value) {
|
|
268
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
269
|
-
}
|
|
270
|
-
function deepMerge(base, override) {
|
|
271
|
-
if (!isPlainObject(base) || !isPlainObject(override)) {
|
|
272
|
-
return override;
|
|
273
|
-
}
|
|
274
|
-
const merged = { ...base };
|
|
275
|
-
for (const [key, value] of Object.entries(override)) {
|
|
276
|
-
merged[key] = key in merged ? deepMerge(merged[key], value) : value;
|
|
277
|
-
}
|
|
278
|
-
return merged;
|
|
279
|
-
}
|
|
280
|
-
function formatZodIssues(error) {
|
|
281
|
-
return error.issues
|
|
282
|
-
.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`)
|
|
283
|
-
.join("; ");
|
|
284
|
-
}
|
|
285
|
-
async function readSettingsSource(filePath) {
|
|
286
|
-
try {
|
|
287
|
-
const text = await fs.readFile(filePath, "utf-8");
|
|
288
|
-
try {
|
|
289
|
-
return { found: true, raw: JSON.parse(text) };
|
|
290
|
-
}
|
|
291
|
-
catch (error) {
|
|
292
|
-
const reason = error instanceof SyntaxError
|
|
293
|
-
? `invalid JSON: ${error.message}`
|
|
294
|
-
: error instanceof Error
|
|
295
|
-
? error.message
|
|
296
|
-
: String(error);
|
|
297
|
-
return { found: true, raw: undefined, parseError: reason };
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
catch (error) {
|
|
301
|
-
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
302
|
-
return { found: false };
|
|
303
|
-
}
|
|
304
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
305
|
-
return { found: true, raw: undefined, parseError: reason };
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
function builtinProviderInstanceCatalog() {
|
|
309
|
-
return { selected: { ...BUILTIN_PROVIDER_INSTANCE_CANDIDATES }, shadows: [] };
|
|
310
|
-
}
|
|
311
|
-
function formatProviderInstanceIssues(name, error) {
|
|
312
|
-
return error.issues
|
|
313
|
-
.map((issue) => {
|
|
314
|
-
const suffix = issue.path.length === 0 ? "" : `.${issue.path.join(".")}`;
|
|
315
|
-
return `providers.${name}${suffix}: ${issue.message}`;
|
|
316
|
-
})
|
|
317
|
-
.join("; ");
|
|
318
|
-
}
|
|
319
|
-
function collectProviderCandidates(source, rawProviders) {
|
|
320
|
-
if (!isPlainObject(rawProviders)) {
|
|
321
|
-
return {
|
|
322
|
-
candidates: [],
|
|
323
|
-
disease: { source: source === "project" ? "local" : "global", knob: "providers", reason: "providers: must be a JSON object" },
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
const candidates = [];
|
|
327
|
-
for (const [name, rawInstance] of Object.entries(rawProviders)) {
|
|
328
|
-
const nameResult = providerInstanceNameSchema.safeParse(name);
|
|
329
|
-
if (!nameResult.success) {
|
|
330
|
-
candidates.push({
|
|
331
|
-
name,
|
|
332
|
-
source,
|
|
333
|
-
reason: formatProviderInstanceIssues(name, nameResult.error),
|
|
334
|
-
});
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
const instanceResult = providerInstanceSchema.safeParse(rawInstance);
|
|
338
|
-
if (!instanceResult.success) {
|
|
339
|
-
candidates.push({
|
|
340
|
-
name,
|
|
341
|
-
source,
|
|
342
|
-
reason: formatProviderInstanceIssues(name, instanceResult.error),
|
|
343
|
-
});
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
const instance = instanceResult.data;
|
|
347
|
-
if (instance.kind === "codex-app-server" && instance.writableRoots !== undefined && source === "global") {
|
|
348
|
-
let relativeIndex;
|
|
349
|
-
for (let i = 0; i < instance.writableRoots.length; i++) {
|
|
350
|
-
if (!path.posix.isAbsolute(instance.writableRoots[i])) {
|
|
351
|
-
relativeIndex = i;
|
|
352
|
-
break;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
if (relativeIndex !== undefined) {
|
|
356
|
-
candidates.push({
|
|
357
|
-
name,
|
|
358
|
-
source,
|
|
359
|
-
reason: `providers.${name}.writableRoots.${relativeIndex}: writableRoots item must be an absolute path for global provider instances`,
|
|
360
|
-
});
|
|
361
|
-
continue;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
candidates.push({ name, source, instance: instanceResult.data });
|
|
365
|
-
}
|
|
366
|
-
return { candidates };
|
|
367
|
-
}
|
|
368
|
-
function mergeProviderCandidates(catalog, candidates) {
|
|
369
|
-
const selected = { ...catalog.selected };
|
|
370
|
-
const shadows = [...catalog.shadows];
|
|
371
|
-
for (const candidate of candidates) {
|
|
372
|
-
const prior = selected[candidate.name];
|
|
373
|
-
if (prior !== undefined) {
|
|
374
|
-
shadows.push({ name: candidate.name, source: prior.source, shadowedBy: candidate.source });
|
|
375
|
-
}
|
|
376
|
-
selected[candidate.name] = candidate;
|
|
377
|
-
}
|
|
378
|
-
return { selected, shadows };
|
|
379
|
-
}
|
|
380
|
-
function collectSourceDiseases(source, providerSource, read) {
|
|
381
|
-
if (!read.found) {
|
|
382
|
-
return { partial: {}, diseases: [], providerCandidates: [] };
|
|
383
|
-
}
|
|
384
|
-
if (read.parseError !== undefined) {
|
|
385
|
-
return {
|
|
386
|
-
partial: {},
|
|
387
|
-
diseases: [{ source, reason: `settings root is unreadable (${read.parseError})` }],
|
|
388
|
-
providerCandidates: [],
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
if (!isPlainObject(read.raw)) {
|
|
392
|
-
return {
|
|
393
|
-
partial: {},
|
|
394
|
-
diseases: [{ source, reason: "settings root must be a JSON object" }],
|
|
395
|
-
providerCandidates: [],
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
const diseases = [];
|
|
399
|
-
const partial = {};
|
|
400
|
-
let providerCandidates = [];
|
|
401
|
-
const raw = read.raw;
|
|
402
|
-
for (const key of Object.keys(raw)) {
|
|
403
|
-
if (!KNOWN_SETTINGS_ROOT_KEYS.includes(key)) {
|
|
404
|
-
diseases.push({
|
|
405
|
-
source,
|
|
406
|
-
knob: key,
|
|
407
|
-
reason: key === "agents"
|
|
408
|
-
? 'settings key "agents" has been removed; define profiles in .keiyaku/akuma/<name>.md'
|
|
409
|
-
: `unknown settings key "${key}"`,
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
if ("providers" in raw) {
|
|
414
|
-
const collectedProviders = collectProviderCandidates(providerSource, raw.providers);
|
|
415
|
-
providerCandidates = collectedProviders.candidates;
|
|
416
|
-
if (collectedProviders.disease !== undefined)
|
|
417
|
-
diseases.push(collectedProviders.disease);
|
|
418
|
-
}
|
|
419
|
-
for (const knob of KNOWN_SETTINGS_KNOBS) {
|
|
420
|
-
if (!(knob in raw))
|
|
421
|
-
continue;
|
|
422
|
-
// worktreeBootstrap is project-layer only. Global/user presence is a named
|
|
423
|
-
// disease and never contributes execution authority.
|
|
424
|
-
if (knob === "worktreeBootstrap" && source === "global") {
|
|
425
|
-
diseases.push({
|
|
426
|
-
source,
|
|
427
|
-
knob,
|
|
428
|
-
reason: "worktreeBootstrap is project-only and never supplies a value from global settings",
|
|
429
|
-
});
|
|
430
|
-
continue;
|
|
431
|
-
}
|
|
432
|
-
const schema = knobSchemas[knob];
|
|
433
|
-
const result = schema.safeParse(raw[knob]);
|
|
434
|
-
if (!result.success) {
|
|
435
|
-
diseases.push({
|
|
436
|
-
source,
|
|
437
|
-
knob,
|
|
438
|
-
reason: formatZodIssues(result.error),
|
|
439
|
-
});
|
|
440
|
-
continue;
|
|
441
|
-
}
|
|
442
|
-
if (result.data === undefined)
|
|
443
|
-
continue;
|
|
444
|
-
partial[knob] = result.data;
|
|
445
|
-
}
|
|
446
|
-
return { partial, diseases, providerCandidates };
|
|
447
|
-
}
|
|
448
|
-
function mergeSettingsKnobs(base, override) {
|
|
449
|
-
// worktreeBootstrap is whole-value project authority; never field-merge it.
|
|
450
|
-
const { worktreeBootstrap: baseBootstrap, ...baseRest } = base;
|
|
451
|
-
const { worktreeBootstrap: overrideBootstrap, ...overrideRest } = override;
|
|
452
|
-
const mergedRaw = deepMerge(baseRest, overrideRest);
|
|
453
|
-
if (!isPlainObject(mergedRaw)) {
|
|
454
|
-
return overrideBootstrap !== undefined
|
|
455
|
-
? { ...override, worktreeBootstrap: overrideBootstrap }
|
|
456
|
-
: { ...override };
|
|
457
|
-
}
|
|
458
|
-
const merged = {};
|
|
459
|
-
for (const knob of KNOWN_SETTINGS_KNOBS) {
|
|
460
|
-
if (knob === "worktreeBootstrap")
|
|
461
|
-
continue;
|
|
462
|
-
if (knob in mergedRaw) {
|
|
463
|
-
merged[knob] = mergedRaw[knob];
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
const bootstrap = overrideBootstrap ?? baseBootstrap;
|
|
467
|
-
if (bootstrap !== undefined)
|
|
468
|
-
merged.worktreeBootstrap = bootstrap;
|
|
469
|
-
return merged;
|
|
470
|
-
}
|
|
471
|
-
export function findSettingsDiseasesForKnob(diseases, knob) {
|
|
472
|
-
return diseases.filter((disease) => disease.knob === knob);
|
|
473
|
-
}
|
|
474
|
-
export function formatSettingsDiseases(diseases) {
|
|
475
|
-
return diseases.map((disease) => {
|
|
476
|
-
const where = disease.knob ? `${disease.source}:${disease.knob}` : `${disease.source}:(root)`;
|
|
477
|
-
return `${where}: ${disease.reason}`;
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
/**
|
|
481
|
-
* Reject when a named knob is diseased and was not admitted into knobs.
|
|
482
|
-
* A healthy value from another source still wins; only failed-and-unset knobs reject.
|
|
483
|
-
*/
|
|
484
|
-
export function assertSettingsKnobUsable(loaded, knob) {
|
|
485
|
-
const knobs = loaded.knobs;
|
|
486
|
-
if (knobs !== null && knobs[knob] !== undefined)
|
|
487
|
-
return;
|
|
488
|
-
const hits = findSettingsDiseasesForKnob(loaded.diseases, knob);
|
|
489
|
-
if (hits.length === 0)
|
|
490
|
-
return;
|
|
491
|
-
throw new FlowError("INVALID_SETTINGS", `Invalid settings in ${SETTINGS_FILE} (diseased knob '${knob}').`, {
|
|
492
|
-
hints: formatSettingsDiseases(hits),
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
export async function loadKeiyakuSettings(cwd) {
|
|
496
|
-
const localSettingsPath = path.join(cwd, SETTINGS_FILE);
|
|
497
|
-
const globalSettingsPath = path.join(getKeiyakuHome(), path.basename(SETTINGS_FILE));
|
|
498
|
-
let globalRead;
|
|
499
|
-
let localRead;
|
|
500
|
-
try {
|
|
501
|
-
[globalRead, localRead] = await Promise.all([
|
|
502
|
-
readSettingsSource(globalSettingsPath),
|
|
503
|
-
readSettingsSource(localSettingsPath),
|
|
504
|
-
]);
|
|
505
|
-
}
|
|
506
|
-
catch (error) {
|
|
507
|
-
// loadKeiyakuSettings never throws; unexpected I/O becomes a local root disease.
|
|
508
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
509
|
-
return {
|
|
510
|
-
knobs: null,
|
|
511
|
-
diseases: [{ source: "local", reason: `settings root is unreadable (${reason})` }],
|
|
512
|
-
providerInstances: builtinProviderInstanceCatalog(),
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
const globalCollected = collectSourceDiseases("global", "global", globalRead);
|
|
516
|
-
const localCollected = collectSourceDiseases("local", "project", localRead);
|
|
517
|
-
const diseases = [...globalCollected.diseases, ...localCollected.diseases];
|
|
518
|
-
let providerInstances = builtinProviderInstanceCatalog();
|
|
519
|
-
providerInstances = mergeProviderCandidates(providerInstances, globalCollected.providerCandidates);
|
|
520
|
-
providerInstances = mergeProviderCandidates(providerInstances, localCollected.providerCandidates);
|
|
521
|
-
let knobs = null;
|
|
522
|
-
if (globalRead.found || localRead.found) {
|
|
523
|
-
knobs = mergeSettingsKnobs(globalCollected.partial, localCollected.partial);
|
|
524
|
-
}
|
|
525
|
-
// Empty object still means "settings files were present"; callers treat null as absent files.
|
|
526
|
-
return { knobs, diseases, providerInstances };
|
|
527
|
-
}
|
|
528
|
-
export function resolveDefaultAgentName(settings) {
|
|
529
|
-
return settings?.default ?? "akuma-codex";
|
|
530
|
-
}
|
|
531
|
-
export function resolveReviewerAgentName(settings) {
|
|
532
|
-
return settings?.reviewer ?? resolveDefaultAgentName(settings);
|
|
533
|
-
}
|