@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.
- package/README.md +73 -56
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms_v2.js +103 -0
- package/build/agents/harness/activity-values.js +54 -0
- package/build/agents/harness/event-channel.js +55 -20
- package/build/agents/harness/events.js +47 -1
- package/build/agents/harness/execution-handle.js +292 -61
- package/build/agents/harness/index.js +103 -121
- package/build/agents/harness/pump.js +36 -47
- package/build/agents/harness/runtime.js +3 -0
- package/build/agents/launch-snapshot_v2.js +348 -0
- package/build/agents/opencode-sdk.js +42 -169
- package/build/agents/providers/claude-agent-sdk.js +240 -79
- package/build/agents/providers/codex-app-server.js +280 -72
- package/build/agents/providers/codex-sdk.js +159 -41
- package/build/agents/providers/event-type-tail.js +16 -0
- package/build/agents/providers/opencode-sdk.js +165 -59
- package/build/agents/providers/pi.js +254 -42
- package/build/agents/selector_v2.js +122 -0
- package/build/cli/commands/akuma.js +120 -42
- package/build/cli/completion.js +6 -7
- package/build/cli/flags.js +36 -10
- package/build/cli/help.js +42 -26
- package/build/cli/index.js +225 -93
- package/build/cli/parse.js +194 -75
- package/build/cli/projection-address.js +11 -0
- package/build/cli/render/address.js +88 -0
- package/build/cli/render/arc.js +10 -10
- package/build/cli/render/call.js +59 -0
- package/build/cli/render/compact-text.js +3 -0
- package/build/cli/render/errors.js +4 -2
- package/build/cli/render/format.js +6 -0
- package/build/cli/render/line-width.js +70 -0
- package/build/cli/render/misc.js +7 -4
- package/build/cli/render/petition.js +4 -3
- package/build/cli/render/projection-activity.js +201 -0
- package/build/cli/render/shared.js +46 -12
- package/build/cli/render/status.js +59 -10
- package/build/cli/render/tool-ledger-rollup.js +74 -0
- package/build/cli/render/tool-presentation.js +97 -0
- package/build/cli/render/wait.js +229 -0
- package/build/cli/skills-install.js +42 -24
- package/build/cli/subagent-guard.js +1 -1
- package/build/config/akuma-loader_v2.js +222 -0
- package/build/config/env-keys.js +0 -4
- package/build/config/env.js +0 -6
- package/build/config/settings.js +325 -12
- package/build/core/addressing.js +224 -0
- package/build/core/amend.js +30 -47
- package/build/core/arc.js +68 -60
- package/build/core/bind.js +105 -25
- package/build/core/call-persist.js +69 -0
- package/build/core/call.js +658 -0
- package/build/core/claim.js +72 -44
- package/build/core/command-io.js +19 -19
- package/build/core/context.js +11 -36
- package/build/core/draft.js +2 -3
- package/build/core/entry.js +101 -6
- package/build/core/execution-coordinate.js +62 -0
- package/build/core/execution-pact.js +141 -0
- package/build/core/forfeit.js +2 -3
- package/build/core/hints.js +10 -10
- package/build/core/ids.js +6 -0
- package/build/core/ledger.js +16 -1
- package/build/core/petition-claim-gates.js +14 -12
- package/build/core/petition-claim.js +1 -0
- package/build/core/petition-run.js +6 -2
- package/build/core/petition.js +63 -21
- package/build/core/places.js +106 -13
- package/build/core/projection/generation.js +412 -0
- package/build/core/projection/heart.js +316 -0
- package/build/core/projection/identity.js +81 -0
- package/build/core/projection/leash.js +87 -0
- package/build/core/projection/mint.js +154 -0
- package/build/core/projection-activity.js +219 -0
- package/build/core/projection-coordinate.js +35 -0
- package/build/core/projection-core.js +69 -463
- package/build/core/projection-generation-continuation.js +31 -0
- package/build/core/projection-generation-execution.js +148 -0
- package/build/core/projection-generation-identity.js +17 -0
- package/build/core/projection-generation-launcher.js +90 -0
- package/build/core/projection-generation-process.js +43 -0
- package/build/core/projection-generation-runner.js +144 -0
- package/build/core/projection-generation-runtime.js +15 -0
- package/build/core/projection-generation-store.js +707 -0
- package/build/core/projection-identity.js +11 -0
- package/build/core/projection-life-observer.js +87 -0
- package/build/core/projection-life-protocol.js +95 -0
- package/build/core/projection-mint.js +96 -24
- package/build/core/projection-runner-lock.js +230 -0
- package/build/core/projection-status.js +207 -107
- package/build/core/projection-tells.js +401 -0
- package/build/core/projection-wait.js +155 -0
- package/build/core/projection-wake.js +109 -231
- package/build/core/queue_v2.js +342 -0
- package/build/core/registry.js +6 -3
- package/build/core/render.js +63 -1
- package/build/core/renew.js +80 -53
- package/build/core/scope.js +186 -137
- package/build/core/seal.js +16 -19
- package/build/core/status.js +88 -25
- package/build/core/stored-agent-event.js +101 -0
- package/build/core/transcripts.js +214 -228
- package/build/core/verdict.js +27 -15
- package/build/core/worktree-bootstrap.js +187 -0
- package/build/core/worktree-path.js +72 -18
- package/build/flow-error.js +4 -6
- package/build/generated/version.js +1 -1
- package/build/git/branches.js +6 -1
- package/build/index.js +8 -5
- package/build/keiyaku.js +1 -3
- package/package.json +6 -5
- package/skills/keiyaku/SKILL.md +6 -47
- package/skills/keiyaku-akuma/SKILL.md +75 -0
- package/build/agents/effective-policy.js +0 -32
- package/build/agents/index.js +0 -114
- package/build/agents/selector.js +0 -28
- package/build/cli/render/summon.js +0 -17
- package/build/cli/render/tell.js +0 -11
- package/build/config/akuma-loader.js +0 -268
- package/build/core/queue.js +0 -73
- package/build/core/summon-persist.js +0 -73
- package/build/core/summon.js +0 -377
package/build/core/amend.js
CHANGED
|
@@ -6,15 +6,10 @@ import { KEIYAKU_FILE } from "../keiyaku.js";
|
|
|
6
6
|
import { activeLedgerRef, appendWithRetry, readLedger } from "./ledger.js";
|
|
7
7
|
import { renderContractView } from "./render.js";
|
|
8
8
|
import { deriveContractState, isTerminalState } from "./status.js";
|
|
9
|
-
import {
|
|
9
|
+
import { resolveContractAddress, withResolvedAddress } from "./addressing.js";
|
|
10
10
|
import { resolveDefaultBranch } from "./status.js";
|
|
11
11
|
import { assertCleanWorkingTree } from "./arc.js";
|
|
12
|
-
|
|
13
|
-
const normalized = value.trim();
|
|
14
|
-
if (!normalized)
|
|
15
|
-
throw new FlowError("EMPTY_PARAM", `${name} cannot be empty`);
|
|
16
|
-
return normalized;
|
|
17
|
-
}
|
|
12
|
+
import { normalizeScopePattern } from "./scope.js";
|
|
18
13
|
function requireAmendment(value) {
|
|
19
14
|
if (!value.trim())
|
|
20
15
|
throw new FlowError("EMPTY_PARAM", "amendment cannot be empty");
|
|
@@ -39,15 +34,11 @@ async function renderUpdatedContract(cwd, contractId) {
|
|
|
39
34
|
function normalizeScopeDelta(scopeDelta) {
|
|
40
35
|
if (!scopeDelta)
|
|
41
36
|
return undefined;
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
if (conflict) {
|
|
46
|
-
throw new FlowError("INVALID_KEIYAKU_DRAFT", `amend scope pattern appears in both ## Scope Add and ## Scope Remove: ${conflict}`);
|
|
47
|
-
}
|
|
48
|
-
if (add.length === 0 && remove.length === 0)
|
|
37
|
+
// Scope patterns are gitignore text; do not requireText/trim, or escaped trailing spaces die.
|
|
38
|
+
const add = scopeDelta.add.map((pattern) => normalizeScopePattern(pattern));
|
|
39
|
+
if (add.length === 0)
|
|
49
40
|
return undefined;
|
|
50
|
-
return { add
|
|
41
|
+
return { add };
|
|
51
42
|
}
|
|
52
43
|
export async function amendContract(input) {
|
|
53
44
|
const amendment = requireAmendment(input.amendment);
|
|
@@ -84,37 +75,29 @@ export async function amendKeiyaku(input) {
|
|
|
84
75
|
if (!isRepo) {
|
|
85
76
|
throw new FlowError("NOT_GIT_REPO", `${input.cwd} is not a git repository`);
|
|
86
77
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
78
|
+
const address = await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
|
|
79
|
+
return await withResolvedAddress(address, async () => {
|
|
80
|
+
const contractId = address.binding.commissionId;
|
|
81
|
+
await assertCleanWorkingTree(input.cwd, []);
|
|
82
|
+
const amendment = requireAmendment(input.amendment);
|
|
83
|
+
const currentBranch = await getCurrentBranch(input.cwd);
|
|
84
|
+
const baseBranch = (await resolveDefaultBranch(input.cwd))?.branch ?? currentBranch;
|
|
85
|
+
const amended = await amendContract({
|
|
86
|
+
cwd: input.cwd,
|
|
87
|
+
contractId,
|
|
88
|
+
actor: "keiyaku",
|
|
89
|
+
nowMs: Date.now(),
|
|
90
|
+
amendment,
|
|
91
|
+
scopeDelta: input.scopeDelta,
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
status: "success",
|
|
95
|
+
amendmentNumber: amended.amendmentNumber,
|
|
96
|
+
currentBranch,
|
|
97
|
+
baseBranch,
|
|
98
|
+
ledgerCommit: amended.ledgerHead,
|
|
99
|
+
summary: `Appended Amendment ${amended.amendmentNumber} to contract ledger ${contractId}.`,
|
|
100
|
+
address,
|
|
101
|
+
};
|
|
111
102
|
});
|
|
112
|
-
return {
|
|
113
|
-
status: "success",
|
|
114
|
-
amendmentNumber: amended.amendmentNumber,
|
|
115
|
-
currentBranch,
|
|
116
|
-
baseBranch,
|
|
117
|
-
ledgerCommit: amended.ledgerHead,
|
|
118
|
-
summary: `Appended Amendment ${amended.amendmentNumber} to contract ledger ${contractId}.`,
|
|
119
|
-
};
|
|
120
103
|
}
|
package/build/core/arc.js
CHANGED
|
@@ -6,8 +6,8 @@ import { DIRTY_FILE_CATEGORY, getDirtyFiles, renderDirtyFileStatusLine } from ".
|
|
|
6
6
|
import { KEIYAKU_FILE, ACTOR_IDENTITY } from "../keiyaku.js";
|
|
7
7
|
import { buildDirtyWorktreeArtifactHint } from "./hints.js";
|
|
8
8
|
import { normalizeInlineTitle } from "./markdown/titles.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { parseCommandMarkdown, requireMarkdownTextSection, requireMarkdownTitle, } from "./command-io.js";
|
|
10
|
+
import { resolveContractAddress, withResolvedAddress } from "./addressing.js";
|
|
11
11
|
import { activeLedgerRef, appendWithRetry, readLedger } from "./ledger.js";
|
|
12
12
|
import { renderContractView } from "./render.js";
|
|
13
13
|
import { findOpenArc, getMaxArc, sealOpenArc } from "./seal.js";
|
|
@@ -117,6 +117,7 @@ export async function openArc(input) {
|
|
|
117
117
|
contractId: input.contractId,
|
|
118
118
|
sealedBy: "arc",
|
|
119
119
|
nowMs: input.nowMs,
|
|
120
|
+
waivePaths: input.waivePaths,
|
|
120
121
|
});
|
|
121
122
|
const at = new Date(input.nowMs).toISOString();
|
|
122
123
|
let arcNumber = 0;
|
|
@@ -141,14 +142,16 @@ export async function openArc(input) {
|
|
|
141
142
|
await renderUpdatedContract(input.cwd, input.contractId);
|
|
142
143
|
return { contractId: input.contractId, arc: arcNumber, ledgerHead: result.head, sealed };
|
|
143
144
|
}
|
|
144
|
-
export function buildArcInput(content, cwd) {
|
|
145
|
+
export function buildArcInput(content, cwd, waivePaths = [], contractId, contractAddressSource) {
|
|
145
146
|
const parts = parseCommandMarkdown(content);
|
|
146
|
-
assertKnownMarkdownSections(parts, "arc", ["Objective", "Brief"]);
|
|
147
147
|
return {
|
|
148
148
|
cwd,
|
|
149
|
+
...(contractId ? { contractId } : {}),
|
|
150
|
+
...(contractAddressSource ? { contractAddressSource } : {}),
|
|
149
151
|
title: requireMarkdownTitle(parts),
|
|
150
152
|
objective: requireMarkdownTextSection(parts, "Objective"),
|
|
151
153
|
brief: requireMarkdownTextSection(parts, "Brief"),
|
|
154
|
+
waivePaths,
|
|
152
155
|
};
|
|
153
156
|
}
|
|
154
157
|
async function readBranchLabels(cwd) {
|
|
@@ -160,66 +163,71 @@ export async function arcKeiyaku(input) {
|
|
|
160
163
|
if (!(await isGitRepo(input.cwd))) {
|
|
161
164
|
throw new FlowError("NOT_GIT_REPO", `${input.cwd} is not a git repository`);
|
|
162
165
|
}
|
|
163
|
-
await
|
|
164
|
-
|
|
165
|
-
await
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const opened = await openArc({
|
|
178
|
-
cwd: input.cwd,
|
|
179
|
-
contractId,
|
|
180
|
-
actor: ACTOR_IDENTITY,
|
|
181
|
-
nowMs: Date.now(),
|
|
182
|
-
title,
|
|
183
|
-
objective,
|
|
184
|
-
brief,
|
|
185
|
-
});
|
|
186
|
-
const { currentBranch, baseBranch } = await readBranchLabels(input.cwd);
|
|
187
|
-
const summary = opened.sealed
|
|
188
|
-
? `a${opened.sealed.arc} sealed; a${opened.arc} open: ${title}.`
|
|
189
|
-
: `a${opened.arc} open: ${title}.`;
|
|
190
|
-
const diff = opened.sealed?.incrementalDiff ?? "working tree untouched";
|
|
191
|
-
let responsePath;
|
|
192
|
-
try {
|
|
193
|
-
responsePath = await persistResponseHistory({
|
|
166
|
+
const address = await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
|
|
167
|
+
return await withResolvedAddress(address, async () => {
|
|
168
|
+
await assertCleanWorkingTree(input.cwd, []);
|
|
169
|
+
try {
|
|
170
|
+
await fs.access(path.join(input.cwd, KEIYAKU_FILE));
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
throw new FlowError("MISSING_PROTOCOL_FILES", `missing protocol file for current keiyaku (${KEIYAKU_FILE})`);
|
|
174
|
+
}
|
|
175
|
+
const title = normalizeInlineTitle(requireText("title", input.title));
|
|
176
|
+
const objective = requireText("objective", input.objective);
|
|
177
|
+
const brief = requireText("brief", input.brief);
|
|
178
|
+
const contractId = address.binding.commissionId;
|
|
179
|
+
const opened = await openArc({
|
|
194
180
|
cwd: input.cwd,
|
|
195
|
-
|
|
181
|
+
contractId,
|
|
182
|
+
actor: ACTOR_IDENTITY,
|
|
183
|
+
nowMs: Date.now(),
|
|
196
184
|
title,
|
|
197
|
-
|
|
185
|
+
objective,
|
|
186
|
+
brief,
|
|
187
|
+
waivePaths: input.waivePaths,
|
|
188
|
+
});
|
|
189
|
+
const { currentBranch, baseBranch } = await readBranchLabels(input.cwd);
|
|
190
|
+
const summary = opened.sealed
|
|
191
|
+
? `a${opened.sealed.arc} sealed; a${opened.arc} open: ${title}.`
|
|
192
|
+
: `a${opened.arc} open: ${title}.`;
|
|
193
|
+
const diff = opened.sealed?.incrementalDiff ?? "working tree untouched";
|
|
194
|
+
let responsePath;
|
|
195
|
+
let artifactId;
|
|
196
|
+
try {
|
|
197
|
+
const persisted = await persistResponseHistory({
|
|
198
|
+
cwd: input.cwd,
|
|
199
|
+
tool: "arc",
|
|
200
|
+
provenance: { authority: { kind: "repository" }, binding: address.binding, evidenceCwd: input.cwd },
|
|
201
|
+
title,
|
|
202
|
+
objective,
|
|
203
|
+
brief,
|
|
204
|
+
ledgerCommit: opened.ledgerHead,
|
|
205
|
+
summary,
|
|
206
|
+
diff,
|
|
207
|
+
...(opened.sealed ? { sealed: opened.sealed } : {}),
|
|
208
|
+
});
|
|
209
|
+
responsePath = persisted.responsePath;
|
|
210
|
+
artifactId = persisted.artifactId;
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
responsePath = undefined;
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
status: "success",
|
|
217
|
+
arc: opened.arc,
|
|
218
|
+
currentBranch,
|
|
219
|
+
baseBranch,
|
|
198
220
|
commit: opened.ledgerHead,
|
|
221
|
+
diff,
|
|
222
|
+
summary,
|
|
223
|
+
title,
|
|
199
224
|
objective,
|
|
200
225
|
brief,
|
|
201
226
|
ledgerCommit: opened.ledgerHead,
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
responsePath = undefined;
|
|
209
|
-
}
|
|
210
|
-
return {
|
|
211
|
-
status: "success",
|
|
212
|
-
arc: opened.arc,
|
|
213
|
-
currentBranch,
|
|
214
|
-
baseBranch,
|
|
215
|
-
commit: opened.ledgerHead,
|
|
216
|
-
diff,
|
|
217
|
-
summary,
|
|
218
|
-
title,
|
|
219
|
-
objective,
|
|
220
|
-
brief,
|
|
221
|
-
ledgerCommit: opened.ledgerHead,
|
|
222
|
-
sealed: opened.sealed ?? undefined,
|
|
223
|
-
responsePath,
|
|
224
|
-
};
|
|
227
|
+
sealed: opened.sealed ?? undefined,
|
|
228
|
+
responsePath,
|
|
229
|
+
artifactId,
|
|
230
|
+
address,
|
|
231
|
+
};
|
|
232
|
+
});
|
|
225
233
|
}
|
package/build/core/bind.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import * as crypto from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { assertSettingsKnobUsable, loadKeiyakuSettings } from "../config/settings.js";
|
|
4
5
|
import { FlowError } from "../flow-error.js";
|
|
5
6
|
import { KEIYAKU_FILE } from "../keiyaku.js";
|
|
6
7
|
import { createGit, wrapGitError } from "../git/core.js";
|
|
7
|
-
import { activeLedgerRef, appendEntry, appendWithRetry, readLedger } from "./ledger.js";
|
|
8
|
+
import { activeLedgerRef, appendEntry, appendWithRetry, listContractIds, readLedger } from "./ledger.js";
|
|
8
9
|
import { parseBindDraft } from "./draft.js";
|
|
9
10
|
import { assertValidContractId, buildContractId } from "./ids.js";
|
|
10
11
|
import { CONTRACT_PLACES } from "./places.js";
|
|
11
12
|
import { renderContractView } from "./render.js";
|
|
12
13
|
import { registerScope, releaseScope } from "./registry.js";
|
|
13
|
-
import { deriveContractState, isTerminalState,
|
|
14
|
-
import {
|
|
14
|
+
import { deriveContractState, isTerminalState, normalizeTargetRef, resolveDefaultBranch } from "./status.js";
|
|
15
|
+
import { bootstrapEntryDataFromObservation, isBootstrapSuccess, runWorktreeBootstrap, } from "./worktree-bootstrap.js";
|
|
16
|
+
import { contractWorktreePathFromBind, findBindEntry } from "./worktree-path.js";
|
|
15
17
|
function randomBytesDefault() {
|
|
16
18
|
return [...crypto.randomBytes(10)];
|
|
17
19
|
}
|
|
@@ -112,25 +114,50 @@ async function appendBindFailedForfeit(cwd, id, actor, at) {
|
|
|
112
114
|
throw new FlowError("INTERNAL_STATE", `BIND_FAILURE_FORFEIT_FAILED: failed to append bind-failed forfeit for ${id}: ${result.reason}`);
|
|
113
115
|
}
|
|
114
116
|
}
|
|
115
|
-
async function
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
async function appendBootstrapEntry(cwd, id, actor, at, data) {
|
|
118
|
+
const entry = {
|
|
119
|
+
v: 1,
|
|
120
|
+
kind: "bootstrap",
|
|
121
|
+
contract: id,
|
|
122
|
+
at,
|
|
123
|
+
actor,
|
|
124
|
+
data,
|
|
125
|
+
};
|
|
126
|
+
// Bootstrap is legal only at index 1 immediately after the planned bind.
|
|
127
|
+
// If another entry advanced during bootstrap execution, abort rather than
|
|
128
|
+
// CAS-retrying into a late (corrupt) position.
|
|
129
|
+
const result = await appendWithRetry(cwd, activeLedgerRef(id), (_currentHead, currentEntries) => {
|
|
130
|
+
if (currentEntries.length !== 1)
|
|
131
|
+
return null;
|
|
132
|
+
const only = currentEntries[0];
|
|
133
|
+
if (!only || only.kind !== "bind" || only.contract !== id || !only.data.bootstrap) {
|
|
134
|
+
return null;
|
|
123
135
|
}
|
|
136
|
+
return entry;
|
|
137
|
+
});
|
|
138
|
+
if (!result.ok) {
|
|
139
|
+
throw new FlowError("INTERNAL_STATE", `BIND_BOOTSTRAP_APPEND_FAILED: failed to append bootstrap entry for ${id}: ${result.reason}`);
|
|
124
140
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
141
|
+
return result.head;
|
|
142
|
+
}
|
|
143
|
+
function resolvedBootstrapPlan(settings) {
|
|
144
|
+
if (!settings)
|
|
145
|
+
return undefined;
|
|
146
|
+
return {
|
|
147
|
+
command: [...settings.command],
|
|
148
|
+
timeoutMs: settings.timeoutMs,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function bootstrapProgressLine(command) {
|
|
152
|
+
return `bootstrap » ${command.join(" ")}`;
|
|
153
|
+
}
|
|
154
|
+
async function occupiedWorktreePlaces(cwd) {
|
|
155
|
+
const occupied = new Set();
|
|
129
156
|
for (const id of await listContractIds(cwd)) {
|
|
130
157
|
const ledger = await readLedger(cwd, id);
|
|
131
158
|
if (!ledger || isTerminalState(deriveContractState(ledger.entries)))
|
|
132
159
|
continue;
|
|
133
|
-
const place =
|
|
160
|
+
const place = findBindEntry(ledger.entries)?.data.place;
|
|
134
161
|
if (place)
|
|
135
162
|
occupied.add(place);
|
|
136
163
|
}
|
|
@@ -143,12 +170,21 @@ function randomIndex(randomBytes, size) {
|
|
|
143
170
|
}
|
|
144
171
|
return value;
|
|
145
172
|
}
|
|
173
|
+
function nextGenerationPlace(base, occupied) {
|
|
174
|
+
for (let generation = 2;; generation += 1) {
|
|
175
|
+
const candidate = `${base}${generation}`;
|
|
176
|
+
if (!occupied.has(candidate))
|
|
177
|
+
return candidate;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
146
180
|
async function allocateWorktreePlace(cwd, randomBytes) {
|
|
147
181
|
const occupied = await occupiedWorktreePlaces(cwd);
|
|
148
182
|
const candidates = CONTRACT_PLACES.filter((place) => !occupied.has(place));
|
|
149
|
-
if (candidates.length
|
|
150
|
-
return
|
|
151
|
-
|
|
183
|
+
if (candidates.length > 0) {
|
|
184
|
+
return candidates[randomIndex(randomBytes, candidates.length)];
|
|
185
|
+
}
|
|
186
|
+
const base = CONTRACT_PLACES[randomIndex(randomBytes, CONTRACT_PLACES.length)];
|
|
187
|
+
return nextGenerationPlace(base, occupied);
|
|
152
188
|
}
|
|
153
189
|
function makeBindEntry(input) {
|
|
154
190
|
return {
|
|
@@ -162,9 +198,11 @@ function makeBindEntry(input) {
|
|
|
162
198
|
objective: input.draft.objective,
|
|
163
199
|
scope: input.draft.scope,
|
|
164
200
|
base: input.base,
|
|
201
|
+
target: input.target,
|
|
165
202
|
workspace: input.workspace,
|
|
166
203
|
after: input.after,
|
|
167
204
|
...(input.workspace === "worktree" ? { place: input.place ?? null } : {}),
|
|
205
|
+
...(input.bootstrap ? { bootstrap: input.bootstrap } : {}),
|
|
168
206
|
},
|
|
169
207
|
};
|
|
170
208
|
}
|
|
@@ -173,6 +211,17 @@ export async function bindContract(input) {
|
|
|
173
211
|
const workspace = input.workspace ?? "here";
|
|
174
212
|
const at = new Date(input.nowMs).toISOString();
|
|
175
213
|
const base = await getHead(input.cwd);
|
|
214
|
+
const defaultBranch = await resolveDefaultBranch(input.cwd);
|
|
215
|
+
if (!defaultBranch) {
|
|
216
|
+
throw new FlowError("INTERNAL_STATE", "cannot bind: no default branch");
|
|
217
|
+
}
|
|
218
|
+
const target = normalizeTargetRef(defaultBranch.branch);
|
|
219
|
+
const loadedSettings = await loadKeiyakuSettings(input.cwd);
|
|
220
|
+
let bootstrapPlan;
|
|
221
|
+
if (workspace === "worktree") {
|
|
222
|
+
assertSettingsKnobUsable(loadedSettings, "worktreeBootstrap");
|
|
223
|
+
bootstrapPlan = resolvedBootstrapPlan(loadedSettings.knobs?.worktreeBootstrap);
|
|
224
|
+
}
|
|
176
225
|
const collidedIds = [];
|
|
177
226
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
178
227
|
const randomBytes = input.randomBytes?.(attempt) ?? randomBytesDefault();
|
|
@@ -189,9 +238,11 @@ export async function bindContract(input) {
|
|
|
189
238
|
actor: input.actor,
|
|
190
239
|
draft,
|
|
191
240
|
base,
|
|
241
|
+
target,
|
|
192
242
|
after: input.after ?? [],
|
|
193
243
|
workspace,
|
|
194
244
|
place,
|
|
245
|
+
bootstrap: bootstrapPlan,
|
|
195
246
|
});
|
|
196
247
|
const ref = activeLedgerRef(id);
|
|
197
248
|
const appended = await appendEntry(input.cwd, ref, entry, null);
|
|
@@ -203,6 +254,8 @@ export async function bindContract(input) {
|
|
|
203
254
|
const branch = `keiyaku/${id}`;
|
|
204
255
|
const worktreePath = await contractWorktreePathFromBind(input.cwd, id, entry);
|
|
205
256
|
const targetRoot = workspace === "here" ? input.cwd : worktreePath;
|
|
257
|
+
let ledgerHead = appended.head;
|
|
258
|
+
let bootstrapRecorded = false;
|
|
206
259
|
try {
|
|
207
260
|
const tree = await resolveTreeRoot(input.cwd, workspace, id, entry);
|
|
208
261
|
const registration = await registerScope(input.cwd, {
|
|
@@ -226,6 +279,16 @@ export async function bindContract(input) {
|
|
|
226
279
|
});
|
|
227
280
|
resources.worktreePath = worktreePath;
|
|
228
281
|
}
|
|
282
|
+
if (bootstrapPlan) {
|
|
283
|
+
input.onProgress?.(bootstrapProgressLine(bootstrapPlan.command));
|
|
284
|
+
const runner = input.bootstrapRunner ?? runWorktreeBootstrap;
|
|
285
|
+
const observation = await runner({ cwd: worktreePath, plan: bootstrapPlan }, input.bootstrapDependencies);
|
|
286
|
+
ledgerHead = await appendBootstrapEntry(input.cwd, id, input.actor, at, bootstrapEntryDataFromObservation(observation));
|
|
287
|
+
bootstrapRecorded = true;
|
|
288
|
+
if (!isBootstrapSuccess(observation)) {
|
|
289
|
+
throw new FlowError("INTERNAL_STATE", `BIND_BOOTSTRAP_FAILED: worktree bootstrap failed with ${observation.result.kind}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
229
292
|
const ledger = await readLedger(input.cwd, id);
|
|
230
293
|
if (!ledger)
|
|
231
294
|
throw new FlowError("INTERNAL_STATE", `LEDGER_MISSING: ledger missing immediately after bind for ${id}`);
|
|
@@ -239,17 +302,34 @@ export async function bindContract(input) {
|
|
|
239
302
|
branch: workspace === "worktree" ? branch : undefined,
|
|
240
303
|
worktreePath: targetRoot,
|
|
241
304
|
workspace,
|
|
242
|
-
ledgerHead
|
|
305
|
+
ledgerHead,
|
|
243
306
|
warnings: registration.warnings,
|
|
244
307
|
};
|
|
245
308
|
}
|
|
246
309
|
catch (error) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
310
|
+
// Bootstrap failure order is strict: bootstrap entry (already appended) →
|
|
311
|
+
// forfeit(bind-failed) → cleanup. Cleanup cold facts cannot convert success.
|
|
312
|
+
const cleanupFailures = [];
|
|
313
|
+
if (!bootstrapRecorded) {
|
|
314
|
+
// Non-bootstrap failure: cleanup first remains compatible with prior path,
|
|
315
|
+
// then forfeit. When bootstrap already recorded its failed fact, forfeit
|
|
316
|
+
// must precede cleanup.
|
|
317
|
+
cleanupFailures.push(...(await cleanupCreatedResources(input.cwd, resources)));
|
|
318
|
+
try {
|
|
319
|
+
await appendBindFailedForfeit(input.cwd, id, input.actor, at);
|
|
320
|
+
}
|
|
321
|
+
catch (forfeitError) {
|
|
322
|
+
cleanupFailures.push(forfeitError instanceof Error ? forfeitError.message : String(forfeitError));
|
|
323
|
+
}
|
|
250
324
|
}
|
|
251
|
-
|
|
252
|
-
|
|
325
|
+
else {
|
|
326
|
+
try {
|
|
327
|
+
await appendBindFailedForfeit(input.cwd, id, input.actor, at);
|
|
328
|
+
}
|
|
329
|
+
catch (forfeitError) {
|
|
330
|
+
cleanupFailures.push(forfeitError instanceof Error ? forfeitError.message : String(forfeitError));
|
|
331
|
+
}
|
|
332
|
+
cleanupFailures.push(...(await cleanupCreatedResources(input.cwd, resources)));
|
|
253
333
|
}
|
|
254
334
|
if (resources.registry) {
|
|
255
335
|
try {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { appendDebugLog } from "../telemetry/debug-log.js";
|
|
2
|
+
import { logWarn } from "../telemetry/logger.js";
|
|
3
|
+
import { runCall } from "./call.js";
|
|
4
|
+
import { persistResponseHistory } from "./transcripts.js";
|
|
5
|
+
import { isGitRepo } from "../git/branches.js";
|
|
6
|
+
import * as path from "node:path";
|
|
7
|
+
import * as fs from "node:fs/promises";
|
|
8
|
+
import { stableRepoRoot } from "./worktree-path.js";
|
|
9
|
+
function responsePathFromPrintingCwd(historyStorageCwd, responsePath) {
|
|
10
|
+
const printingCwd = process.cwd();
|
|
11
|
+
const absoluteHistoryCwd = path.resolve(printingCwd, historyStorageCwd);
|
|
12
|
+
return path.relative(printingCwd, path.resolve(absoluteHistoryCwd, responsePath));
|
|
13
|
+
}
|
|
14
|
+
export async function runCallAndPersist(input) {
|
|
15
|
+
const historyCandidate = input.repo ?? input.cwd;
|
|
16
|
+
const canonicalHistoryCandidate = await fs.realpath(historyCandidate);
|
|
17
|
+
const historyStorageCwd = (await isGitRepo(historyCandidate))
|
|
18
|
+
? await stableRepoRoot(historyCandidate)
|
|
19
|
+
: canonicalHistoryCandidate;
|
|
20
|
+
const historyDisplayCwd = path.resolve(historyCandidate, path.relative(canonicalHistoryCandidate, historyStorageCwd));
|
|
21
|
+
const result = await runCall(input);
|
|
22
|
+
if (input.incognito) {
|
|
23
|
+
return { result };
|
|
24
|
+
}
|
|
25
|
+
if (result.projectionOwnsResponsePersistence === true) {
|
|
26
|
+
return {
|
|
27
|
+
result,
|
|
28
|
+
...(result.responsePath ? { responsePath: responsePathFromPrintingCwd(historyDisplayCwd, result.responsePath) } : {}),
|
|
29
|
+
...(result.artifactId ? { artifactId: result.artifactId } : {}),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
let responsePath;
|
|
33
|
+
let artifactId;
|
|
34
|
+
try {
|
|
35
|
+
if (!result.evidenceCwd)
|
|
36
|
+
throw new Error("call response persistence is missing evidenceCwd");
|
|
37
|
+
const authority = result.address.repo.kind === "repository"
|
|
38
|
+
? { kind: "repository" }
|
|
39
|
+
: { kind: "user" };
|
|
40
|
+
const persisted = await persistResponseHistory({
|
|
41
|
+
cwd: historyStorageCwd,
|
|
42
|
+
tool: "call",
|
|
43
|
+
provenance: { authority, binding: result.address.binding, evidenceCwd: result.evidenceCwd },
|
|
44
|
+
title: input.title,
|
|
45
|
+
task: input.task,
|
|
46
|
+
context: input.context,
|
|
47
|
+
response: result.summary,
|
|
48
|
+
session: result.session,
|
|
49
|
+
subagentName: result.agent,
|
|
50
|
+
launchSnapshot: result.launchSnapshot,
|
|
51
|
+
effectivePolicy: result.meta?.effectivePolicy,
|
|
52
|
+
});
|
|
53
|
+
responsePath = persisted.responsePath;
|
|
54
|
+
artifactId = persisted.artifactId;
|
|
55
|
+
}
|
|
56
|
+
catch (historyError) {
|
|
57
|
+
const historyErrorMessage = historyError instanceof Error ? historyError.message : String(historyError);
|
|
58
|
+
logWarn(`Failed to persist call response: ${historyErrorMessage}`, { cwd: historyStorageCwd, section: "script" });
|
|
59
|
+
appendDebugLog(`tool call response persistence failed: ${historyErrorMessage}`, {
|
|
60
|
+
cwd: historyStorageCwd,
|
|
61
|
+
section: "script",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
result: artifactId ? { ...result, artifactId } : result,
|
|
66
|
+
...(responsePath ? { responsePath: responsePathFromPrintingCwd(historyDisplayCwd, responsePath) } : {}),
|
|
67
|
+
...(artifactId ? { artifactId } : {}),
|
|
68
|
+
};
|
|
69
|
+
}
|