@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
package/build/cli/parse.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FlowError } from "../flow-error.js";
|
|
2
2
|
import { agentNameSchema } from "../config/settings.js";
|
|
3
|
+
import { isResponseArtifactId } from "../core/response-artifact-id.js";
|
|
3
4
|
import { BOOLEAN_FLAGS, CONTROL_FLAGS, HELP_FLAGS, SHORT_FLAG_ALIASES, VALUE_FLAGS, } from "./flags.js";
|
|
4
5
|
/** Commands that may carry an explicit commission selector (@ or --contract). */
|
|
5
6
|
const COMMISSION_SELECTOR_COMMANDS = new Set([
|
|
@@ -8,6 +9,7 @@ const COMMISSION_SELECTOR_COMMANDS = new Set([
|
|
|
8
9
|
"call",
|
|
9
10
|
"wait",
|
|
10
11
|
"tell",
|
|
12
|
+
"kill",
|
|
11
13
|
"log",
|
|
12
14
|
"renew",
|
|
13
15
|
"petition",
|
|
@@ -20,6 +22,7 @@ const CLI_COMMAND_ORDER = [
|
|
|
20
22
|
"revive",
|
|
21
23
|
"wait",
|
|
22
24
|
"tell",
|
|
25
|
+
"kill",
|
|
23
26
|
"log",
|
|
24
27
|
"akuma list",
|
|
25
28
|
"akuma ls",
|
|
@@ -40,10 +43,11 @@ const COMMAND_FLAGS = {
|
|
|
40
43
|
// contract is parseable on bind only so the mouth can reject it with bind-specific copy.
|
|
41
44
|
bind: ["cwd", "repo", "contract", "after", "exclusive", "draft-only"],
|
|
42
45
|
arc: ["cwd", "repo", "contract", "waive"],
|
|
43
|
-
call: ["cwd", "repo", "contract", "akuma", "model", "effort", "incognito", "bare", "detach"],
|
|
46
|
+
call: ["cwd", "repo", "contract", "akuma", "model", "effort", "incognito", "bare", "detach", "alias"],
|
|
44
47
|
revive: ["cwd", "repo", "model", "effort"],
|
|
45
48
|
wait: ["cwd", "repo", "contract", "timeout"],
|
|
46
49
|
tell: ["cwd", "repo", "contract", "effort"],
|
|
50
|
+
kill: ["cwd", "repo", "contract"],
|
|
47
51
|
log: ["cwd", "repo", "contract"],
|
|
48
52
|
akuma: [],
|
|
49
53
|
"akuma ls": ["cwd", "repo"],
|
|
@@ -57,7 +61,7 @@ const COMMAND_FLAGS = {
|
|
|
57
61
|
forfeit: ["cwd", "repo", "contract", "reason"],
|
|
58
62
|
guide: [],
|
|
59
63
|
completion: ["cwd", "repo", "shell", "word", "previous", "complete"],
|
|
60
|
-
status: ["cwd", "repo", "target"],
|
|
64
|
+
status: ["cwd", "repo", "target", "all"],
|
|
61
65
|
"dump-env": [],
|
|
62
66
|
};
|
|
63
67
|
const STDIN_COMMANDS = new Set(["bind", "arc", "amend"]);
|
|
@@ -95,9 +99,11 @@ export function commandPositionalRange(command) {
|
|
|
95
99
|
if (command === "revive")
|
|
96
100
|
return { min: 1, max: 1, label: "<artifact-id>" };
|
|
97
101
|
if (command === "wait")
|
|
98
|
-
return { min: 1, max: 1, label: "<
|
|
102
|
+
return { min: 1, max: 1, label: "<projection-address>" };
|
|
99
103
|
if (command === "tell")
|
|
100
104
|
return { min: 2, max: 2, label: "<projection> <message>" };
|
|
105
|
+
if (command === "kill")
|
|
106
|
+
return { min: 1, max: 1, label: "<projection-address>" };
|
|
101
107
|
if (isCallCommand(command))
|
|
102
108
|
return { min: 0, max: 1 };
|
|
103
109
|
return { min: 0, max: 0 };
|
|
@@ -129,6 +135,9 @@ function assignFlag(flags, name, value) {
|
|
|
129
135
|
case "projection":
|
|
130
136
|
flags.projection = String(value);
|
|
131
137
|
return;
|
|
138
|
+
case "alias":
|
|
139
|
+
flags.alias = String(value);
|
|
140
|
+
return;
|
|
132
141
|
case "timeout":
|
|
133
142
|
flags.timeoutMs = parseWaitTimeout(String(value));
|
|
134
143
|
return;
|
|
@@ -182,6 +191,9 @@ function assignFlag(flags, name, value) {
|
|
|
182
191
|
case "complete":
|
|
183
192
|
flags.complete = Boolean(value);
|
|
184
193
|
return;
|
|
194
|
+
case "all":
|
|
195
|
+
flags.all = Boolean(value);
|
|
196
|
+
return;
|
|
185
197
|
case "target":
|
|
186
198
|
if (flags.target !== undefined) {
|
|
187
199
|
throw new FlowError("EMPTY_PARAM", "option --target may only be specified once");
|
|
@@ -244,21 +256,28 @@ function readValueFlag(name, token, eq, tokens, index) {
|
|
|
244
256
|
function commissionConflictMessage(atForm, contractForm) {
|
|
245
257
|
return `commission address supplied twice: ${atForm} and --contract ${contractForm}; use exactly one form`;
|
|
246
258
|
}
|
|
247
|
-
function
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return
|
|
259
|
+
function formatSelectorList(items) {
|
|
260
|
+
if (items.length <= 1)
|
|
261
|
+
return items[0] ?? "";
|
|
262
|
+
if (items.length === 2)
|
|
263
|
+
return `${items[0]} and ${items[1]}`;
|
|
264
|
+
return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
|
|
265
|
+
}
|
|
266
|
+
function multiCommissionSelectorMessage(command, spellings) {
|
|
267
|
+
return `${command} accepts one commission address; got ${formatSelectorList(spellings)}`;
|
|
268
|
+
}
|
|
269
|
+
function commissionSelectorSpelling(source, value, rawToken) {
|
|
270
|
+
if (source === "at-address")
|
|
271
|
+
return rawToken ?? `@${value}`;
|
|
272
|
+
return `--contract ${rawToken ?? value}`;
|
|
253
273
|
}
|
|
254
274
|
export function parseCliArgs(tokens) {
|
|
255
275
|
const { head, payload } = splitAtLiteralBoundary(tokens);
|
|
256
276
|
const flags = {};
|
|
257
277
|
const free = [];
|
|
258
278
|
const seenFlagNames = new Set();
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
let contractFlagRaw;
|
|
279
|
+
const atTokens = [];
|
|
280
|
+
const contractFlagRaws = [];
|
|
262
281
|
let helpRequested = false;
|
|
263
282
|
for (let index = 0; index < head.length; index += 1) {
|
|
264
283
|
const token = head[index];
|
|
@@ -267,11 +286,7 @@ export function parseCliArgs(tokens) {
|
|
|
267
286
|
continue;
|
|
268
287
|
}
|
|
269
288
|
if (token.startsWith("@")) {
|
|
270
|
-
|
|
271
|
-
throw new FlowError("EMPTY_PARAM", "command accepts at most one contract address");
|
|
272
|
-
}
|
|
273
|
-
atToken = token;
|
|
274
|
-
atContractId = normalizeContractAddress(token);
|
|
289
|
+
atTokens.push(token);
|
|
275
290
|
continue;
|
|
276
291
|
}
|
|
277
292
|
if (/^-[A-Za-z]$/.test(token)) {
|
|
@@ -288,7 +303,7 @@ export function parseCliArgs(tokens) {
|
|
|
288
303
|
}
|
|
289
304
|
index += 1;
|
|
290
305
|
if (name === "contract") {
|
|
291
|
-
|
|
306
|
+
contractFlagRaws.push(value);
|
|
292
307
|
}
|
|
293
308
|
else {
|
|
294
309
|
assignFlag(flags, name, value);
|
|
@@ -307,6 +322,9 @@ export function parseCliArgs(tokens) {
|
|
|
307
322
|
}
|
|
308
323
|
const eq = token.indexOf("=");
|
|
309
324
|
const name = parseFlagName(eq === -1 ? token : token.slice(0, eq));
|
|
325
|
+
if (name === "place") {
|
|
326
|
+
throw new FlowError("EMPTY_PARAM", "unknown option --place; use @addr or --contract <addr>");
|
|
327
|
+
}
|
|
310
328
|
if (!CONTROL_FLAGS.has(name)) {
|
|
311
329
|
throw new FlowError("EMPTY_PARAM", `unknown option: --${name}`);
|
|
312
330
|
}
|
|
@@ -315,7 +333,7 @@ export function parseCliArgs(tokens) {
|
|
|
315
333
|
const { value, nextIndex } = readValueFlag(name, token, eq, head, index);
|
|
316
334
|
index = nextIndex;
|
|
317
335
|
if (name === "contract") {
|
|
318
|
-
|
|
336
|
+
contractFlagRaws.push(value);
|
|
319
337
|
}
|
|
320
338
|
else {
|
|
321
339
|
assignFlag(flags, name, value);
|
|
@@ -329,14 +347,23 @@ export function parseCliArgs(tokens) {
|
|
|
329
347
|
assignFlag(flags, name, true);
|
|
330
348
|
}
|
|
331
349
|
}
|
|
332
|
-
|
|
333
|
-
|
|
350
|
+
const { command, rest } = resolveCommand(free);
|
|
351
|
+
if (atTokens.length > 0 && contractFlagRaws.length > 0) {
|
|
352
|
+
throw new FlowError("EMPTY_PARAM", commissionConflictMessage(atTokens[0], contractFlagRaws[0]));
|
|
353
|
+
}
|
|
354
|
+
if (atTokens.length > 1) {
|
|
355
|
+
throw new FlowError("EMPTY_PARAM", multiCommissionSelectorMessage(command, atTokens));
|
|
356
|
+
}
|
|
357
|
+
if (contractFlagRaws.length > 1) {
|
|
358
|
+
throw new FlowError("EMPTY_PARAM", multiCommissionSelectorMessage(command, contractFlagRaws.map((raw) => `--contract ${raw}`)));
|
|
334
359
|
}
|
|
335
|
-
if (
|
|
336
|
-
flags.contractId =
|
|
360
|
+
if (atTokens.length === 1) {
|
|
361
|
+
flags.contractId = normalizeContractAddress(atTokens[0]);
|
|
337
362
|
flags.contractAddressSource = "at-address";
|
|
338
363
|
}
|
|
339
|
-
|
|
364
|
+
else if (contractFlagRaws.length === 1) {
|
|
365
|
+
assignFlag(flags, "contract", contractFlagRaws[0]);
|
|
366
|
+
}
|
|
340
367
|
if (helpRequested) {
|
|
341
368
|
return {
|
|
342
369
|
command,
|
|
@@ -362,6 +389,17 @@ export function parseCliArgs(tokens) {
|
|
|
362
389
|
? `bind creates a commission and does not accept @${flags.contractId}; use -C/--cwd to select the repository`
|
|
363
390
|
: `contract address @${flags.contractId} is not valid for ${command}`);
|
|
364
391
|
}
|
|
392
|
+
if ((command === "wait" || command === "tell") && flags.contractId !== undefined) {
|
|
393
|
+
const selectorSpelling = commissionSelectorSpelling(flags.contractAddressSource === "at-address" ? "at-address" : "explicit-flag", flags.contractId, flags.contractAddressSource === "at-address" ? atTokens[0] : contractFlagRaws[0]);
|
|
394
|
+
const scopeOnlyMessage = `${command} requires a projection id or alias; ${selectorSpelling} only limits projection lookup, so add the projection address`;
|
|
395
|
+
if (command === "wait" && rest.length + payload.length === 0) {
|
|
396
|
+
throw new FlowError("EMPTY_PARAM", scopeOnlyMessage);
|
|
397
|
+
}
|
|
398
|
+
if (command === "tell" && rest.length === 0) {
|
|
399
|
+
// Payload after -- is the message body, never the projection address.
|
|
400
|
+
throw new FlowError("EMPTY_PARAM", scopeOnlyMessage);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
365
403
|
const positional = [...rest, ...payload];
|
|
366
404
|
if (isCallCommand(command)) {
|
|
367
405
|
if (flags.akuma) {
|
|
@@ -374,6 +412,9 @@ export function parseCliArgs(tokens) {
|
|
|
374
412
|
if (!name) {
|
|
375
413
|
throw new FlowError("EMPTY_PARAM", `${command} requires an agent name; use \`keiyaku ${command} NAME [prompt|-]\``);
|
|
376
414
|
}
|
|
415
|
+
if (isResponseArtifactId(name)) {
|
|
416
|
+
throw new FlowError("EMPTY_PARAM", `artifact ${name} is a response artifact; call expects an Akuma profile name`);
|
|
417
|
+
}
|
|
377
418
|
const parsedName = agentNameSchema.safeParse(name);
|
|
378
419
|
if (!parsedName.success) {
|
|
379
420
|
throw new FlowError("EMPTY_PARAM", `${command} requires a valid agent name before the optional prompt`);
|
|
@@ -1,11 +1,98 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { AddressResolutionError, buildObjectResolutionAttempt, } from "../core/addressing.js";
|
|
4
|
+
import { resolveProjectionCoordinate } from "../core/projection-coordinate.js";
|
|
5
|
+
import { isProjectionId, projectionDir } from "../core/projection-core.js";
|
|
6
|
+
import { isProjectionAlias, readProjectionAlias } from "../core/projection-alias.js";
|
|
7
|
+
import { readProjectionIdentity } from "../core/projection-identity.js";
|
|
8
|
+
import { isResponseArtifactId } from "../core/response-artifact-id.js";
|
|
9
|
+
import { isErrnoException } from "../errno.js";
|
|
10
|
+
function isRealDirectory(directory) {
|
|
11
|
+
try {
|
|
12
|
+
return fs.lstatSync(directory).isDirectory();
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (isErrnoException(error) && error.code === "ENOENT")
|
|
16
|
+
return false;
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function resolveProjectionAddress(input) {
|
|
21
|
+
const workdir = path.resolve(input.cwd);
|
|
22
|
+
// Scoped lookup reuses the commission carrier's repository root: zero Git probes.
|
|
23
|
+
// Unscoped lookup performs exactly one coordinate probe.
|
|
24
|
+
let repo;
|
|
25
|
+
let physicalRoot;
|
|
26
|
+
let attempt;
|
|
27
|
+
if (input.scope) {
|
|
28
|
+
repo = input.scope.repo;
|
|
29
|
+
physicalRoot = input.scope.repo.stableRoot;
|
|
30
|
+
attempt = buildObjectResolutionAttempt({
|
|
31
|
+
repo,
|
|
32
|
+
workdir,
|
|
33
|
+
expected: "projection",
|
|
34
|
+
supplied: input.address,
|
|
35
|
+
scope: {
|
|
36
|
+
commissionId: input.scope.binding.commissionId,
|
|
37
|
+
asTyped: input.scope.spelling.asTyped,
|
|
38
|
+
},
|
|
39
|
+
inference: ["commission-scoped projection lookup"],
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const coordinate = resolveProjectionCoordinate(input.cwd);
|
|
44
|
+
repo = coordinate.authority.kind === "repository"
|
|
45
|
+
? { kind: "repository", stableRoot: coordinate.authority.stableRoot }
|
|
46
|
+
: { kind: "user" };
|
|
47
|
+
physicalRoot = coordinate.physicalRoot;
|
|
48
|
+
attempt = buildObjectResolutionAttempt({
|
|
49
|
+
repo,
|
|
50
|
+
workdir,
|
|
51
|
+
expected: "projection",
|
|
52
|
+
supplied: input.address,
|
|
53
|
+
inference: ["projection directory lookup"],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (isResponseArtifactId(input.address)) {
|
|
57
|
+
throw new AddressResolutionError("EMPTY_PARAM", `artifact ${input.address} is a response artifact; ${input.verb} expects a projection id or alias`, { ...attempt, actual: "artifact" });
|
|
58
|
+
}
|
|
59
|
+
const suffix = input.scope
|
|
60
|
+
? `within ${input.scope.spelling.asTyped}`
|
|
61
|
+
: repo.kind === "repository" ? `in repository ${repo.stableRoot}` : `in workdir ${workdir}`;
|
|
62
|
+
let id;
|
|
63
|
+
let alias;
|
|
64
|
+
if (input.address.includes("/")) {
|
|
65
|
+
if (!isProjectionId(input.address))
|
|
66
|
+
throw new AddressResolutionError("EMPTY_PARAM", `projection ${input.address} has no match ${suffix}`, attempt);
|
|
67
|
+
id = input.address;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
if (!isProjectionAlias(input.address))
|
|
71
|
+
throw new AddressResolutionError("EMPTY_PARAM", `projection alias ${input.address} has no match ${suffix}`, attempt);
|
|
72
|
+
const target = readProjectionAlias(physicalRoot, input.address);
|
|
73
|
+
if (!target)
|
|
74
|
+
throw new AddressResolutionError("EMPTY_PARAM", `projection alias ${input.address} has no match ${suffix}`, attempt);
|
|
75
|
+
id = target;
|
|
76
|
+
alias = input.address;
|
|
77
|
+
}
|
|
78
|
+
const directory = projectionDir(physicalRoot, id);
|
|
79
|
+
if (!isRealDirectory(directory)) {
|
|
80
|
+
const subject = alias ? `projection alias ${alias} points to missing projection ${id}` : `projection ${id}`;
|
|
81
|
+
throw new AddressResolutionError("EMPTY_PARAM", `${subject} has no match ${suffix}`, attempt);
|
|
82
|
+
}
|
|
83
|
+
let identity;
|
|
84
|
+
try {
|
|
85
|
+
identity = readProjectionIdentity(directory);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (error instanceof Error && /ENOENT|no such file/i.test(error.message)) {
|
|
89
|
+
const subject = alias ? `projection alias ${alias} points to missing projection ${id}` : `projection ${id}`;
|
|
90
|
+
throw new AddressResolutionError("EMPTY_PARAM", `${subject} has no match ${suffix}`, attempt);
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
if (input.scope && (identity.binding.kind !== "commission" || identity.binding.commissionId !== input.scope.binding.commissionId)) {
|
|
95
|
+
throw new AddressResolutionError("EMPTY_PARAM", `projection ${id} has no match ${suffix}`, attempt);
|
|
96
|
+
}
|
|
97
|
+
return { id, ...(alias ? { alias } : {}) };
|
|
11
98
|
}
|
|
@@ -25,7 +25,7 @@ export function renderResolvedAddressReceipt(address, facts = {}) {
|
|
|
25
25
|
`repo ${address.repo.stableRoot}`,
|
|
26
26
|
`workdir ${address.workdir}`,
|
|
27
27
|
facts.artifactId ? `artifact ${facts.artifactId}` : undefined,
|
|
28
|
-
facts.projectionId ? `projection ${facts.projectionId}` : undefined,
|
|
28
|
+
facts.projectionId ? `projection ${facts.projectionId}${facts.projectionAlias ? ` (${facts.projectionAlias})` : ""}` : undefined,
|
|
29
29
|
].filter((part) => part !== undefined).join(" · ");
|
|
30
30
|
}
|
|
31
31
|
const repository = address.repo.kind === "repository"
|
|
@@ -37,7 +37,7 @@ export function renderResolvedAddressReceipt(address, facts = {}) {
|
|
|
37
37
|
repository,
|
|
38
38
|
`workdir ${address.workdir}`,
|
|
39
39
|
facts.artifactId ? `artifact ${facts.artifactId}` : undefined,
|
|
40
|
-
facts.projectionId ? `projection ${facts.projectionId}` : undefined,
|
|
40
|
+
facts.projectionId ? `projection ${facts.projectionId}${facts.projectionAlias ? ` (${facts.projectionAlias})` : ""}` : undefined,
|
|
41
41
|
].filter((part) => part !== undefined).join(" · ");
|
|
42
42
|
}
|
|
43
43
|
return [
|
|
@@ -46,7 +46,7 @@ export function renderResolvedAddressReceipt(address, facts = {}) {
|
|
|
46
46
|
`repo ${address.repo.stableRoot}`,
|
|
47
47
|
`target ${address.target.branch}`,
|
|
48
48
|
facts.artifactId ? `artifact ${facts.artifactId}` : undefined,
|
|
49
|
-
facts.projectionId ? `projection ${facts.projectionId}` : undefined,
|
|
49
|
+
facts.projectionId ? `projection ${facts.projectionId}${facts.projectionAlias ? ` (${facts.projectionAlias})` : ""}` : undefined,
|
|
50
50
|
`via ${sourceLabel(address)}`,
|
|
51
51
|
].filter((part) => part !== undefined).join(" · ");
|
|
52
52
|
}
|
|
@@ -61,7 +61,16 @@ function renderAttemptCandidate(candidate) {
|
|
|
61
61
|
candidate.matchedBy ? `matched by ${candidate.matchedBy}` : undefined,
|
|
62
62
|
].filter((part) => part !== undefined).join(" · ");
|
|
63
63
|
}
|
|
64
|
+
function isCommissionResolutionAttempt(attempt) {
|
|
65
|
+
return attempt.expected === "commission";
|
|
66
|
+
}
|
|
64
67
|
export function renderResolutionAttemptPreamble(attempt) {
|
|
68
|
+
if (!isCommissionResolutionAttempt(attempt)) {
|
|
69
|
+
return [
|
|
70
|
+
attempt.repo.kind === "repository" ? `At repo ${attempt.repo.stableRoot}` : "At no repository",
|
|
71
|
+
attempt.scope ? `scope ${attempt.scope.asTyped}` : undefined,
|
|
72
|
+
].filter((part) => part !== undefined).join(" · ");
|
|
73
|
+
}
|
|
65
74
|
const parts = [
|
|
66
75
|
attempt.repo.kind === "repository" ? `At repo ${attempt.repo.stableRoot}` : "At no repository",
|
|
67
76
|
attempt.spelling ? `address ${attempt.spelling.asTyped}` : undefined,
|
package/build/cli/render/call.js
CHANGED
|
@@ -2,10 +2,12 @@ import { assembleResponse, buildPayload, buildSection, formatMaybe, formatWarnin
|
|
|
2
2
|
import { WARNING_SECTION_TITLE } from "./response-style.js";
|
|
3
3
|
import { CONTENT_SECTION_TITLE, formatPersistenceSummary, formatCallCompletionHeading, formatCallNextAction, INFO_AKUMA_MAX_CHARS, projectionRejoinHintLines, } from "./shared.js";
|
|
4
4
|
import { carryAddress, prependAddressReceipt, renderResolvedAddressReceipt } from "./address.js";
|
|
5
|
-
export function renderAdoptedLaunchReceipt(address, projectionId) {
|
|
5
|
+
export function renderAdoptedLaunchReceipt(address, projectionId, alias, previousAliasTarget) {
|
|
6
6
|
return [
|
|
7
|
-
renderResolvedAddressReceipt(address, { projectionId }),
|
|
8
|
-
...
|
|
7
|
+
renderResolvedAddressReceipt(address, { projectionId, ...(alias ? { projectionAlias: alias } : {}) }),
|
|
8
|
+
...(alias ? [`alias ${alias} → ${projectionId}`] : []),
|
|
9
|
+
...(alias && previousAliasTarget ? [`(moved from ${previousAliasTarget}, still running)`] : []),
|
|
10
|
+
...projectionRejoinHintLines(alias ?? projectionId),
|
|
9
11
|
].join("\n");
|
|
10
12
|
}
|
|
11
13
|
export function buildCallResponse(result, input) {
|
|
@@ -15,8 +17,11 @@ export function buildCallResponse(result, input) {
|
|
|
15
17
|
const { session: _session, evidenceCwd: _evidenceCwd, address: _address, foregroundWait: _foregroundWait, ...responseData } = result;
|
|
16
18
|
const output = {
|
|
17
19
|
text: input.addressPlacement === "stdout"
|
|
18
|
-
? renderAdoptedLaunchReceipt(result.address, result.projection.id)
|
|
19
|
-
:
|
|
20
|
+
? renderAdoptedLaunchReceipt(result.address, result.projection.id, result.projection.alias, result.projection.previousAliasTarget)
|
|
21
|
+
: [
|
|
22
|
+
...(result.projection.alias ? [`alias ${result.projection.alias} → ${result.projection.id}`] : []),
|
|
23
|
+
...projectionRejoinHintLines(result.projection.alias ?? result.projection.id),
|
|
24
|
+
].join("\n"),
|
|
20
25
|
payload: buildPayload(responseData),
|
|
21
26
|
};
|
|
22
27
|
switch (input.addressPlacement) {
|
|
@@ -135,14 +135,14 @@ export function projectionRejoinHintLines(projectionId) {
|
|
|
135
135
|
}
|
|
136
136
|
export function formatCallNextAction(input) {
|
|
137
137
|
if (input.projection?.phase === "active") {
|
|
138
|
-
return projectionRejoinHintLines(input.projection.id).join("\n");
|
|
138
|
+
return projectionRejoinHintLines(input.projection.alias ?? input.projection.id).join("\n");
|
|
139
139
|
}
|
|
140
140
|
if (input.projection?.phase === "done") {
|
|
141
141
|
return [
|
|
142
142
|
"The akuma is back.",
|
|
143
143
|
...(input.artifactId ? [`record: ${input.artifactId}`] : []),
|
|
144
144
|
"talk to it:",
|
|
145
|
-
` » keiyaku tell ${input.projection.id} "your words"`,
|
|
145
|
+
` » keiyaku tell ${input.projection.alias ?? input.projection.id} "your words"`,
|
|
146
146
|
].join("\n");
|
|
147
147
|
}
|
|
148
148
|
return "⟶ keiyaku status";
|
|
@@ -15,6 +15,16 @@ function renderProjectionDiagnostics(row) {
|
|
|
15
15
|
return "";
|
|
16
16
|
return ` · diag ${row.diagnostics.map((diagnostic) => diagnostic.detail).join("; ")}`;
|
|
17
17
|
}
|
|
18
|
+
function renderProjectionOmission(board) {
|
|
19
|
+
if (!board.omitted)
|
|
20
|
+
return undefined;
|
|
21
|
+
const counts = ["failed", "dead", "done", "dismissed"]
|
|
22
|
+
.flatMap((state) => {
|
|
23
|
+
const count = board.omitted?.byState[state] ?? 0;
|
|
24
|
+
return count > 0 ? [`${count} ${state}`] : [];
|
|
25
|
+
});
|
|
26
|
+
return ` · +${board.omitted.total} not shown (${counts.join(" · ")}) — keiyaku status --all`;
|
|
27
|
+
}
|
|
18
28
|
function renderProjectionAge(row) {
|
|
19
29
|
if (row.state === "done")
|
|
20
30
|
return "done";
|
|
@@ -63,18 +73,19 @@ function renderProjectionRow(row) {
|
|
|
63
73
|
: row.state === "startup-timeout"
|
|
64
74
|
? "minting"
|
|
65
75
|
: row.state);
|
|
76
|
+
const identity = row.alias ? `${row.id} (${row.alias})` : row.id;
|
|
66
77
|
let heading;
|
|
67
78
|
if (row.state === "startup-timeout" || row.state === "unknown") {
|
|
68
79
|
const akuma = (row.akuma ?? "(unknown)").padEnd(18);
|
|
69
|
-
heading = ` ${glyph} ${
|
|
80
|
+
heading = ` ${glyph} ${identity} ${akuma} ${renderProjectionAge(row)}${renderProjectionDiagnostics(row)}`.trimEnd();
|
|
70
81
|
}
|
|
71
82
|
else if (row.state === "minting") {
|
|
72
|
-
heading = ` ${glyph} ${
|
|
83
|
+
heading = ` ${glyph} ${identity.padEnd(18)} (minting)${renderProjectionDiagnostics(row)}`.trimEnd();
|
|
73
84
|
}
|
|
74
85
|
else {
|
|
75
86
|
const age = renderProjectionAge(row).padEnd(9);
|
|
76
87
|
const akuma = (row.akuma ?? "(unknown)").padEnd(18);
|
|
77
|
-
heading = ` ${glyph} ${
|
|
88
|
+
heading = ` ${glyph} ${identity} ${akuma} ${age}${renderProjectionTellCounts(row)}${renderProjectionDiagnostics(row)}`.trimEnd();
|
|
78
89
|
}
|
|
79
90
|
const activity = renderProjectionActivityLine(row);
|
|
80
91
|
return activity ? [heading, activity] : [heading];
|
|
@@ -82,7 +93,11 @@ function renderProjectionRow(row) {
|
|
|
82
93
|
export function renderProjectionStatusSection(board) {
|
|
83
94
|
if (!board || board.rows.length === 0)
|
|
84
95
|
return "";
|
|
85
|
-
|
|
96
|
+
const lines = board.rows.flatMap(renderProjectionRow);
|
|
97
|
+
const omission = renderProjectionOmission(board);
|
|
98
|
+
if (omission)
|
|
99
|
+
lines.push(omission);
|
|
100
|
+
return `${STATUS_PROJECTION_SECTION_TITLE}\n${lines.join("\n")}\n`;
|
|
86
101
|
}
|
|
87
102
|
function renderClaimReconciliationSection(recon) {
|
|
88
103
|
if (!recon)
|
package/build/cli/render/wait.js
CHANGED
|
@@ -3,7 +3,7 @@ import { displayColumns, resolveLineColumns, truncateColumns } from "./line-widt
|
|
|
3
3
|
import { projectionRejoinHintLines, textResponse } from "./shared.js";
|
|
4
4
|
const WAIT_HEADER_MAX_COLUMNS = 60;
|
|
5
5
|
function shortAddress(projectionId) {
|
|
6
|
-
return
|
|
6
|
+
return /\/([0-9a-f]{8})$/.exec(projectionId)?.[1] ?? projectionId;
|
|
7
7
|
}
|
|
8
8
|
function phaseState(phase) {
|
|
9
9
|
switch (phase) {
|
|
@@ -42,7 +42,7 @@ function waitStateText(snapshot) {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
function projectionIdentityParts(projectionId) {
|
|
45
|
-
const match = /^(.*)
|
|
45
|
+
const match = /^(.*)\/([0-9a-f]{8})$/.exec(projectionId);
|
|
46
46
|
return match ? { slug: match[1], shortId: match[2] } : { slug: "", shortId: projectionId };
|
|
47
47
|
}
|
|
48
48
|
function waitHeader(projectionId, akuma, snapshot, outputColumns) {
|
|
@@ -215,7 +215,7 @@ export function buildWaitResponse(projectionId, result, options = {}) {
|
|
|
215
215
|
if (rendered.lines.length === 0 && state === "running") {
|
|
216
216
|
lines.push(" not a word out of it yet — pulse is steady");
|
|
217
217
|
}
|
|
218
|
-
const nextActions = nextActionLines(projectionId, result, state);
|
|
218
|
+
const nextActions = nextActionLines(options.alias ?? projectionId, result, state);
|
|
219
219
|
if (rendered.hasMutationFiles && nextActions.length > 0)
|
|
220
220
|
lines.push("");
|
|
221
221
|
lines.push(...nextActions);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const EMPTY_OWNERSHIP = Object.freeze({
|
|
2
|
+
access: Object.freeze([]),
|
|
3
|
+
network: Object.freeze([]),
|
|
4
|
+
webSearch: Object.freeze([]),
|
|
5
|
+
});
|
|
6
|
+
function owns(config, key) {
|
|
7
|
+
return Object.prototype.hasOwnProperty.call(config, key);
|
|
8
|
+
}
|
|
9
|
+
export function nativePolicyOwnership(instanceName, instance) {
|
|
10
|
+
if (instance.kind !== "codex-sdk" && instance.kind !== "codex-app-server") {
|
|
11
|
+
return EMPTY_OWNERSHIP;
|
|
12
|
+
}
|
|
13
|
+
const config = instance.config;
|
|
14
|
+
if (!config)
|
|
15
|
+
return EMPTY_OWNERSHIP;
|
|
16
|
+
const access = [];
|
|
17
|
+
const network = [];
|
|
18
|
+
const webSearch = [];
|
|
19
|
+
const add = (target, key) => {
|
|
20
|
+
target.push({ path: `providers.${instanceName}.config.${key}`, value: config[key] });
|
|
21
|
+
};
|
|
22
|
+
if (owns(config, "sandbox_mode"))
|
|
23
|
+
add(access, "sandbox_mode");
|
|
24
|
+
if (owns(config, "approval_policy"))
|
|
25
|
+
add(access, "approval_policy");
|
|
26
|
+
for (const key of Object.keys(config)) {
|
|
27
|
+
if (key !== "sandbox_workspace_write" && !key.startsWith("sandbox_workspace_write."))
|
|
28
|
+
continue;
|
|
29
|
+
add(access, key);
|
|
30
|
+
add(network, key);
|
|
31
|
+
}
|
|
32
|
+
if (owns(config, "web_search"))
|
|
33
|
+
add(webSearch, "web_search");
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
access: Object.freeze(access),
|
|
36
|
+
network: Object.freeze(network),
|
|
37
|
+
webSearch: Object.freeze(webSearch),
|
|
38
|
+
});
|
|
39
|
+
}
|
package/build/config/settings.js
CHANGED
|
@@ -41,19 +41,9 @@ const CODEX_SDK_FORBIDDEN_THREAD_OPTIONS_KEYS = [
|
|
|
41
41
|
const FORBIDDEN_CONFIG_KEYS = [
|
|
42
42
|
"model",
|
|
43
43
|
"model_reasoning_effort",
|
|
44
|
-
"sandbox_mode",
|
|
45
|
-
"sandbox_workspace_write",
|
|
46
|
-
"web_search",
|
|
47
|
-
"approval_policy",
|
|
48
44
|
];
|
|
49
45
|
function isConfigKeyForbidden(key) {
|
|
50
|
-
|
|
51
|
-
if (FORBIDDEN_CONFIG_KEYS.includes(key))
|
|
52
|
-
return true;
|
|
53
|
-
// sandbox_workspace_write also rejects dotted subkeys from adapter-native TOML flattening
|
|
54
|
-
if (key.startsWith("sandbox_workspace_write."))
|
|
55
|
-
return true;
|
|
56
|
-
return false;
|
|
46
|
+
return FORBIDDEN_CONFIG_KEYS.includes(key);
|
|
57
47
|
}
|
|
58
48
|
const claudeSettingSourceSchema = z.enum(CLAUDE_SETTING_SOURCES);
|
|
59
49
|
const writableRootItemSchema = z.string().superRefine((value, ctx) => {
|