@astrosheep/keiyaku 2.8.0 → 2.8.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/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms_v2.js +6 -2
- package/build/agents/harness/index.js +5 -1
- package/build/agents/harness/pump.js +22 -5
- package/build/agents/launch-snapshot_v2.js +14 -9
- package/build/agents/selector_v2.js +15 -3
- package/build/cli/commands/akuma.js +21 -3
- package/build/cli/completion.js +4 -1
- package/build/cli/flags.js +10 -1
- package/build/cli/help.js +5 -3
- package/build/cli/index.js +67 -14
- package/build/cli/parse.js +65 -24
- package/build/cli/projection-address.js +97 -10
- package/build/cli/render/address.js +12 -3
- package/build/cli/render/call.js +10 -5
- package/build/cli/render/shared.js +2 -2
- package/build/cli/render/status.js +19 -4
- package/build/cli/render/wait.js +3 -3
- package/build/config/provider-policy-ownership.js +39 -0
- package/build/config/settings.js +1 -11
- package/build/core/addressing.js +82 -38
- package/build/core/call.js +33 -5
- package/build/core/projection/mint.js +23 -3
- package/build/core/projection-alias.js +123 -0
- package/build/core/projection-coordinate.js +23 -7
- package/build/core/projection-core.js +8 -47
- package/build/core/projection-kill.js +26 -0
- package/build/core/projection-mint.js +24 -3
- package/build/core/projection-runner-lock.js +1 -1
- package/build/core/projection-status.js +111 -12
- package/build/core/projection-wake.js +3 -0
- package/build/core/response-artifact-id.js +5 -0
- package/build/core/status.js +1 -1
- package/build/core/transcripts.js +42 -15
- package/build/generated/version.js +1 -1
- package/package.json +4 -4
|
@@ -54,8 +54,12 @@ export function buildCallTermsV2(snapshot, prompt, cwd, options) {
|
|
|
54
54
|
? { config: snapshot.provider.config }
|
|
55
55
|
: {}),
|
|
56
56
|
...(snapshot.provider.executable !== undefined ? { executable: snapshot.provider.executable } : {}),
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
...(snapshot.provider.sandboxPolicy !== undefined
|
|
58
|
+
? { sandboxPolicy: snapshot.provider.sandboxPolicy }
|
|
59
|
+
: {}),
|
|
60
|
+
...(snapshot.provider.approvalPolicy !== undefined
|
|
61
|
+
? { approvalPolicy: snapshot.provider.approvalPolicy }
|
|
62
|
+
: {}),
|
|
59
63
|
developerInstructions: body || undefined,
|
|
60
64
|
onCheckpoint() { },
|
|
61
65
|
};
|
|
@@ -202,7 +202,11 @@ async function settleProjection(shared) {
|
|
|
202
202
|
? launchTellFence
|
|
203
203
|
: (() => { throw new FlowError("INTERNAL_STATE", "generation launch has an invalid tellFence"); })();
|
|
204
204
|
const launchSnapshot = shared.terms.projectionDir && shared.terms.projectionExecutionId
|
|
205
|
-
? snapshotReplayPrompt(shared.terms.projectionDir, shared.terms.prompt, tellFence, shared.terms.projectionExecutionId
|
|
205
|
+
? snapshotReplayPrompt(shared.terms.projectionDir, shared.terms.prompt, tellFence, shared.terms.projectionExecutionId,
|
|
206
|
+
// Session resume already holds launch history. Only fenced tells are
|
|
207
|
+
// new operator input; re-blasting the original prompt reasserts stale
|
|
208
|
+
// allowlists and breaks resting continuation.
|
|
209
|
+
{ sessionContinuation: shared.reviveTerms !== undefined })
|
|
206
210
|
: undefined;
|
|
207
211
|
const launchTerms = launchSnapshot
|
|
208
212
|
? shared.terms.provider === "codex-sdk" && launchSnapshot.effort
|
|
@@ -1,14 +1,31 @@
|
|
|
1
1
|
import { claimAllInbox, claimFencedTells, listTellIds, markTellSubmitted, markTellConsumed, readTellSubmitted, readTellOriginal, isTellCausallyConsumed, appendGenerationEvent, } from "../../core/projection-core.js";
|
|
2
2
|
import { prepareAgentEventForPersistence, } from "./execution-handle.js";
|
|
3
|
+
const OPERATOR_PROMPT_MARKER = "[Operator prompt]\n";
|
|
4
|
+
function frameTellBodies(projectionDirectory, tellIds) {
|
|
5
|
+
const originals = tellIds.map((tellId) => readTellOriginal(projectionDirectory, "inflight", tellId));
|
|
6
|
+
const frames = originals
|
|
7
|
+
.map((original, index) => `--- tell ${tellIds[index]} ---\n${original.text}`)
|
|
8
|
+
.join("\n\n");
|
|
9
|
+
const effort = originals.reduce((value, original) => original.effort ?? value, undefined);
|
|
10
|
+
return { frames, ...(effort ? { effort } : {}) };
|
|
11
|
+
}
|
|
12
|
+
function operatorBodyForContinuation(prompt, tellFrames, sessionContinuation) {
|
|
13
|
+
if (!sessionContinuation)
|
|
14
|
+
return `${prompt}\n\n${tellFrames}`;
|
|
15
|
+
const markerAt = prompt.indexOf(OPERATOR_PROMPT_MARKER);
|
|
16
|
+
if (markerAt >= 0) {
|
|
17
|
+
return `${prompt.slice(0, markerAt + OPERATOR_PROMPT_MARKER.length)}${tellFrames}`;
|
|
18
|
+
}
|
|
19
|
+
return tellFrames;
|
|
20
|
+
}
|
|
3
21
|
/** Tier-2 boundary snapshot. No tells leaves prompt byte-identical. */
|
|
4
|
-
export function snapshotReplayPrompt(projectionDirectory, prompt, tellFence, executionId) {
|
|
22
|
+
export function snapshotReplayPrompt(projectionDirectory, prompt, tellFence, executionId, options = {}) {
|
|
5
23
|
const tellIds = claimFencedTells(projectionDirectory, tellFence, executionId);
|
|
6
24
|
if (tellIds.length === 0)
|
|
7
25
|
return { prompt, tellIds };
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
return { prompt: `${prompt}\n\n${frames.join("\n\n")}`, tellIds, ...(effort ? { effort } : {}) };
|
|
26
|
+
const { frames, effort } = frameTellBodies(projectionDirectory, tellIds);
|
|
27
|
+
const nextPrompt = operatorBodyForContinuation(prompt, frames, options.sessionContinuation === true);
|
|
28
|
+
return { prompt: nextPrompt, tellIds, ...(effort ? { effort } : {}) };
|
|
12
29
|
}
|
|
13
30
|
/** Tier-2 has no steer acknowledgement: actual driver start is its ordinal-zero submission point. */
|
|
14
31
|
export function markReplayStarted(projectionDirectory, executionId, tellIds) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { nativePolicyOwnership } from "../config/provider-policy-ownership.js";
|
|
2
3
|
import { FlowError } from "../flow-error.js";
|
|
3
|
-
import { buildCodexAppServerWorkspaceWriteSandboxPolicy, } from "./providers/codex-app-server.js";
|
|
4
|
+
import { buildCodexAppServerWorkspaceWriteSandboxPolicy, configOwnsApprovalPolicy, configOwnsSandboxPolicy, } from "./providers/codex-app-server.js";
|
|
4
5
|
const ACCESS_VALUES = new Set(["read", "write", "auto"]);
|
|
5
6
|
const NETWORK_VALUES = new Set(["disabled", "enabled"]);
|
|
6
7
|
const WEB_SEARCH_VALUES = new Set(["disabled", "cached", "live"]);
|
|
@@ -198,16 +199,15 @@ function parseProvider(value) {
|
|
|
198
199
|
}
|
|
199
200
|
case "codex-app-server": {
|
|
200
201
|
exactKeys(provider, ["name", "source", "kind", "executable", "profile", "config", "model", "effort", "sandboxPolicy", "approvalPolicy"], "provider");
|
|
201
|
-
if (
|
|
202
|
-
throw snapshotError("provider.sandboxPolicy", "is required");
|
|
203
|
-
if (provider.approvalPolicy !== "never")
|
|
202
|
+
if ("approvalPolicy" in provider && provider.approvalPolicy !== "never")
|
|
204
203
|
throw snapshotError("provider.approvalPolicy", "must equal 'never'");
|
|
205
204
|
const executable = optionalString(provider, "executable", "provider");
|
|
206
205
|
const profile = optionalString(provider, "profile", "provider");
|
|
207
206
|
const config = optionalJsonObject(provider, "config");
|
|
208
207
|
const model = optionalString(provider, "model", "provider");
|
|
209
208
|
const effort = optionalString(provider, "effort", "provider");
|
|
210
|
-
|
|
209
|
+
const sandboxPolicy = "sandboxPolicy" in provider ? parseSandboxPolicy(provider.sandboxPolicy) : undefined;
|
|
210
|
+
return Object.freeze({ ...common, kind, ...(executable !== undefined ? { executable } : {}), ...(profile !== undefined ? { profile } : {}), ...(config !== undefined ? { config } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(sandboxPolicy !== undefined ? { sandboxPolicy } : {}), ...(provider.approvalPolicy === "never" ? { approvalPolicy: "never" } : {}) });
|
|
211
211
|
}
|
|
212
212
|
case "claude-agent-sdk": {
|
|
213
213
|
exactKeys(provider, ["name", "source", "kind", "executable", "model", "effort", "settingSources", "permissionMode", "allowDangerouslySkipPermissions", "disallowedTools"], "provider");
|
|
@@ -295,9 +295,14 @@ export function resolveAkumaLaunchSnapshot(selected, input) {
|
|
|
295
295
|
switch (instance.kind) {
|
|
296
296
|
case "codex-sdk": {
|
|
297
297
|
const threadOptions = { ...(instance.threadOptions ?? {}) };
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
298
|
+
const ownership = nativePolicyOwnership(selected.profile.provider, instance);
|
|
299
|
+
if (ownership.access.length === 0) {
|
|
300
|
+
threadOptions.sandboxMode = selected.profile.access === "read" ? "read-only" : selected.profile.access === "auto" ? "danger-full-access" : "workspace-write";
|
|
301
|
+
}
|
|
302
|
+
if (ownership.network.length === 0)
|
|
303
|
+
threadOptions.networkAccessEnabled = selected.profile.network === "enabled";
|
|
304
|
+
if (ownership.webSearch.length === 0)
|
|
305
|
+
threadOptions.webSearchMode = selected.profile.webSearch ?? "disabled";
|
|
301
306
|
if (model !== undefined)
|
|
302
307
|
threadOptions.model = model;
|
|
303
308
|
if (effort !== undefined)
|
|
@@ -310,7 +315,7 @@ export function resolveAkumaLaunchSnapshot(selected, input) {
|
|
|
310
315
|
case "codex-app-server": {
|
|
311
316
|
const roots = [path.resolve(input.executionCwd), ...(instance.writableRoots ?? []).map((root) => path.resolve(input.projectRoot, root))];
|
|
312
317
|
const writableRoots = [...new Set(roots)];
|
|
313
|
-
provider = { ...common, ...(instance.executable !== undefined ? { executable: instance.executable } : {}), ...(instance.profile !== undefined ? { profile: instance.profile } : {}), ...(instance.config !== undefined ? { config: instance.config } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), sandboxPolicy: buildCodexAppServerWorkspaceWriteSandboxPolicy({ writableRoots, networkAccess: selected.profile.network === "enabled" }), approvalPolicy: "never" };
|
|
318
|
+
provider = { ...common, ...(instance.executable !== undefined ? { executable: instance.executable } : {}), ...(instance.profile !== undefined ? { profile: instance.profile } : {}), ...(instance.config !== undefined ? { config: instance.config } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(!configOwnsSandboxPolicy(instance.config) ? { sandboxPolicy: buildCodexAppServerWorkspaceWriteSandboxPolicy({ writableRoots, networkAccess: selected.profile.network === "enabled" }) } : {}), ...(!configOwnsApprovalPolicy(instance.config) ? { approvalPolicy: "never" } : {}) };
|
|
314
319
|
break;
|
|
315
320
|
}
|
|
316
321
|
case "claude-agent-sdk": {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getConfig } from "../config/env.js";
|
|
2
2
|
import { loadAkumaCatalogV2, } from "../config/akuma-loader_v2.js";
|
|
3
3
|
import { assertSettingsKnobUsable, loadKeiyakuSettings, resolveDefaultAgentName, } from "../config/settings.js";
|
|
4
|
+
import { nativePolicyOwnership } from "../config/provider-policy-ownership.js";
|
|
4
5
|
import { FlowError } from "../flow-error.js";
|
|
5
6
|
const ACCESS_VALUES = ["read", "write", "auto"];
|
|
6
7
|
const NETWORK_VALUES = ["disabled", "enabled"];
|
|
@@ -8,7 +9,7 @@ const SUPPORTED_EXPLICIT = {
|
|
|
8
9
|
"codex-sdk": {
|
|
9
10
|
access: new Set(ACCESS_VALUES),
|
|
10
11
|
network: new Set(NETWORK_VALUES),
|
|
11
|
-
webSearch: new Set(["disabled"]),
|
|
12
|
+
webSearch: new Set(["disabled", "cached", "live"]),
|
|
12
13
|
},
|
|
13
14
|
"codex-app-server": {
|
|
14
15
|
access: new Set(["write"]),
|
|
@@ -56,6 +57,15 @@ export function assertPortablePolicySupported(adapter, policy) {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
}
|
|
60
|
+
function assertPortablePolicyOwnership(akumaName, instanceName, instance, policy) {
|
|
61
|
+
const ownership = nativePolicyOwnership(instanceName, instance);
|
|
62
|
+
for (const axis of ["access", "network", "webSearch"]) {
|
|
63
|
+
if (policy[axis] === undefined || ownership[axis].length === 0)
|
|
64
|
+
continue;
|
|
65
|
+
const settingsPaths = ownership[axis].map((owner) => `settings.${owner.path}`).join(", ");
|
|
66
|
+
throw new FlowError("INVALID_SETTINGS", `Akuma '${akumaName}' declares frontmatter.${axis}, but provider instance '${instanceName}' also owns that policy at ${settingsPaths}. Remove one declaration.`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
59
69
|
function isValidAkumaCandidate(candidate) {
|
|
60
70
|
return !("reason" in candidate && candidate.reason !== undefined) && candidate.profile !== undefined && candidate.body !== undefined;
|
|
61
71
|
}
|
|
@@ -96,11 +106,13 @@ export async function selectSubagentV2(agentName, cwd) {
|
|
|
96
106
|
}
|
|
97
107
|
const providerInstance = providerCandidate.instance;
|
|
98
108
|
const adapter = providerInstance.kind;
|
|
99
|
-
|
|
109
|
+
const portablePolicy = {
|
|
100
110
|
...(candidate.profile.access !== undefined ? { access: candidate.profile.access } : {}),
|
|
101
111
|
...(candidate.profile.network !== undefined ? { network: candidate.profile.network } : {}),
|
|
102
112
|
...(candidate.profile.webSearch !== undefined ? { webSearch: candidate.profile.webSearch } : {}),
|
|
103
|
-
}
|
|
113
|
+
};
|
|
114
|
+
assertPortablePolicyOwnership(selected, instanceName, providerInstance, portablePolicy);
|
|
115
|
+
assertPortablePolicySupported(adapter, portablePolicy);
|
|
104
116
|
const profile = freezeResolvedProfile({
|
|
105
117
|
provider: instanceName,
|
|
106
118
|
adapter,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveDefaultAgentName, resolveReviewerAgentName, } from "../../config/settings.js";
|
|
2
2
|
import { FlowError } from "../../flow-error.js";
|
|
3
|
+
import { nativePolicyOwnership } from "../../config/provider-policy-ownership.js";
|
|
3
4
|
function formatRoleMarkers(settings, name) {
|
|
4
5
|
const markers = [];
|
|
5
6
|
if (name === resolveDefaultAgentName(settings))
|
|
@@ -81,6 +82,25 @@ function appendPromptPreview(lines, body) {
|
|
|
81
82
|
lines.push(...promptLines.slice(0, 8).map((line) => ` ${line}`));
|
|
82
83
|
lines.push(`(${promptLines.length} lines total)`);
|
|
83
84
|
}
|
|
85
|
+
function appendPolicyLines(lines, resolved) {
|
|
86
|
+
const ownership = nativePolicyOwnership(resolved.instanceName, resolved.instance);
|
|
87
|
+
const axes = ["access", "network", "webSearch"];
|
|
88
|
+
for (const axis of axes) {
|
|
89
|
+
const value = resolved.profile[axis];
|
|
90
|
+
if (value !== undefined) {
|
|
91
|
+
lines.push(`${axis}: ${value} [source: akuma frontmatter.${axis}]`);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const owners = ownership[axis];
|
|
95
|
+
if (owners.length === 0)
|
|
96
|
+
continue;
|
|
97
|
+
if (axis === "webSearch") {
|
|
98
|
+
lines.push(`webSearch: ${JSON.stringify(owners[0].value)} [source: instance config ${owners[0].path}]`);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
lines.push(`${axis}: native [source: instance config ${owners.map((owner) => owner.path).join(", ")}]`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
84
104
|
function chooseCallHintName(validNames, settings) {
|
|
85
105
|
if (validNames.length === 0)
|
|
86
106
|
return null;
|
|
@@ -149,9 +169,7 @@ export function renderAkumaShow(catalog, providerInstances, settings, name) {
|
|
|
149
169
|
appendOptionalLine(lines, "model", resolved.profile.model);
|
|
150
170
|
appendOptionalLine(lines, "effort", resolved.profile.effort);
|
|
151
171
|
appendOptionalLine(lines, "effortOptions", resolved.profile.effortOptions);
|
|
152
|
-
|
|
153
|
-
appendOptionalLine(lines, "network", resolved.profile.network);
|
|
154
|
-
appendOptionalLine(lines, "webSearch", resolved.profile.webSearch);
|
|
172
|
+
appendPolicyLines(lines, resolved);
|
|
155
173
|
appendPromptPreview(lines, resolved.body);
|
|
156
174
|
return lines.join("\n");
|
|
157
175
|
}
|
package/build/cli/completion.js
CHANGED
|
@@ -35,7 +35,10 @@ export async function renderCompletionCandidates(input) {
|
|
|
35
35
|
if (word.startsWith("@")) {
|
|
36
36
|
return (await contractAddressCandidates(input.cwd, word)).join("\n");
|
|
37
37
|
}
|
|
38
|
-
if (input.previous === "call"
|
|
38
|
+
if (input.previous === "call") {
|
|
39
|
+
return filterPrefix([...(await akumaCandidates(input.cwd, word)), "--alias"], word).join("\n");
|
|
40
|
+
}
|
|
41
|
+
if (input.previous === "show") {
|
|
39
42
|
return (await akumaCandidates(input.cwd, word)).join("\n");
|
|
40
43
|
}
|
|
41
44
|
if (input.previous === "akuma") {
|
package/build/cli/flags.js
CHANGED
|
@@ -34,7 +34,12 @@ export const FLAG_SPECS = {
|
|
|
34
34
|
projection: {
|
|
35
35
|
kind: "value",
|
|
36
36
|
placeholder: "ID",
|
|
37
|
-
description: "Akuma projection
|
|
37
|
+
description: "Akuma projection address.",
|
|
38
|
+
},
|
|
39
|
+
alias: {
|
|
40
|
+
kind: "value",
|
|
41
|
+
placeholder: "WORD",
|
|
42
|
+
description: "Move a projection alias to this newly launched call.",
|
|
38
43
|
},
|
|
39
44
|
timeout: {
|
|
40
45
|
kind: "value",
|
|
@@ -108,6 +113,10 @@ export const FLAG_SPECS = {
|
|
|
108
113
|
placeholder: "REF",
|
|
109
114
|
description: "Filter target-keyed status facts by branch name or full refs/heads/<name>.",
|
|
110
115
|
},
|
|
116
|
+
all: {
|
|
117
|
+
kind: "boolean",
|
|
118
|
+
description: "Show all retained Akuma projections while preserving status priority order.",
|
|
119
|
+
},
|
|
111
120
|
};
|
|
112
121
|
export const CONTROL_FLAGS = new Set(Object.keys(FLAG_SPECS));
|
|
113
122
|
export const VALUE_FLAGS = new Set(Object.entries(FLAG_SPECS)
|
package/build/cli/help.js
CHANGED
|
@@ -10,10 +10,11 @@ export const CLI_COMMAND_METADATA = {
|
|
|
10
10
|
input: ["# <arc title>", "", "## Objective", "<what this arc should accomplish>", "", "## Brief", "<scope, constraints, relevant files, instructions>"],
|
|
11
11
|
notes: ["An empty seal is valid.", "The command does not create delivery commits."],
|
|
12
12
|
}),
|
|
13
|
-
call: command("call", "Start an Akuma helper session", ["keiyaku call NAME [prompt|-]", "keiyaku call NAME --detach [prompt|-]", "keiyaku @addr call NAME [prompt|-]", "keiyaku call NAME --contract ADDR [prompt|-]"], "none", ["cwd", "repo", "contract", "akuma", "model", "effort", "incognito", "bare", "detach"], "Run a scoped helper task."),
|
|
13
|
+
call: command("call", "Start an Akuma helper session", ["keiyaku call NAME [--alias WORD] [prompt|-]", "keiyaku call NAME --detach [prompt|-]", "keiyaku @addr call NAME [prompt|-]", "keiyaku call NAME --contract ADDR [prompt|-]"], "none", ["cwd", "repo", "contract", "akuma", "model", "effort", "incognito", "bare", "detach", "alias"], "Run a scoped helper task."),
|
|
14
14
|
revive: command("revive", "Revive an Akuma helper session", ["keiyaku revive ARTIFACT_ID"], "none", ["cwd", "repo", "model", "effort"], "Continue a persisted helper session."),
|
|
15
|
-
wait: command("wait", "Wait for an akuma projection", ["keiyaku wait <
|
|
15
|
+
wait: command("wait", "Wait for an akuma projection", ["keiyaku wait <projection-address> [--timeout DURATION]", "keiyaku @addr wait <projection-address> [--timeout DURATION]"], "none", ["cwd", "repo", "contract", "timeout"], "Read an akuma projection until its outcome is available, or show a timeout snapshot."),
|
|
16
16
|
tell: command("tell", "Send intent to a projection mailbox", ["keiyaku tell [--effort LEVEL] PROJECTION MESSAGE", "keiyaku @addr tell [--effort LEVEL] PROJECTION MESSAGE"], "none", ["cwd", "repo", "contract", "effort"], "Write intent to the mailbox and wake only when eligible."),
|
|
17
|
+
kill: command("kill", "Stop the current projection generation", ["keiyaku kill <projection-address>", "keiyaku @addr kill <projection-address>"], "none", ["cwd", "repo", "contract"], "Terminate the current generation while keeping the projection identity and tell ledger."),
|
|
17
18
|
log: command("log", "Render a contract ledger", ["keiyaku log [-C|--cwd DIR] [--repo DIR] [--contract ADDR]", "keiyaku @addr log [-C|--cwd DIR] [--repo DIR]"], "none", ["cwd", "repo", "contract"], "Read a contract ledger."),
|
|
18
19
|
akuma: command("akuma", "Inspect Akuma profiles", ["keiyaku akuma list|show"], "none", [], "Inspect configured helper profiles.", {
|
|
19
20
|
subcommands: ["list", "ls", "show"],
|
|
@@ -31,7 +32,7 @@ export const CLI_COMMAND_METADATA = {
|
|
|
31
32
|
forfeit: command("forfeit", "Forfeit an addressed contract", ["keiyaku forfeit [-C|--cwd DIR] [--repo DIR] [--contract ADDR] [--reason TEXT]", "keiyaku @addr forfeit [-C|--cwd DIR] [--repo DIR] [--reason TEXT]"], "none", ["cwd", "repo", "contract", "reason"], "Record an abandoned contract without sealing it."),
|
|
32
33
|
guide: command("guide", "Print the Keiyaku guide", ["keiyaku guide"], "none", [], "Print workflow guidance."),
|
|
33
34
|
completion: command("completion", "Print shell completion", ["keiyaku completion [--shell bash|zsh]"], "none", ["cwd", "repo", "shell", "word", "previous", "complete"], "Print command and address candidates."),
|
|
34
|
-
status: command("status", "Show the Kanshi board", ["keiyaku status [-C|--cwd DIR] [--repo DIR] [--target REF]"], "none", ["cwd", "repo", "target"], "Read contract, queue, and projection facts without mutation."),
|
|
35
|
+
status: command("status", "Show the Kanshi board", ["keiyaku status [-C|--cwd DIR] [--repo DIR] [--target REF] [--all]"], "none", ["cwd", "repo", "target", "all"], "Read contract, queue, and projection facts without mutation."),
|
|
35
36
|
"dump-env": command("dump-env", "Print the environment template", ["keiyaku dump-env"], "none", [], "Print supported environment keys.", { notes: ["Keiyaku loads <KEIYAKU_HOME>/.env, then .keiyaku/.env; process environment values win."] }),
|
|
36
37
|
};
|
|
37
38
|
function indent(lines) {
|
|
@@ -84,6 +85,7 @@ export function renderCliHelp(version) {
|
|
|
84
85
|
" wait Wait for an akuma projection.",
|
|
85
86
|
" revive Revive an Akuma helper session.",
|
|
86
87
|
" tell Send intent to an Akuma projection.",
|
|
88
|
+
" kill Stop the current Akuma generation.",
|
|
87
89
|
" log Render a contract ledger.",
|
|
88
90
|
" akuma Inspect helper profiles.",
|
|
89
91
|
" skills Install bundled official skills.",
|
package/build/cli/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { amendKeiyaku } from "../core/amend.js";
|
|
|
6
6
|
import { arcKeiyaku, buildArcInput } from "../core/arc.js";
|
|
7
7
|
import { bindContract } from "../core/bind.js";
|
|
8
8
|
import { buildAmendInput, defaultActorClock } from "../core/command-io.js";
|
|
9
|
-
import { AddressResolutionError, buildResolutionAttempt, resolveContractAddress, withResolvedAddress, } from "../core/addressing.js";
|
|
9
|
+
import { AddressResolutionError, buildArtifactResolutionAttempt, buildResolutionAttempt, resolveContractAddress, withResolvedAddress, } from "../core/addressing.js";
|
|
10
10
|
import { forfeitContractCommand } from "../core/forfeit.js";
|
|
11
11
|
import { readContractLog } from "../core/log.js";
|
|
12
12
|
import { buildPetitionClaimInput } from "../core/petition.js";
|
|
@@ -14,6 +14,7 @@ import { petitionKeiyaku } from "../core/petition-run.js";
|
|
|
14
14
|
import { renewKeiyaku } from "../core/renew.js";
|
|
15
15
|
import { normalizeTargetRef, readKanshiBoard } from "../core/status.js";
|
|
16
16
|
import { runCallAndPersist } from "../core/call-persist.js";
|
|
17
|
+
import { isResponseArtifactId } from "../core/response-artifact-id.js";
|
|
17
18
|
import { locateResponseArtifact } from "../core/transcripts.js";
|
|
18
19
|
import { renderEnvExample } from "../config/env-keys.js";
|
|
19
20
|
import { assertSettingsKnobUsable, loadKeiyakuSettings } from "../config/settings.js";
|
|
@@ -34,6 +35,7 @@ import { buildBindDraftResponse, buildBindResponse, buildForfeitResponse, format
|
|
|
34
35
|
import { buildCallResponse, renderAdoptedLaunchReceipt } from "./render/call.js";
|
|
35
36
|
import { projectionDir, writeInboxTell } from "../core/projection-core.js";
|
|
36
37
|
import { assertTellEffortSupported, tellProjection } from "../core/projection-wake.js";
|
|
38
|
+
import { killProjectionGeneration } from "../core/projection-kill.js";
|
|
37
39
|
import { resolveProjectionAddress } from "./projection-address.js";
|
|
38
40
|
import { waitForProjection } from "../core/projection-wait.js";
|
|
39
41
|
import { buildWaitResponse } from "./render/wait.js";
|
|
@@ -41,6 +43,7 @@ import { prependAddressReceipt, renderResolvedAddressReceipt } from "./render/ad
|
|
|
41
43
|
import { installOfficialSkills } from "./skills-install.js";
|
|
42
44
|
import { isGitRepo } from "../git/branches.js";
|
|
43
45
|
import { stableRepoRoot } from "../core/worktree-path.js";
|
|
46
|
+
import { assertProjectionAlias } from "../core/projection-alias.js";
|
|
44
47
|
const CALL_INPUT_HINTS = [
|
|
45
48
|
"Call input can be a prompt argument, `-` to read stdin, or omitted to read stdin.",
|
|
46
49
|
"Use `keiyaku call NAME \"prompt\"`, `keiyaku call NAME - < prompt.md`, or `keiyaku revive ARTIFACT_ID`.",
|
|
@@ -79,6 +82,7 @@ function buildCallInput(content, flags, mode = "call") {
|
|
|
79
82
|
incognito: flags.incognito,
|
|
80
83
|
bare: flags.bare,
|
|
81
84
|
detach: flags.detach,
|
|
85
|
+
...(mode === "call" && flags.alias !== undefined ? { alias: assertProjectionAlias(flags.alias) } : {}),
|
|
82
86
|
};
|
|
83
87
|
}
|
|
84
88
|
function revivePrompt(stdin) {
|
|
@@ -96,7 +100,7 @@ async function repositoryDirectory(flags) {
|
|
|
96
100
|
if (flags.contractId) {
|
|
97
101
|
const asTyped = flags.contractAddressSource === "at-address"
|
|
98
102
|
? `@${flags.contractId}`
|
|
99
|
-
: flags.contractId
|
|
103
|
+
: `--contract ${flags.contractId}`;
|
|
100
104
|
throw new AddressResolutionError("EMPTY_PARAM", `${asTyped} is a commission address, but ${candidate} has no repository ledger; contract addressing requires a ledger`, buildResolutionAttempt({
|
|
101
105
|
repo: { kind: "user" },
|
|
102
106
|
workdir: effectiveDirectory(flags),
|
|
@@ -142,13 +146,13 @@ const CLI_COMMAND_SPECS = {
|
|
|
142
146
|
...(!flags.detach
|
|
143
147
|
? {
|
|
144
148
|
onLaunch: ({ address, projection }) => {
|
|
145
|
-
process.stdout.write(`${renderAdoptedLaunchReceipt(address, projection.id)}\n`);
|
|
149
|
+
process.stdout.write(`${renderAdoptedLaunchReceipt(address, projection.id, projection.alias, projection.previousAliasTarget)}\n`);
|
|
146
150
|
},
|
|
147
151
|
}
|
|
148
152
|
: {}),
|
|
149
153
|
});
|
|
150
154
|
if (outcome.result.foregroundWait && outcome.result.projection) {
|
|
151
|
-
return buildWaitResponse(outcome.result.projection.id, outcome.result.foregroundWait);
|
|
155
|
+
return buildWaitResponse(outcome.result.projection.id, outcome.result.foregroundWait, { alias: outcome.result.projection.alias });
|
|
152
156
|
}
|
|
153
157
|
return buildCallResponse(outcome.result, {
|
|
154
158
|
responsePath: outcome.responsePath,
|
|
@@ -160,8 +164,20 @@ const CLI_COMMAND_SPECS = {
|
|
|
160
164
|
const cwd = effectiveDirectory(flags);
|
|
161
165
|
const artifactCwd = await projectDirectory(flags);
|
|
162
166
|
const artifactId = positional[0];
|
|
163
|
-
const
|
|
164
|
-
const
|
|
167
|
+
const isRepo = await isGitRepo(artifactCwd);
|
|
168
|
+
const responseStoreCwd = isRepo ? await stableRepoRoot(artifactCwd) : await fs.realpath(artifactCwd);
|
|
169
|
+
const artifact = await locateResponseArtifact({
|
|
170
|
+
cwd: responseStoreCwd,
|
|
171
|
+
artifactId,
|
|
172
|
+
attempt: buildArtifactResolutionAttempt({
|
|
173
|
+
repo: isRepo
|
|
174
|
+
? { kind: "repository", stableRoot: responseStoreCwd }
|
|
175
|
+
: { kind: "user" },
|
|
176
|
+
workdir: cwd,
|
|
177
|
+
supplied: artifactId,
|
|
178
|
+
inference: ["response artifact identity lookup"],
|
|
179
|
+
}),
|
|
180
|
+
});
|
|
165
181
|
let executionCwd;
|
|
166
182
|
let repo;
|
|
167
183
|
if (artifact.provenance.authority.kind === "repository") {
|
|
@@ -186,20 +202,30 @@ const CLI_COMMAND_SPECS = {
|
|
|
186
202
|
const cwd = flags.contractId ? await repositoryDirectory(flags) : await projectDirectory(flags);
|
|
187
203
|
const address = flags.contractId ? await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource) : undefined;
|
|
188
204
|
const operation = async () => {
|
|
189
|
-
const
|
|
190
|
-
|
|
205
|
+
const projection = await resolveProjectionAddress({
|
|
206
|
+
cwd,
|
|
207
|
+
address: positional[0],
|
|
208
|
+
scope: address,
|
|
209
|
+
verb: "wait",
|
|
210
|
+
});
|
|
211
|
+
return prependAddressReceipt(buildWaitResponse(projection.id, await waitForProjection({ projectionDirectory: projectionDir(cwd, projection.id), timeoutMs: flags.timeoutMs, signal }), { alias: projection.alias }), address, { projectionId: projection.id, projectionAlias: projection.alias });
|
|
191
212
|
};
|
|
192
213
|
return address ? await withResolvedAddress(address, operation) : await operation();
|
|
193
214
|
}),
|
|
194
215
|
tell: commandSpec("tell", async (_stdin, flags, _signal, positional) => {
|
|
195
216
|
const cwd = flags.contractId ? await repositoryDirectory(flags) : await projectDirectory(flags);
|
|
196
|
-
const [
|
|
197
|
-
if (!
|
|
198
|
-
throw new FlowError("EMPTY_PARAM", "tell requires a projection and message");
|
|
217
|
+
const [projectionAddress, message] = positional;
|
|
218
|
+
if (!projectionAddress || !message)
|
|
219
|
+
throw new FlowError("EMPTY_PARAM", "tell requires a projection address and message");
|
|
199
220
|
const address = flags.contractId ? await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource) : undefined;
|
|
200
221
|
const operation = async () => {
|
|
201
|
-
const
|
|
202
|
-
|
|
222
|
+
const projection = await resolveProjectionAddress({
|
|
223
|
+
cwd,
|
|
224
|
+
address: projectionAddress,
|
|
225
|
+
scope: address,
|
|
226
|
+
verb: "tell",
|
|
227
|
+
});
|
|
228
|
+
const directory = projectionDir(cwd, projection.id);
|
|
203
229
|
assertTellEffortSupported(directory, flags.effort);
|
|
204
230
|
// Mailbox first: do not inspect phase before this immutable write.
|
|
205
231
|
writeInboxTell(directory, message, Date.now(), { effort: flags.effort });
|
|
@@ -208,6 +234,27 @@ const CLI_COMMAND_SPECS = {
|
|
|
208
234
|
};
|
|
209
235
|
return address ? await withResolvedAddress(address, operation) : await operation();
|
|
210
236
|
}),
|
|
237
|
+
kill: commandSpec("kill", async (_stdin, flags, _signal, positional) => {
|
|
238
|
+
const cwd = flags.contractId ? await repositoryDirectory(flags) : await projectDirectory(flags);
|
|
239
|
+
const [projectionAddress] = positional;
|
|
240
|
+
if (!projectionAddress)
|
|
241
|
+
throw new FlowError("EMPTY_PARAM", "kill requires a projection address");
|
|
242
|
+
const address = flags.contractId ? await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource) : undefined;
|
|
243
|
+
const operation = async () => {
|
|
244
|
+
const projection = await resolveProjectionAddress({
|
|
245
|
+
cwd,
|
|
246
|
+
address: projectionAddress,
|
|
247
|
+
scope: address,
|
|
248
|
+
verb: "kill",
|
|
249
|
+
});
|
|
250
|
+
const result = killProjectionGeneration(projectionDir(cwd, projection.id));
|
|
251
|
+
const line = result.status === "killed"
|
|
252
|
+
? `× killed ${projection.id}`
|
|
253
|
+
: `· already resting ${projection.id}`;
|
|
254
|
+
return textResponse(line);
|
|
255
|
+
};
|
|
256
|
+
return address ? await withResolvedAddress(address, operation) : await operation();
|
|
257
|
+
}),
|
|
211
258
|
log: commandSpec("log", async (_stdin, flags) => {
|
|
212
259
|
const cwd = await repositoryDirectory(flags);
|
|
213
260
|
const address = await resolveContractAddress(cwd, flags.contractId, flags.contractAddressSource);
|
|
@@ -232,6 +279,9 @@ const CLI_COMMAND_SPECS = {
|
|
|
232
279
|
if (!name) {
|
|
233
280
|
throw new FlowError("EMPTY_PARAM", "akuma show requires an Akuma name");
|
|
234
281
|
}
|
|
282
|
+
if (isResponseArtifactId(name)) {
|
|
283
|
+
throw new FlowError("EMPTY_PARAM", `artifact ${name} is a response artifact; akuma show expects an Akuma profile name`);
|
|
284
|
+
}
|
|
235
285
|
const cwd = await projectDirectory(flags);
|
|
236
286
|
const loaded = await loadKeiyakuSettings(cwd);
|
|
237
287
|
// Role markers need settings pointers; catalog consumers keep legacy agents disease actionable.
|
|
@@ -288,7 +338,10 @@ const CLI_COMMAND_SPECS = {
|
|
|
288
338
|
// honors --repo as a repository coordinate when the operator names one.
|
|
289
339
|
const cwd = await projectDirectory(flags);
|
|
290
340
|
const target = flags.target === undefined ? undefined : normalizeTargetRef(flags.target);
|
|
291
|
-
return textResponse(renderStatusBoard(await readKanshiBoard(cwd, Date.now(),
|
|
341
|
+
return textResponse(renderStatusBoard(await readKanshiBoard(cwd, Date.now(), {
|
|
342
|
+
...(target === undefined ? {} : { target }),
|
|
343
|
+
showAllProjections: flags.all === true,
|
|
344
|
+
})));
|
|
292
345
|
}),
|
|
293
346
|
"dump-env": commandSpec("dump-env", async () => textResponse(renderEnvExample())),
|
|
294
347
|
};
|