@astrosheep/keiyaku 2.8.5 → 2.9.1
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 +42 -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 +37 -0
- package/build/cli/commands/projection/kill/meta.js +11 -0
- package/build/cli/commands/projection/revive/handler.js +56 -0
- package/build/cli/commands/projection/revive/meta.js +9 -0
- package/build/cli/commands/projection/status/handler.js +25 -0
- package/build/cli/commands/projection/status/meta.js +9 -0
- package/build/cli/commands/projection/tell/handler.js +36 -0
- package/build/cli/commands/projection/tell/meta.js +9 -0
- package/build/cli/commands/projection/wait/handler.js +23 -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-flags.js +197 -0
- package/build/cli/parse-metadata.js +129 -0
- package/build/cli/parse-selectors.js +65 -0
- package/build/cli/parse.js +46 -313
- 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} +7 -4
- 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 +5 -8
- package/build/core/arc.js +7 -8
- package/build/core/bind-workspace.js +123 -0
- package/build/core/bind.js +75 -155
- package/build/core/call/call.js +229 -0
- package/build/core/call/context.js +251 -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 +10 -3
- 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/path-coordinate.js +27 -0
- package/build/core/petition-claim-gates.js +8 -6
- package/build/core/petition-preview.js +97 -0
- package/build/core/petition-run.js +5 -7
- 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-coordinate.js +14 -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 +9 -11
- 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 +289 -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 +237 -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 +37 -0
- package/build/core/task-git-store.js +385 -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 +33 -8
- package/build/flow-error.js +5 -2
- package/build/generated/version.js +1 -1
- package/build/git/branches.js +33 -9
- package/build/git/commits.js +3 -0
- package/build/git/core.js +9 -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 +30 -13
- package/skills/keiyaku-akuma/SKILL.md +30 -57
- package/skills/keiyaku-task/SKILL.md +51 -0
- package/skills/keiyaku-workflow/SKILL.md +71 -0
- 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
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { isErrnoException } from "../../errno.js";
|
|
4
|
+
import { SETTINGS_FILE } from "../../keiyaku.js";
|
|
5
|
+
import { getKeiyakuHome } from "../env.js";
|
|
6
|
+
import { KNOWN_SETTINGS_KNOBS, KNOWN_SETTINGS_ROOT_KEYS } from "./knobs.js";
|
|
7
|
+
import { BUILTIN_PROVIDER_INSTANCE_CANDIDATES, knobSchemas, providerInstanceNameSchema, providerInstanceSchema, } from "./schema.js";
|
|
8
|
+
function isPlainObject(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function deepMerge(base, override) {
|
|
12
|
+
if (!isPlainObject(base) || !isPlainObject(override)) {
|
|
13
|
+
return override;
|
|
14
|
+
}
|
|
15
|
+
const merged = { ...base };
|
|
16
|
+
for (const [key, value] of Object.entries(override)) {
|
|
17
|
+
merged[key] = key in merged ? deepMerge(merged[key], value) : value;
|
|
18
|
+
}
|
|
19
|
+
return merged;
|
|
20
|
+
}
|
|
21
|
+
function formatZodIssues(error) {
|
|
22
|
+
return error.issues
|
|
23
|
+
.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`)
|
|
24
|
+
.join("; ");
|
|
25
|
+
}
|
|
26
|
+
async function readSettingsSource(filePath) {
|
|
27
|
+
try {
|
|
28
|
+
const text = await fs.readFile(filePath, "utf-8");
|
|
29
|
+
try {
|
|
30
|
+
return { found: true, raw: JSON.parse(text) };
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
const reason = error instanceof SyntaxError
|
|
34
|
+
? `invalid JSON: ${error.message}`
|
|
35
|
+
: error instanceof Error
|
|
36
|
+
? error.message
|
|
37
|
+
: String(error);
|
|
38
|
+
return { found: true, raw: undefined, parseError: reason };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (isErrnoException(error) && error.code === "ENOENT") {
|
|
43
|
+
return { found: false };
|
|
44
|
+
}
|
|
45
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
46
|
+
return { found: true, raw: undefined, parseError: reason };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function builtinProviderInstanceCatalog() {
|
|
50
|
+
return { selected: { ...BUILTIN_PROVIDER_INSTANCE_CANDIDATES }, shadows: [] };
|
|
51
|
+
}
|
|
52
|
+
function formatProviderInstanceIssues(name, error) {
|
|
53
|
+
return error.issues
|
|
54
|
+
.map((issue) => {
|
|
55
|
+
const suffix = issue.path.length === 0 ? "" : `.${issue.path.join(".")}`;
|
|
56
|
+
return `providers.${name}${suffix}: ${issue.message}`;
|
|
57
|
+
})
|
|
58
|
+
.join("; ");
|
|
59
|
+
}
|
|
60
|
+
function collectProviderCandidates(source, rawProviders) {
|
|
61
|
+
if (!isPlainObject(rawProviders)) {
|
|
62
|
+
return {
|
|
63
|
+
candidates: [],
|
|
64
|
+
disease: { source: source === "project" ? "local" : "global", knob: "providers", reason: "providers: must be a JSON object" },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const candidates = [];
|
|
68
|
+
for (const [name, rawInstance] of Object.entries(rawProviders)) {
|
|
69
|
+
const nameResult = providerInstanceNameSchema.safeParse(name);
|
|
70
|
+
if (!nameResult.success) {
|
|
71
|
+
candidates.push({
|
|
72
|
+
name,
|
|
73
|
+
source,
|
|
74
|
+
reason: formatProviderInstanceIssues(name, nameResult.error),
|
|
75
|
+
});
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const instanceResult = providerInstanceSchema.safeParse(rawInstance);
|
|
79
|
+
if (!instanceResult.success) {
|
|
80
|
+
candidates.push({
|
|
81
|
+
name,
|
|
82
|
+
source,
|
|
83
|
+
reason: formatProviderInstanceIssues(name, instanceResult.error),
|
|
84
|
+
});
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const instance = instanceResult.data;
|
|
88
|
+
if (instance.kind === "codex-app-server" && instance.writableRoots !== undefined && source === "global") {
|
|
89
|
+
let relativeIndex;
|
|
90
|
+
for (let i = 0; i < instance.writableRoots.length; i++) {
|
|
91
|
+
if (!path.posix.isAbsolute(instance.writableRoots[i])) {
|
|
92
|
+
relativeIndex = i;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (relativeIndex !== undefined) {
|
|
97
|
+
candidates.push({
|
|
98
|
+
name,
|
|
99
|
+
source,
|
|
100
|
+
reason: `providers.${name}.writableRoots.${relativeIndex}: writableRoots item must be an absolute path for global provider instances`,
|
|
101
|
+
});
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
candidates.push({ name, source, instance: instanceResult.data });
|
|
106
|
+
}
|
|
107
|
+
return { candidates };
|
|
108
|
+
}
|
|
109
|
+
function mergeProviderCandidates(catalog, candidates) {
|
|
110
|
+
const selected = { ...catalog.selected };
|
|
111
|
+
const shadows = [...catalog.shadows];
|
|
112
|
+
for (const candidate of candidates) {
|
|
113
|
+
const prior = selected[candidate.name];
|
|
114
|
+
if (prior !== undefined) {
|
|
115
|
+
shadows.push({ name: candidate.name, source: prior.source, shadowedBy: candidate.source });
|
|
116
|
+
}
|
|
117
|
+
selected[candidate.name] = candidate;
|
|
118
|
+
}
|
|
119
|
+
return { selected, shadows };
|
|
120
|
+
}
|
|
121
|
+
function collectSourceDiseases(source, providerSource, read) {
|
|
122
|
+
if (!read.found) {
|
|
123
|
+
return { partial: {}, diseases: [], providerCandidates: [] };
|
|
124
|
+
}
|
|
125
|
+
if (read.parseError !== undefined) {
|
|
126
|
+
return {
|
|
127
|
+
partial: {},
|
|
128
|
+
diseases: [{ source, reason: `settings root is unreadable (${read.parseError})` }],
|
|
129
|
+
providerCandidates: [],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (!isPlainObject(read.raw)) {
|
|
133
|
+
return {
|
|
134
|
+
partial: {},
|
|
135
|
+
diseases: [{ source, reason: "settings root must be a JSON object" }],
|
|
136
|
+
providerCandidates: [],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const diseases = [];
|
|
140
|
+
const partial = {};
|
|
141
|
+
let providerCandidates = [];
|
|
142
|
+
const raw = read.raw;
|
|
143
|
+
for (const key of Object.keys(raw)) {
|
|
144
|
+
if (!KNOWN_SETTINGS_ROOT_KEYS.includes(key)) {
|
|
145
|
+
diseases.push({
|
|
146
|
+
source,
|
|
147
|
+
knob: key,
|
|
148
|
+
reason: key === "agents"
|
|
149
|
+
? 'settings key "agents" has been removed; define profiles in .keiyaku/akuma/<name>.md'
|
|
150
|
+
: `unknown settings key "${key}"`,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if ("providers" in raw) {
|
|
155
|
+
const collectedProviders = collectProviderCandidates(providerSource, raw.providers);
|
|
156
|
+
providerCandidates = collectedProviders.candidates;
|
|
157
|
+
if (collectedProviders.disease !== undefined)
|
|
158
|
+
diseases.push(collectedProviders.disease);
|
|
159
|
+
}
|
|
160
|
+
for (const knob of KNOWN_SETTINGS_KNOBS) {
|
|
161
|
+
if (!(knob in raw))
|
|
162
|
+
continue;
|
|
163
|
+
// worktreeBootstrap is project-layer only. Global/user presence is a named
|
|
164
|
+
// disease and never contributes execution authority.
|
|
165
|
+
if (knob === "worktreeBootstrap" && source === "global") {
|
|
166
|
+
diseases.push({
|
|
167
|
+
source,
|
|
168
|
+
knob,
|
|
169
|
+
reason: "worktreeBootstrap is project-only and never supplies a value from global settings",
|
|
170
|
+
});
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const schema = knobSchemas[knob];
|
|
174
|
+
const result = schema.safeParse(raw[knob]);
|
|
175
|
+
if (!result.success) {
|
|
176
|
+
diseases.push({
|
|
177
|
+
source,
|
|
178
|
+
knob,
|
|
179
|
+
reason: formatZodIssues(result.error),
|
|
180
|
+
});
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (result.data === undefined)
|
|
184
|
+
continue;
|
|
185
|
+
partial[knob] = result.data;
|
|
186
|
+
}
|
|
187
|
+
return { partial, diseases, providerCandidates };
|
|
188
|
+
}
|
|
189
|
+
function mergeSettingsKnobs(base, override) {
|
|
190
|
+
// worktreeBootstrap is whole-value project authority; never field-merge it.
|
|
191
|
+
const { worktreeBootstrap: baseBootstrap, ...baseRest } = base;
|
|
192
|
+
const { worktreeBootstrap: overrideBootstrap, ...overrideRest } = override;
|
|
193
|
+
const mergedRaw = deepMerge(baseRest, overrideRest);
|
|
194
|
+
if (!isPlainObject(mergedRaw)) {
|
|
195
|
+
return overrideBootstrap !== undefined
|
|
196
|
+
? { ...override, worktreeBootstrap: overrideBootstrap }
|
|
197
|
+
: { ...override };
|
|
198
|
+
}
|
|
199
|
+
const merged = {};
|
|
200
|
+
for (const knob of KNOWN_SETTINGS_KNOBS) {
|
|
201
|
+
if (knob === "worktreeBootstrap")
|
|
202
|
+
continue;
|
|
203
|
+
if (knob in mergedRaw) {
|
|
204
|
+
merged[knob] = mergedRaw[knob];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const bootstrap = overrideBootstrap ?? baseBootstrap;
|
|
208
|
+
if (bootstrap !== undefined)
|
|
209
|
+
merged.worktreeBootstrap = bootstrap;
|
|
210
|
+
return merged;
|
|
211
|
+
}
|
|
212
|
+
export async function loadKeiyakuSettings(cwd) {
|
|
213
|
+
const localSettingsPath = path.join(cwd, SETTINGS_FILE);
|
|
214
|
+
const globalSettingsPath = path.join(getKeiyakuHome(), path.basename(SETTINGS_FILE));
|
|
215
|
+
let globalRead;
|
|
216
|
+
let localRead;
|
|
217
|
+
try {
|
|
218
|
+
[globalRead, localRead] = await Promise.all([
|
|
219
|
+
readSettingsSource(globalSettingsPath),
|
|
220
|
+
readSettingsSource(localSettingsPath),
|
|
221
|
+
]);
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
// loadKeiyakuSettings never throws; unexpected I/O becomes a local root disease.
|
|
225
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
226
|
+
return {
|
|
227
|
+
knobs: null,
|
|
228
|
+
diseases: [{ source: "local", reason: `settings root is unreadable (${reason})` }],
|
|
229
|
+
providerInstances: builtinProviderInstanceCatalog(),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
const globalCollected = collectSourceDiseases("global", "global", globalRead);
|
|
233
|
+
const localCollected = collectSourceDiseases("local", "project", localRead);
|
|
234
|
+
const diseases = [...globalCollected.diseases, ...localCollected.diseases];
|
|
235
|
+
let providerInstances = builtinProviderInstanceCatalog();
|
|
236
|
+
providerInstances = mergeProviderCandidates(providerInstances, globalCollected.providerCandidates);
|
|
237
|
+
providerInstances = mergeProviderCandidates(providerInstances, localCollected.providerCandidates);
|
|
238
|
+
let knobs = null;
|
|
239
|
+
if (globalRead.found || localRead.found) {
|
|
240
|
+
knobs = mergeSettingsKnobs(globalCollected.partial, localCollected.partial);
|
|
241
|
+
}
|
|
242
|
+
// Empty object still means "settings files were present"; callers treat null as absent files.
|
|
243
|
+
return { knobs, diseases, providerInstances };
|
|
244
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { CLAUDE_SETTING_SOURCES, } from "../../keiyaku.js";
|
|
3
|
+
const tomlValueSchema = z.lazy(() => z.union([
|
|
4
|
+
z.string(),
|
|
5
|
+
z.number(),
|
|
6
|
+
z.boolean(),
|
|
7
|
+
z.array(tomlValueSchema),
|
|
8
|
+
z.record(z.string(), tomlValueSchema),
|
|
9
|
+
]));
|
|
10
|
+
const structuredTomlValueSchema = z.record(z.string(), tomlValueSchema);
|
|
11
|
+
const executableSchema = z.string().superRefine((value, ctx) => {
|
|
12
|
+
if (value.length === 0 || /\s/.test(value) || value.includes("\"") || value.includes("'")) {
|
|
13
|
+
ctx.addIssue({
|
|
14
|
+
code: z.ZodIssueCode.custom,
|
|
15
|
+
message: "must be a single program name token with no spaces or quotes.",
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const nonblankOptionalString = z
|
|
20
|
+
.string()
|
|
21
|
+
.refine((value) => value.trim().length > 0, { message: "must not be blank." })
|
|
22
|
+
.optional();
|
|
23
|
+
const providerCommonFields = {
|
|
24
|
+
description: nonblankOptionalString,
|
|
25
|
+
};
|
|
26
|
+
const providerEnvSchema = z.record(z.string(), z.string()).optional();
|
|
27
|
+
function refinePiEnvInjection(data, ctx) {
|
|
28
|
+
if (data.env === undefined)
|
|
29
|
+
return;
|
|
30
|
+
if (Object.keys(data.env).length === 0)
|
|
31
|
+
return;
|
|
32
|
+
ctx.addIssue({
|
|
33
|
+
code: z.ZodIssueCode.custom,
|
|
34
|
+
path: ["env"],
|
|
35
|
+
message: "env injection not supported for provider pi",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const CODEX_SDK_FORBIDDEN_THREAD_OPTIONS_KEYS = [
|
|
39
|
+
"model",
|
|
40
|
+
"modelReasoningEffort",
|
|
41
|
+
"sandboxMode",
|
|
42
|
+
"networkAccessEnabled",
|
|
43
|
+
"webSearchMode",
|
|
44
|
+
"approvalPolicy",
|
|
45
|
+
"workingDirectory",
|
|
46
|
+
"skipGitRepoCheck",
|
|
47
|
+
];
|
|
48
|
+
const FORBIDDEN_CONFIG_KEYS = [
|
|
49
|
+
"model",
|
|
50
|
+
"model_reasoning_effort",
|
|
51
|
+
];
|
|
52
|
+
function isConfigKeyForbidden(key) {
|
|
53
|
+
return FORBIDDEN_CONFIG_KEYS.includes(key);
|
|
54
|
+
}
|
|
55
|
+
const claudeSettingSourceSchema = z.enum(CLAUDE_SETTING_SOURCES);
|
|
56
|
+
const writableRootItemSchema = z.string().superRefine((value, ctx) => {
|
|
57
|
+
if (value.length === 0) {
|
|
58
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not be empty." });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (value.startsWith("~")) {
|
|
62
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not begin with tilde." });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (value.includes("\0")) {
|
|
66
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not contain NUL byte." });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (/\$[A-Za-z_]/.test(value) || /\$\{/.test(value)) {
|
|
70
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not contain environment variable references." });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
export const providerInstanceSchema = z.discriminatedUnion("kind", [
|
|
75
|
+
z
|
|
76
|
+
.object({
|
|
77
|
+
kind: z.literal("codex-sdk"),
|
|
78
|
+
...providerCommonFields,
|
|
79
|
+
executable: executableSchema.optional(),
|
|
80
|
+
profile: z.string().optional(),
|
|
81
|
+
config: structuredTomlValueSchema.optional(),
|
|
82
|
+
threadOptions: structuredTomlValueSchema.optional(),
|
|
83
|
+
})
|
|
84
|
+
.strict()
|
|
85
|
+
.superRefine((data, ctx) => {
|
|
86
|
+
if (data.threadOptions) {
|
|
87
|
+
for (const key of CODEX_SDK_FORBIDDEN_THREAD_OPTIONS_KEYS) {
|
|
88
|
+
if (key in data.threadOptions) {
|
|
89
|
+
ctx.addIssue({
|
|
90
|
+
code: z.ZodIssueCode.custom,
|
|
91
|
+
path: ["threadOptions", key],
|
|
92
|
+
message: `"${key}" is owned by the Akuma or Keiyaku execution, not this provider instance`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (data.config) {
|
|
98
|
+
for (const key of Object.keys(data.config)) {
|
|
99
|
+
if (isConfigKeyForbidden(key)) {
|
|
100
|
+
ctx.addIssue({
|
|
101
|
+
code: z.ZodIssueCode.custom,
|
|
102
|
+
path: ["config", key],
|
|
103
|
+
message: `"${key}" is owned by the Akuma or Keiyaku execution, not this provider instance`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}),
|
|
109
|
+
z
|
|
110
|
+
.object({
|
|
111
|
+
kind: z.literal("codex-app-server"),
|
|
112
|
+
...providerCommonFields,
|
|
113
|
+
executable: executableSchema.optional(),
|
|
114
|
+
profile: z.string().optional(),
|
|
115
|
+
config: structuredTomlValueSchema.optional(),
|
|
116
|
+
writableRoots: z.array(writableRootItemSchema).optional(),
|
|
117
|
+
env: providerEnvSchema,
|
|
118
|
+
})
|
|
119
|
+
.strict()
|
|
120
|
+
.superRefine((data, ctx) => {
|
|
121
|
+
if (data.config) {
|
|
122
|
+
for (const key of Object.keys(data.config)) {
|
|
123
|
+
if (isConfigKeyForbidden(key)) {
|
|
124
|
+
ctx.addIssue({
|
|
125
|
+
code: z.ZodIssueCode.custom,
|
|
126
|
+
path: ["config", key],
|
|
127
|
+
message: `"${key}" is owned by the Akuma or Keiyaku execution, not this provider instance`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}),
|
|
133
|
+
z
|
|
134
|
+
.object({
|
|
135
|
+
kind: z.literal("claude-agent-sdk"),
|
|
136
|
+
...providerCommonFields,
|
|
137
|
+
executable: executableSchema.optional(),
|
|
138
|
+
settingSources: z.array(claudeSettingSourceSchema).optional(),
|
|
139
|
+
env: providerEnvSchema,
|
|
140
|
+
})
|
|
141
|
+
.strict(),
|
|
142
|
+
z
|
|
143
|
+
.object({
|
|
144
|
+
kind: z.literal("opencode-sdk"),
|
|
145
|
+
...providerCommonFields,
|
|
146
|
+
executable: executableSchema.optional(),
|
|
147
|
+
env: providerEnvSchema,
|
|
148
|
+
})
|
|
149
|
+
.strict(),
|
|
150
|
+
z
|
|
151
|
+
.object({
|
|
152
|
+
kind: z.literal("pi"),
|
|
153
|
+
...providerCommonFields,
|
|
154
|
+
env: providerEnvSchema,
|
|
155
|
+
})
|
|
156
|
+
.strict()
|
|
157
|
+
.superRefine(refinePiEnvInjection),
|
|
158
|
+
]);
|
|
159
|
+
const PROVIDER_INSTANCE_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
160
|
+
export const providerInstanceNameSchema = z
|
|
161
|
+
.string()
|
|
162
|
+
.max(64, "must contain at most 64 characters.")
|
|
163
|
+
.regex(PROVIDER_INSTANCE_NAME_PATTERN, "must match ^[a-z0-9][a-z0-9-]*$.");
|
|
164
|
+
export const BUILTIN_PROVIDER_INSTANCE_CANDIDATES = {
|
|
165
|
+
"codex-sdk": { name: "codex-sdk", source: "builtin", instance: { kind: "codex-sdk", executable: "codex" } },
|
|
166
|
+
"codex-app-server": {
|
|
167
|
+
name: "codex-app-server",
|
|
168
|
+
source: "builtin",
|
|
169
|
+
instance: { kind: "codex-app-server", executable: "codex" },
|
|
170
|
+
},
|
|
171
|
+
"claude-agent-sdk": {
|
|
172
|
+
name: "claude-agent-sdk",
|
|
173
|
+
source: "builtin",
|
|
174
|
+
instance: { kind: "claude-agent-sdk", executable: "claude", settingSources: ["project"] },
|
|
175
|
+
},
|
|
176
|
+
"opencode-sdk": {
|
|
177
|
+
name: "opencode-sdk",
|
|
178
|
+
source: "builtin",
|
|
179
|
+
instance: { kind: "opencode-sdk", executable: "opencode" },
|
|
180
|
+
},
|
|
181
|
+
pi: { name: "pi", source: "builtin", instance: { kind: "pi" } },
|
|
182
|
+
};
|
|
183
|
+
const AGENT_NAME_PATTERN = /^[a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*$/;
|
|
184
|
+
export const agentNameSchema = z
|
|
185
|
+
.string()
|
|
186
|
+
.min(1, "must not be empty.")
|
|
187
|
+
.regex(AGENT_NAME_PATTERN, "must contain only alphanumeric characters, dots, and hyphens, and must not start or end with a separator.");
|
|
188
|
+
function optionalTrimmedString() {
|
|
189
|
+
return z.string().transform((value) => {
|
|
190
|
+
const trimmed = value.trim();
|
|
191
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
192
|
+
}).optional();
|
|
193
|
+
}
|
|
194
|
+
function requiredTrimmedString(message) {
|
|
195
|
+
return z.string().transform((value, ctx) => {
|
|
196
|
+
const trimmed = value.trim();
|
|
197
|
+
if (trimmed.length === 0) {
|
|
198
|
+
ctx.addIssue({
|
|
199
|
+
code: z.ZodIssueCode.custom,
|
|
200
|
+
message,
|
|
201
|
+
});
|
|
202
|
+
return z.NEVER;
|
|
203
|
+
}
|
|
204
|
+
return trimmed;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const gateCommandSchema = z
|
|
208
|
+
.object({
|
|
209
|
+
label: requiredTrimmedString("must not be empty."),
|
|
210
|
+
command: requiredTrimmedString("must not be empty.").superRefine((value, ctx) => {
|
|
211
|
+
if (/\s/.test(value) || value.includes("\"") || value.includes("'")) {
|
|
212
|
+
ctx.addIssue({
|
|
213
|
+
code: z.ZodIssueCode.custom,
|
|
214
|
+
message: "must be a single program name token with no spaces or quotes.",
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}),
|
|
218
|
+
args: z.array(z.string()).optional(),
|
|
219
|
+
})
|
|
220
|
+
.strict()
|
|
221
|
+
.transform((value) => ({
|
|
222
|
+
...value,
|
|
223
|
+
args: value.args ?? [],
|
|
224
|
+
}));
|
|
225
|
+
const gateSchema = z
|
|
226
|
+
.object({
|
|
227
|
+
commands: z.array(gateCommandSchema).optional(),
|
|
228
|
+
})
|
|
229
|
+
.strict();
|
|
230
|
+
const WORKTREE_BOOTSTRAP_DEFAULT_TIMEOUT_MS = 300_000;
|
|
231
|
+
const WORKTREE_BOOTSTRAP_MIN_TIMEOUT_MS = 1;
|
|
232
|
+
const WORKTREE_BOOTSTRAP_MAX_TIMEOUT_MS = 3_600_000;
|
|
233
|
+
export const worktreeBootstrapSchema = z
|
|
234
|
+
.object({
|
|
235
|
+
command: z
|
|
236
|
+
.array(z.string())
|
|
237
|
+
.min(1, "command must be a nonempty argv array.")
|
|
238
|
+
.superRefine((items, ctx) => {
|
|
239
|
+
items.forEach((item, index) => {
|
|
240
|
+
if (item.trim().length === 0) {
|
|
241
|
+
ctx.addIssue({
|
|
242
|
+
code: z.ZodIssueCode.custom,
|
|
243
|
+
path: [index],
|
|
244
|
+
message: "command items must be nonempty after trimming.",
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}),
|
|
249
|
+
timeoutMs: z
|
|
250
|
+
.number()
|
|
251
|
+
.int("timeoutMs must be an integer.")
|
|
252
|
+
.min(WORKTREE_BOOTSTRAP_MIN_TIMEOUT_MS, `timeoutMs must be >= ${WORKTREE_BOOTSTRAP_MIN_TIMEOUT_MS}.`)
|
|
253
|
+
.max(WORKTREE_BOOTSTRAP_MAX_TIMEOUT_MS, `timeoutMs must be <= ${WORKTREE_BOOTSTRAP_MAX_TIMEOUT_MS}.`)
|
|
254
|
+
.optional(),
|
|
255
|
+
})
|
|
256
|
+
.strict()
|
|
257
|
+
.transform((value) => ({
|
|
258
|
+
command: value.command,
|
|
259
|
+
timeoutMs: value.timeoutMs ?? WORKTREE_BOOTSTRAP_DEFAULT_TIMEOUT_MS,
|
|
260
|
+
}));
|
|
261
|
+
const settingsSchema = z
|
|
262
|
+
.object({
|
|
263
|
+
default: agentNameSchema.optional(),
|
|
264
|
+
reviewer: agentNameSchema.optional(),
|
|
265
|
+
gate: gateSchema.optional(),
|
|
266
|
+
defaultBranch: optionalTrimmedString(),
|
|
267
|
+
worktreeBootstrap: worktreeBootstrapSchema.optional(),
|
|
268
|
+
})
|
|
269
|
+
.strict();
|
|
270
|
+
export const knobSchemas = {
|
|
271
|
+
default: agentNameSchema,
|
|
272
|
+
reviewer: agentNameSchema,
|
|
273
|
+
gate: gateSchema,
|
|
274
|
+
defaultBranch: optionalTrimmedString(),
|
|
275
|
+
worktreeBootstrap: worktreeBootstrapSchema,
|
|
276
|
+
};
|
package/build/core/addressing.js
CHANGED
|
@@ -3,7 +3,7 @@ import { FlowError } from "../flow-error.js";
|
|
|
3
3
|
import { listContractIds, readLedger } from "./ledger.js";
|
|
4
4
|
import { isResponseArtifactId } from "./response-artifact-id.js";
|
|
5
5
|
import { stableRepoRoot } from "./worktree-path.js";
|
|
6
|
-
import { deriveContractState, isTerminalState, } from "./status.js";
|
|
6
|
+
import { deriveContractState, isTerminalState, } from "./status/lifecycle.js";
|
|
7
7
|
function normalizeAddressQuery(raw) {
|
|
8
8
|
const value = raw.startsWith("@") ? raw.slice(1) : raw;
|
|
9
9
|
if (!value.trim()) {
|
package/build/core/amend.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { FlowError } from "../flow-error.js";
|
|
4
|
-
import { getCurrentBranch,
|
|
4
|
+
import { getCurrentBranch, requireGitRepository } from "../git/branches.js";
|
|
5
5
|
import { KEIYAKU_FILE } from "../keiyaku.js";
|
|
6
6
|
import { activeLedgerRef, appendWithRetry, readLedger } from "./ledger.js";
|
|
7
|
-
import { renderContractView } from "./
|
|
8
|
-
import { deriveContractState, isTerminalState } from "./status.js";
|
|
7
|
+
import { renderContractView } from "./contract-view.js";
|
|
8
|
+
import { deriveContractState, isTerminalState } from "./status/lifecycle.js";
|
|
9
9
|
import { resolveContractAddress, withResolvedAddress } from "./addressing.js";
|
|
10
|
-
import { resolveDefaultBranch } from "./status.js";
|
|
10
|
+
import { resolveDefaultBranch } from "./status/drift.js";
|
|
11
11
|
import { assertCleanWorkingTree } from "./arc.js";
|
|
12
12
|
import { normalizeScopePattern } from "./scope.js";
|
|
13
13
|
function requireAmendment(value) {
|
|
@@ -71,10 +71,7 @@ export async function amendContract(input) {
|
|
|
71
71
|
return { contractId: input.contractId, amendmentNumber, ledgerHead: result.head };
|
|
72
72
|
}
|
|
73
73
|
export async function amendKeiyaku(input) {
|
|
74
|
-
|
|
75
|
-
if (!isRepo) {
|
|
76
|
-
throw new FlowError("NOT_GIT_REPO", `${input.cwd} is not a git repository`);
|
|
77
|
-
}
|
|
74
|
+
await requireGitRepository(input.cwd, "amend");
|
|
78
75
|
const address = await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
|
|
79
76
|
return await withResolvedAddress(address, async () => {
|
|
80
77
|
const contractId = address.binding.commissionId;
|
package/build/core/arc.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { requireText, FlowError } from "../flow-error.js";
|
|
4
|
-
import { getCurrentBranch,
|
|
4
|
+
import { getCurrentBranch, requireGitRepository } from "../git/branches.js";
|
|
5
5
|
import { DIRTY_FILE_CATEGORY, getDirtyFiles, renderDirtyFileStatusLine } from "../git/worktree.js";
|
|
6
6
|
import { KEIYAKU_FILE, ACTOR_IDENTITY } from "../keiyaku.js";
|
|
7
7
|
import { buildDirtyWorktreeArtifactHint } from "./hints.js";
|
|
@@ -9,9 +9,10 @@ import { normalizeInlineTitle } from "./markdown/titles.js";
|
|
|
9
9
|
import { parseCommandMarkdown, requireMarkdownTextSection, requireMarkdownTitle, } from "./command-io.js";
|
|
10
10
|
import { resolveContractAddress, withResolvedAddress } from "./addressing.js";
|
|
11
11
|
import { activeLedgerRef, appendWithRetry, readLedger } from "./ledger.js";
|
|
12
|
-
import { renderContractView } from "./
|
|
13
|
-
import { findOpenArc, getMaxArc,
|
|
14
|
-
import { deriveContractState, isTerminalState
|
|
12
|
+
import { renderContractView } from "./contract-view.js";
|
|
13
|
+
import { findOpenArc, getMaxArc, sealCurrentArc } from "./seal.js";
|
|
14
|
+
import { deriveContractState, isTerminalState } from "./status/lifecycle.js";
|
|
15
|
+
import { resolveDefaultBranch } from "./status/drift.js";
|
|
15
16
|
import { persistResponseHistory } from "./transcripts.js";
|
|
16
17
|
const DIRTY_WORKTREE_LIST_LIMIT = 10;
|
|
17
18
|
const KEIYAKU_ARTIFACT_DIR_PREFIX = ".keiyaku/";
|
|
@@ -111,7 +112,7 @@ export async function openArc(input) {
|
|
|
111
112
|
throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} not found or not active`);
|
|
112
113
|
}
|
|
113
114
|
assertArcOpenable(input.contractId, ledger.entries);
|
|
114
|
-
const sealed = await
|
|
115
|
+
const sealed = await sealCurrentArc({
|
|
115
116
|
cwd: input.cwd,
|
|
116
117
|
renderCwd: input.cwd,
|
|
117
118
|
contractId: input.contractId,
|
|
@@ -160,9 +161,7 @@ async function readBranchLabels(cwd) {
|
|
|
160
161
|
return { currentBranch, baseBranch };
|
|
161
162
|
}
|
|
162
163
|
export async function arcKeiyaku(input) {
|
|
163
|
-
|
|
164
|
-
throw new FlowError("NOT_GIT_REPO", `${input.cwd} is not a git repository`);
|
|
165
|
-
}
|
|
164
|
+
await requireGitRepository(input.cwd, "arc");
|
|
166
165
|
const address = await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
|
|
167
166
|
return await withResolvedAddress(address, async () => {
|
|
168
167
|
await assertCleanWorkingTree(input.cwd, []);
|