@astrosheep/keiyaku 4.6.13 → 4.6.14

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.
Files changed (56) hide show
  1. package/build/plugins/claude/skills/keiyaku/SKILL.md +3 -0
  2. package/build/plugins/claude/skills/keiyaku-akuma/SKILL.md +20 -0
  3. package/build/plugins/codex/skills/keiyaku/SKILL.md +3 -0
  4. package/build/plugins/codex/skills/keiyaku-akuma/SKILL.md +20 -0
  5. package/build/plugins/opencode/skills/keiyaku/SKILL.md +3 -0
  6. package/build/plugins/opencode/skills/keiyaku-akuma/SKILL.md +20 -0
  7. package/build/skills/keiyaku/SKILL.md +3 -0
  8. package/build/skills/keiyaku-akuma/SKILL.md +20 -0
  9. package/build/src/akuma/akuma-instance.js +7 -5
  10. package/build/src/akuma/akuma-observe.js +1 -0
  11. package/build/src/akuma/akuma-owner.js +3 -2
  12. package/build/src/akuma/akuma-wait.d.ts +1 -2
  13. package/build/src/akuma/akuma-wait.js +1 -4
  14. package/build/src/akuma/akuma.d.ts +9 -4
  15. package/build/src/akuma/akuma.js +208 -35
  16. package/build/src/akuma/body.js +4 -0
  17. package/build/src/akuma/caller.d.ts +2 -0
  18. package/build/src/akuma/caller.js +6 -0
  19. package/build/src/akuma/custody.d.ts +11 -0
  20. package/build/src/akuma/custody.js +102 -0
  21. package/build/src/akuma/lineage-cache.d.ts +39 -0
  22. package/build/src/akuma/lineage-cache.js +162 -0
  23. package/build/src/akuma/nuke.d.ts +1 -3
  24. package/build/src/akuma/nuke.js +11 -102
  25. package/build/src/akuma/retention.d.ts +44 -0
  26. package/build/src/akuma/retention.js +106 -0
  27. package/build/src/akuma/selection-owner-port.d.ts +1 -1
  28. package/build/src/akuma/selection-owner-port.js +3 -6
  29. package/build/src/akuma/selection-request.d.ts +131 -4
  30. package/build/src/akuma/selection-request.js +25 -15
  31. package/build/src/alias/index.d.ts +11 -0
  32. package/build/src/alias/index.js +20 -1
  33. package/build/src/cli/commands/contract-grammar.d.ts +12 -0
  34. package/build/src/cli/commands/contract-grammar.js +65 -13
  35. package/build/src/cli/commands/contract-help.d.ts +3 -1
  36. package/build/src/cli/commands/contract-help.js +5 -2
  37. package/build/src/cli/commands/contract.js +31 -4
  38. package/build/src/cli/render/catalog.d.ts +2 -1
  39. package/build/src/cli/render/catalog.js +8 -2
  40. package/build/src/cli/square-identity.d.ts +0 -5
  41. package/build/src/cli/square-identity.js +51 -101
  42. package/build/src/index.d.ts +1 -1
  43. package/build/src/library/akuma-retention.d.ts +11 -0
  44. package/build/src/library/akuma-retention.js +131 -0
  45. package/build/src/library/akumas.d.ts +8 -0
  46. package/build/src/library/akumas.js +42 -2
  47. package/build/src/library/selection.js +22 -9
  48. package/build/src/plugin/akuma-signals.d.ts +1 -0
  49. package/build/src/plugin/akuma-signals.js +17 -0
  50. package/build/src/plugin/public.d.ts +7 -0
  51. package/build/src/plugin/runtime.d.ts +1 -0
  52. package/build/src/plugin/runtime.js +85 -13
  53. package/build/src/runtime/proc/windows-launch.exe +0 -0
  54. package/package.json +3 -3
  55. package/skills/keiyaku/SKILL.md +3 -0
  56. package/skills/keiyaku-akuma/SKILL.md +20 -0
@@ -1,5 +1,4 @@
1
1
  import { AkumaSelfInterruptError } from "./akuma-errors.js";
2
- import { outcomePluginObserver } from "../plugin/akuma-signals.js";
3
2
  import { requireBornAkuma } from "./akuma-probe.js";
4
3
  import { executeAskAkuma, executeKillAkuma, executeTellAkuma } from "./akuma-selection-execution.js";
5
4
  import { executeWaitAkuma } from "./akuma-wait.js";
@@ -13,13 +12,13 @@ import { akumaObservationOf } from "./selection-observation.js";
13
12
  * unaddressable member never starts; the executor's own access to the target is
14
13
  * then the product act, never a second addressing probe.
15
14
  */
16
- export function selectionRequestPort(world, reportDiagnostic, servingBodyId) {
15
+ export function selectionRequestPort(world, _reportDiagnostic, servingBodyId) {
17
16
  const prove = async (targets) => {
18
17
  for (const id of targets)
19
18
  await requireBornAkuma(world, id);
20
19
  };
21
20
  return {
22
- wait: async ({ targets, observer, ...request }) => {
21
+ wait: async ({ targets, ...request }) => {
23
22
  await prove(targets);
24
23
  // The lower wait owner returns association-free native evidence; the
25
24
  // actual request crossing composes its canonical transport shape.
@@ -27,7 +26,6 @@ export function selectionRequestPort(world, reportDiagnostic, servingBodyId) {
27
26
  path: world,
28
27
  ids: targets,
29
28
  ...request,
30
- outcomeObserved: outcomePluginObserver({ world, reportDiagnostic }, observer),
31
29
  });
32
30
  return { ...native, observations: native.observations.map(akumaObservationOf) };
33
31
  },
@@ -37,7 +35,7 @@ export function selectionRequestPort(world, reportDiagnostic, servingBodyId) {
37
35
  await prove([target]);
38
36
  return await executeTellAkuma({ path: world, id: target, ...request });
39
37
  },
40
- ask: async ({ target, observer, ...request }) => {
38
+ ask: async ({ target, ...request }) => {
41
39
  if (request.interrupt === true && servingBodyId === target)
42
40
  throw new AkumaSelfInterruptError(target);
43
41
  await prove([target]);
@@ -45,7 +43,6 @@ export function selectionRequestPort(world, reportDiagnostic, servingBodyId) {
45
43
  path: world,
46
44
  id: target,
47
45
  ...request,
48
- outcomeObserved: outcomePluginObserver({ world, reportDiagnostic }, observer),
49
46
  });
50
47
  },
51
48
  kill: async ({ targets, ...request }) => {
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import type { AkumaStatus } from "./akuma.js";
3
3
  import type { KillEvidence } from "./heart/index.js";
4
+ import type { OutcomeObserver } from "./akuma-observe.js";
4
5
  import { type ErasedRequestCommand, type RequestProtocol, type ServiceRequestCommand } from "./request-wire.js";
5
6
  import type { Schema } from "./schema.js";
6
7
  import { type AkumaAskResult, type AkumaKillResult, type AkumaTellResult, type AkumaWaitResult } from "./selection-observation.js";
@@ -100,13 +101,139 @@ export declare function encodeSelectionLiveFailure(error: unknown): unknown | nu
100
101
  export declare function decodeSelectionLiveFailure(value: unknown): Error | null;
101
102
  export type SelectionRequest = z.infer<typeof waitRequestSchema> | z.infer<typeof tellRequestSchema> | z.infer<typeof askRequestSchema> | z.infer<typeof killRequestSchema>;
102
103
  export type SelectionService = z.infer<typeof waitServiceSchema> | z.infer<typeof tellServiceSchema> | z.infer<typeof askServiceSchema> | z.infer<typeof killServiceSchema>;
103
- type SelectionResult = AkumaWaitResult | AkumaTellResult | AkumaAskResult | AkumaKillResult;
104
+ declare const forwardedAskResultSchema: z.ZodObject<{
105
+ result: z.ZodObject<{
106
+ akuma: z.ZodPipe<z.ZodString, z.ZodTransform<import("./identity.js").AkuId, string>>;
107
+ tell: z.ZodObject<{
108
+ admission: z.ZodObject<{
109
+ fact: z.ZodLiteral<"recorded">;
110
+ tellId: z.ZodString;
111
+ }, z.core.$strict>;
112
+ row: z.ZodObject<{
113
+ kind: z.ZodLiteral<"tell">;
114
+ sequence: z.ZodNumber;
115
+ at: z.ZodString;
116
+ tellId: z.ZodString;
117
+ text: z.ZodString;
118
+ state: z.ZodEnum<{
119
+ pending: "pending";
120
+ told: "told";
121
+ }>;
122
+ deliveries: z.ZodReadonly<z.ZodArray<z.ZodPipe<z.ZodObject<{
123
+ deliveredAt: z.ZodString;
124
+ route: z.ZodEnum<{
125
+ live: "live";
126
+ launch: "launch";
127
+ }>;
128
+ turnSequence: z.ZodNumber;
129
+ receipt: z.ZodOptional<z.ZodEnum<{
130
+ unavailable: "unavailable";
131
+ required: "required";
132
+ }>>;
133
+ }, z.core.$strict>, z.ZodTransform<Readonly<{
134
+ turnSequence: number;
135
+ route: "launch" | "live";
136
+ receipt?: "unavailable" | "required";
137
+ deliveredAt: string;
138
+ }>, {
139
+ deliveredAt: string;
140
+ route: "live" | "launch";
141
+ turnSequence: number;
142
+ receipt?: "unavailable" | "required" | undefined;
143
+ }>>>>;
144
+ }, z.core.$strict>;
145
+ wake: z.ZodUnion<readonly [z.ZodObject<{
146
+ kind: z.ZodLiteral<"told">;
147
+ }, z.core.$strict>, z.ZodObject<{
148
+ kind: z.ZodLiteral<"held">;
149
+ }, z.core.$strict>, z.ZodObject<{
150
+ kind: z.ZodLiteral<"pursuing">;
151
+ bodySequence: z.ZodNumber;
152
+ }, z.core.$strict>, z.ZodPipe<z.ZodObject<{
153
+ kind: z.ZodLiteral<"failed">;
154
+ diagnostic: z.ZodString;
155
+ child: z.ZodOptional<z.ZodObject<{
156
+ code: z.ZodNullable<z.ZodNumber>;
157
+ signal: z.ZodNullable<z.ZodString>;
158
+ log: z.ZodObject<{
159
+ path: z.ZodString;
160
+ from: z.ZodNumber;
161
+ to: z.ZodNumber;
162
+ }, z.core.$strict>;
163
+ }, z.core.$strict>>;
164
+ }, z.core.$strict>, z.ZodTransform<{
165
+ kind: "failed";
166
+ diagnostic: string;
167
+ } | {
168
+ child: {
169
+ code: number | null;
170
+ signal: string | null;
171
+ log: {
172
+ path: string;
173
+ from: number;
174
+ to: number;
175
+ };
176
+ };
177
+ kind: "failed";
178
+ diagnostic: string;
179
+ }, {
180
+ kind: "failed";
181
+ diagnostic: string;
182
+ child?: {
183
+ code: number | null;
184
+ signal: string | null;
185
+ log: {
186
+ path: string;
187
+ from: number;
188
+ to: number;
189
+ };
190
+ } | undefined;
191
+ }>>]>;
192
+ interruption: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
193
+ kind: z.ZodLiteral<"completed">;
194
+ bodySequence: z.ZodNumber;
195
+ putDown: z.ZodEnum<{
196
+ "was-idle": "was-idle";
197
+ "self-aborted": "self-aborted";
198
+ }>;
199
+ }, z.core.$strict>, z.ZodObject<{
200
+ kind: z.ZodLiteral<"pending">;
201
+ bodySequence: z.ZodNumber;
202
+ }, z.core.$strict>, z.ZodObject<{
203
+ kind: z.ZodLiteral<"unavailable">;
204
+ bodySequence: z.ZodNumber;
205
+ evidence: z.ZodEnum<{
206
+ hung: "hung";
207
+ untidy: "untidy";
208
+ }>;
209
+ }, z.core.$strict>], "kind">>;
210
+ }, z.core.$strict>;
211
+ observation: z.ZodUnion<readonly [z.ZodObject<{
212
+ reason: z.ZodLiteral<"answered">;
213
+ answer: z.ZodUnknown;
214
+ }, z.core.$strict>, z.ZodObject<{
215
+ reason: z.ZodLiteral<"failed">;
216
+ diagnostic: z.ZodString;
217
+ }, z.core.$strict>, z.ZodObject<{
218
+ reason: z.ZodLiteral<"invalid-output">;
219
+ diagnostic: z.ZodString;
220
+ answer: z.ZodString;
221
+ }, z.core.$strict>, z.ZodObject<{
222
+ reason: z.ZodLiteral<"unanswered">;
223
+ }, z.core.$strict>, z.ZodObject<{
224
+ reason: z.ZodLiteral<"deadline">;
225
+ }, z.core.$strict>]>;
226
+ completedAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
227
+ }, z.core.$strict>;
228
+ historyIds: z.ZodReadonly<z.ZodArray<z.ZodString>>;
229
+ }, z.core.$strict>;
230
+ type ForwardedAskResult = z.infer<typeof forwardedAskResultSchema>;
231
+ type SelectionResult = AkumaWaitResult | AkumaTellResult | ForwardedAskResult | AkumaKillResult;
104
232
  export type SelectionRequestPort = Readonly<{
105
233
  wait(input: Readonly<{
106
234
  targets: readonly AkumaStatus["id"][];
107
235
  completion: "any" | "all";
108
236
  timeoutMs?: number;
109
- observer: string;
110
237
  signal: AbortSignal;
111
238
  }>): Promise<AkumaWaitResult>;
112
239
  tell(input: Readonly<{
@@ -127,7 +254,7 @@ export type SelectionRequestPort = Readonly<{
127
254
  schemaJson?: string;
128
255
  initiator?: string;
129
256
  interrupt?: boolean;
130
- observer: string;
257
+ outcomeObserved: OutcomeObserver;
131
258
  signal: AbortSignal;
132
259
  }>): Promise<AkumaAskResult>;
133
260
  kill(input: Readonly<{
@@ -169,7 +296,7 @@ export declare function requestForwardedSelectionAsk(input: Readonly<{
169
296
  initiator?: string;
170
297
  interrupt?: boolean;
171
298
  signal?: AbortSignal;
172
- }>): Promise<AkumaAskResult>;
299
+ }>): Promise<ForwardedAskResult>;
173
300
  export declare function requestForwardedSelectionKill(input: Readonly<{
174
301
  directory: string;
175
302
  targets: readonly AkumaStatus["id"][];
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { AkumaNotBornError, AkumaObservationError, AkumaSelfInterruptError } from "./akuma-errors.js";
3
- import { akumaIdSchema } from "./identity.js";
3
+ import { akumaIdSchema, parsePublicHistoryId } from "./identity.js";
4
4
  import { requestBodyCommand } from "./request-rendezvous.js";
5
5
  import { eraseRequestCommand, } from "./request-wire.js";
6
6
  import { schemaJsonText } from "./schema.js";
@@ -104,6 +104,12 @@ export function decodeSelectionLiveFailure(value) {
104
104
  ? new AkumaSelfInterruptError(failure.id)
105
105
  : new AkumaObservationError(failure.id, failure.diagnostic);
106
106
  }
107
+ const forwardedAskResultSchema = z
108
+ .object({
109
+ result: selectionResultSchemas.ask,
110
+ historyIds: z.array(z.string().refine((id) => parsePublicHistoryId(id) !== null)).readonly(),
111
+ })
112
+ .strict();
107
113
  function decodeSelectionRequest(action, value) {
108
114
  const schema = action === "akuma.wait"
109
115
  ? waitRequestSchema
@@ -134,7 +140,7 @@ function decodedSelectionResult(action, value) {
134
140
  : action === "akuma.tell"
135
141
  ? selectionResultSchemas.tell
136
142
  : action === "akuma.ask"
137
- ? selectionResultSchemas.ask
143
+ ? forwardedAskResultSchema
138
144
  : selectionResultSchemas.kill;
139
145
  const parsed = schema.safeParse(value);
140
146
  if (!parsed.success)
@@ -172,7 +178,7 @@ export function selectionRequestCommand(action, port) {
172
178
  if (request.action === "akuma.wait") {
173
179
  return {
174
180
  kind: "served",
175
- result: await port.wait({ ...request, observer: facts.requester, signal: facts.signal }),
181
+ result: await port.wait({ ...request, signal: facts.signal }),
176
182
  service: { action: request.action },
177
183
  };
178
184
  }
@@ -194,20 +200,24 @@ export function selectionRequestCommand(action, port) {
194
200
  if (request.action === "akuma.ask") {
195
201
  if (port.ask === undefined)
196
202
  throw new Error("bounded Tell Selection port is unavailable");
203
+ let historyIds = [];
204
+ const result = await port.ask({
205
+ target: request.target,
206
+ body: request.body,
207
+ tellId: facts.id,
208
+ recordedAt: facts.admittedAt,
209
+ outcomeObserved: async (_id, observed) => {
210
+ historyIds = observed;
211
+ },
212
+ ...(request.timeoutMs === undefined ? {} : { timeoutMs: request.timeoutMs }),
213
+ ...(request.schemaJson === undefined ? {} : { schemaJson: request.schemaJson }),
214
+ ...(request.initiator === undefined ? {} : { initiator: request.initiator }),
215
+ ...(request.interrupt === undefined ? {} : { interrupt: request.interrupt }),
216
+ signal: facts.signal,
217
+ });
197
218
  return {
198
219
  kind: "served",
199
- result: await port.ask({
200
- target: request.target,
201
- body: request.body,
202
- tellId: facts.id,
203
- recordedAt: facts.admittedAt,
204
- observer: facts.requester,
205
- ...(request.timeoutMs === undefined ? {} : { timeoutMs: request.timeoutMs }),
206
- ...(request.schemaJson === undefined ? {} : { schemaJson: request.schemaJson }),
207
- ...(request.initiator === undefined ? {} : { initiator: request.initiator }),
208
- ...(request.interrupt === undefined ? {} : { interrupt: request.interrupt }),
209
- signal: facts.signal,
210
- }),
220
+ result: { result, historyIds },
211
221
  service: { action: request.action, target: request.target, tellId: facts.id },
212
222
  };
213
223
  }
@@ -11,6 +11,17 @@ export type AliasMove = Readonly<{
11
11
  }>;
12
12
  export declare function nukeAliases(world: WorldRoot): Promise<void>;
13
13
  export declare function readAliases(world: WorldRoot): Promise<readonly AliasBinding[]>;
14
+ export type HeldAliasCustody = Readonly<{
15
+ bindings: readonly AliasBinding[];
16
+ release(): void;
17
+ }>;
18
+ /**
19
+ * Take the same authority lock Alias writers use and read the current bindings
20
+ * under it. A holder keeps the lock across its cross-authority decision and
21
+ * effect, so no Alias move can commit between them. Null means another writer
22
+ * holds the authority; the caller must leave its subject untouched.
23
+ */
24
+ export declare function tryAcquireAliasCustody(world: WorldRoot): Promise<HeldAliasCustody | null>;
14
25
  export declare function moveAlias(input: Readonly<{
15
26
  world: WorldRoot;
16
27
  alias: AkumaAlias;
@@ -2,7 +2,7 @@ import { lstat, mkdir, readFile, unlink } from "node:fs/promises";
2
2
  import { dirname, resolve } from "node:path";
3
3
  import { parseAkuId } from "../akuma/identity.js";
4
4
  import { replaceFileDurably } from "../coordination/durable-file.js";
5
- import { acquireSqliteTransactionLock } from "../coordination/sqlite-transaction-lock.js";
5
+ import { acquireSqliteTransactionLock, tryAcquireSqliteTransactionLock, } from "../coordination/sqlite-transaction-lock.js";
6
6
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
7
7
  import { parseAkumaAlias, parseReadableAkumaAlias } from "../identity/selector.js";
8
8
  const VERSION = 1;
@@ -113,6 +113,25 @@ export async function nukeAliases(world) {
113
113
  export async function readAliases(world) {
114
114
  return await read(paths(world).authority);
115
115
  }
116
+ /**
117
+ * Take the same authority lock Alias writers use and read the current bindings
118
+ * under it. A holder keeps the lock across its cross-authority decision and
119
+ * effect, so no Alias move can commit between them. Null means another writer
120
+ * holds the authority; the caller must leave its subject untouched.
121
+ */
122
+ export async function tryAcquireAliasCustody(world) {
123
+ const location = paths(world);
124
+ const held = await tryAcquireSqliteTransactionLock({ path: location.lock, mode: "immediate" });
125
+ if (held === null)
126
+ return null;
127
+ try {
128
+ return { bindings: await read(location.authority), release: () => held.close() };
129
+ }
130
+ catch (error) {
131
+ held.close();
132
+ throw error;
133
+ }
134
+ }
116
135
  export async function moveAlias(input) {
117
136
  const alias = parseAkumaAlias(input.alias);
118
137
  const akuId = parseAkuId(input.akuId).id;
@@ -1,4 +1,15 @@
1
+ import { type AkumaCaller } from "../../akuma/caller.js";
2
+ import { type AkuId } from "../../akuma/identity.js";
1
3
  import { type ContractCommand } from "./contract-help.js";
4
+ type AkumaCallerSelector = Readonly<{
5
+ kind: "mine";
6
+ }> | Readonly<{
7
+ kind: "external";
8
+ caller: AkumaCaller;
9
+ }> | Readonly<{
10
+ kind: "akuma";
11
+ akuId: AkuId;
12
+ }>;
2
13
  type CatalogQuery = Readonly<{
3
14
  kind: "debts";
4
15
  namespace?: readonly string[];
@@ -12,6 +23,7 @@ type CatalogQuery = Readonly<{
12
23
  }> | Readonly<{
13
24
  kind: "akuma";
14
25
  archetype?: string;
26
+ caller?: AkumaCallerSelector;
15
27
  limit?: number;
16
28
  }>;
17
29
  type Output = Readonly<{
@@ -1,4 +1,6 @@
1
1
  import { validateAkumaName } from "../../identity/normalize.js";
2
+ import { decodeAkumaCaller } from "../../akuma/caller.js";
3
+ import { parseAkuId } from "../../akuma/identity.js";
2
4
  import { parseDebtNamespaceSelector } from "../../debt/catalog.js";
3
5
  import { CliUsageError, commandGuide } from "../usage.js";
4
6
  import { CONTRACT_COMMAND_SPECS } from "./contract-help.js";
@@ -231,26 +233,76 @@ function debtCatalogQuery(path, limit) {
231
233
  refuse("ls", error instanceof Error ? error.message : `invalid Debt namespace selector: ${path}`);
232
234
  }
233
235
  }
236
+ function parseCallerSelector(parts) {
237
+ const mine = parts.flags.mine === true;
238
+ const value = optionalFlag(parts.flags, "caller");
239
+ if (mine && value !== undefined)
240
+ refuse("ls", "--mine and --caller are mutually exclusive");
241
+ if (mine)
242
+ return { kind: "mine" };
243
+ if (value === undefined)
244
+ return undefined;
245
+ const text = value.trim();
246
+ if (text.startsWith("aku/")) {
247
+ try {
248
+ return { kind: "akuma", akuId: parseAkuId(text).id };
249
+ }
250
+ catch (error) {
251
+ refuse("ls", `--caller has an invalid Akuma identity: ${error instanceof Error ? error.message : text}`);
252
+ }
253
+ }
254
+ const colon = text.indexOf(":");
255
+ if (colon < 0)
256
+ refuse("ls", "--caller requires <integration>:<id> or aku/<name>/<suffix>");
257
+ try {
258
+ const caller = decodeAkumaCaller({
259
+ kind: "external",
260
+ integration: text.slice(0, colon),
261
+ id: text.slice(colon + 1),
262
+ });
263
+ if (caller === undefined)
264
+ refuse("ls", "--caller requires <integration>:<id> or aku/<name>/<suffix>");
265
+ return { kind: "external", caller };
266
+ }
267
+ catch (error) {
268
+ refuse("ls", `--caller is invalid: ${error instanceof Error ? error.message : String(error)}`);
269
+ }
270
+ }
234
271
  function parseLs(parts) {
235
272
  const path = parts.positionals[0];
236
- if (path === undefined)
237
- refuse("ls", "ls requires a selector");
238
273
  const limit = listLimit(parts);
239
- const debtQuery = debtCatalogQuery(path, limit);
240
- if (debtQuery !== null)
241
- return { command: "ls", query: debtQuery, output: parts.output };
242
- if (path === "kei" || path === "kei/") {
243
- return {
244
- command: "ls",
245
- query: { kind: "contracts", ...(limit === undefined ? {} : { limit }) },
246
- output: parts.output,
247
- };
274
+ const caller = parseCallerSelector(parts);
275
+ const callerRefusal = "--mine and --caller select Akuma instances only";
276
+ if (path !== undefined) {
277
+ const debtQuery = debtCatalogQuery(path, limit);
278
+ if (debtQuery !== null) {
279
+ if (caller !== undefined)
280
+ refuse("ls", callerRefusal);
281
+ return { command: "ls", query: debtQuery, output: parts.output };
282
+ }
283
+ if (path === "kei" || path === "kei/") {
284
+ if (caller !== undefined)
285
+ refuse("ls", callerRefusal);
286
+ return {
287
+ command: "ls",
288
+ query: { kind: "contracts", ...(limit === undefined ? {} : { limit }) },
289
+ output: parts.output,
290
+ };
291
+ }
292
+ if (caller !== undefined && (path === "aku" || path === "aku/"))
293
+ refuse("ls", "--mine and --caller need an Akuma instance catalogue, not Akuma names");
248
294
  }
295
+ if (path === undefined && caller === undefined)
296
+ refuse("ls", "ls requires a selector");
249
297
  try {
250
- const query = parseAkumaCatalogPath(path);
298
+ const query = path === undefined ? { kind: "akuma" } : parseAkumaCatalogPath(path);
251
299
  if (query === null)
252
300
  refuse("ls", "ls requires a supported identity directory selector");
253
- return { command: "ls", query: { ...query, ...(limit === undefined ? {} : { limit }) }, output: parts.output };
301
+ return {
302
+ command: "ls",
303
+ query: { ...query, ...(limit === undefined ? {} : { limit }), ...(caller === undefined ? {} : { caller }) },
304
+ output: parts.output,
305
+ };
254
306
  }
255
307
  catch (error) {
256
308
  if (error instanceof CliUsageError)
@@ -118,8 +118,10 @@ export declare const CONTRACT_COMMAND_SPECS: {
118
118
  readonly flags: {
119
119
  readonly limit: "value";
120
120
  readonly json: "boolean";
121
+ readonly mine: "boolean";
122
+ readonly caller: "value";
121
123
  };
122
- readonly usage: "ls debt[/] [--limit <count>]\nls kei[/] [--limit <count>]\nls aku[/] [--limit <count>]\nls aku/<name>[/] [--limit <count>]\nls \"aku/<name>/*\" [--limit <count>]\nls \"aku/*/*\" [--limit <count>]";
124
+ readonly usage: "ls debt[/] [--limit <count>]\nls kei[/] [--limit <count>]\nls aku[/] [--limit <count>]\nls aku/<name>[/] [--limit <count>]\nls \"aku/<name>/*\" [--limit <count>]\nls \"aku/*/*\" [--limit <count>]\nls [\"aku/*/*\"] --mine\nls [\"aku/*/*\"] --caller <integration>:<id> | aku/<name>/<suffix>";
123
125
  readonly purpose: "List debts (ls debt), contracts (ls kei), or akumas (ls aku).";
124
126
  readonly details: string;
125
127
  };
@@ -182,13 +182,16 @@ export const CONTRACT_COMMAND_SPECS = {
182
182
  ls: {
183
183
  positional: "optional",
184
184
  stdin: "none",
185
- flags: { limit: "value", json: "boolean" },
186
- usage: 'ls debt[/] [--limit <count>]\nls kei[/] [--limit <count>]\nls aku[/] [--limit <count>]\nls aku/<name>[/] [--limit <count>]\nls "aku/<name>/*" [--limit <count>]\nls "aku/*/*" [--limit <count>]',
185
+ flags: { limit: "value", json: "boolean", mine: "boolean", caller: "value" },
186
+ usage: 'ls debt[/] [--limit <count>]\nls kei[/] [--limit <count>]\nls aku[/] [--limit <count>]\nls aku/<name>[/] [--limit <count>]\nls "aku/<name>/*" [--limit <count>]\nls "aku/*/*" [--limit <count>]\nls ["aku/*/*"] --mine\nls ["aku/*/*"] --caller <integration>:<id> | aku/<name>/<suffix>',
187
187
  purpose: "List debts (ls debt), contracts (ls kei), or akumas (ls aku).",
188
188
  details: [
189
189
  "ls kei lists active Contracts; accepted and abandoned ones leave the list, and their record stays in history and Git.",
190
190
  "ls aku/ lists the callable Akuma-name catalog; ls aku/<name> lists the living Akumas under one name.",
191
191
  'ls "aku/<name>/*" is the same living set in glob form; ls "aku/*/*" lists every living Akuma.',
192
+ "--mine selects the caller identity of this process and lists that caller's direct and transitive descendants in this World.",
193
+ "--caller selects an explicit caller instead; both selectors are World-local and refuse for Contract, Debt, or Akuma-name catalogues.",
194
+ "Descendant rows name their immediate calling Akuma beside the row.",
192
195
  ].join("\n"),
193
196
  },
194
197
  reconcile: {
@@ -6,6 +6,7 @@ import { composeContractLibrary, observeKeiyaku } from "../../library/contract-c
6
6
  import { validateArcMarkdown, validateContractMarkdown } from "../../library/input.js";
7
7
  import { createKeiyakuHandle } from "../../library/keiyaku.js";
8
8
  import { SettingsError, settings } from "../../settings.js";
9
+ import { akumaCallerFromEnvironment } from "../akuma-caller.js";
9
10
  import { Debts } from "../../debt/index.js";
10
11
  import { actorFromEdge } from "../actor.js";
11
12
  import { resolveInvocationCwd } from "../coordinates.js";
@@ -21,7 +22,8 @@ import { renderRegionText } from "../render/region.js";
21
22
  import { renderSettingsText, settingsJsonValue } from "../render/settings.js";
22
23
  import { canonicalContractSelector, contractFromInput, resolveContractId } from "../selectors.js";
23
24
  import { displayContext, emit, writeJson, writeStderr, writeStdout } from "../streams.js";
24
- import { CliUsageError, isBlankInput } from "../usage.js";
25
+ import { CliUsageError, commandGuide, isBlankInput } from "../usage.js";
26
+ import { CONTRACT_COMMAND_SPECS } from "./contract-help.js";
25
27
  function requiredRepo(coordinates) {
26
28
  if (coordinates.repo === undefined)
27
29
  throw new CliUsageError("this command requires a resolved Repo");
@@ -305,7 +307,7 @@ async function runNuke(command, coordinates) {
305
307
  });
306
308
  return emit(command.output, result, () => renderNukeText(result), nukeExitCode(result));
307
309
  }
308
- async function runLs(command, coordinates, home) {
310
+ async function runLs(command, coordinates, home, runtime) {
309
311
  const query = command.query;
310
312
  if (query.kind === "contracts") {
311
313
  const list = await Keiyaku.with().list({
@@ -334,11 +336,36 @@ async function runLs(command, coordinates, home) {
334
336
  throw new Error("Debt list did not return an accepted observation");
335
337
  return emit(command.output, listed, () => renderDebtCatalogue(listed.value, query.namespace ?? []), 0);
336
338
  }
339
+ const caller = await lsCaller(query.caller, coordinates, runtime);
337
340
  const listed = await Akumas.of(coordinates.world).list({
338
341
  ...(query.archetype === undefined ? {} : { archetype: query.archetype }),
339
342
  ...(query.limit === undefined ? {} : { limit: query.limit }),
343
+ ...(caller === undefined ? {} : { caller }),
340
344
  });
341
- return emit(command.output, listed, () => renderAkumaCatalogue(listed, query.archetype ?? null), 0);
345
+ return emit(command.output, listed, () => renderAkumaCatalogue(listed, query.archetype ?? null, caller), 0);
346
+ }
347
+ /** `--mine` is the process caller identity; `--caller` names the same coordinate explicitly. */
348
+ async function lsCaller(selection, coordinates, runtime) {
349
+ const usage = (diagnostic) => new CliUsageError(diagnostic, commandGuide("ls", CONTRACT_COMMAND_SPECS.ls.usage));
350
+ if (selection === undefined)
351
+ return undefined;
352
+ if (selection.kind === "external")
353
+ return selection.caller;
354
+ if (selection.kind === "akuma") {
355
+ if (coordinates.world === null)
356
+ throw usage("no Keiyaku world contains the invocation cwd");
357
+ return { kind: "akuma", world: coordinates.world, akuId: selection.akuId };
358
+ }
359
+ let caller;
360
+ try {
361
+ caller = await akumaCallerFromEnvironment(runtime.environment);
362
+ }
363
+ catch (error) {
364
+ throw usage(error instanceof Error ? error.message : String(error));
365
+ }
366
+ if (caller === undefined)
367
+ throw usage("--mine requires a caller identity; set KEIYAKU_ACTOR_ID and KEIYAKU_ACTOR_WORLD or run inside a calling host");
368
+ return caller;
342
369
  }
343
370
  async function readAkumaStatus(akuma, alias, coordinates) {
344
371
  if (coordinates.world === null)
@@ -531,7 +558,7 @@ export async function runContractCommand(command, coordinates, runtime) {
531
558
  case "nuke":
532
559
  return await runNuke(command, coordinates);
533
560
  case "ls":
534
- return await runLs(command, coordinates, home);
561
+ return await runLs(command, coordinates, home, runtime);
535
562
  case "show":
536
563
  return await runShow(command, coordinates);
537
564
  case "status":
@@ -1,8 +1,9 @@
1
1
  import type { AkumaList } from "../../akuma/akuma.js";
2
+ import { type AkumaCaller } from "../../akuma/caller.js";
2
3
  import type { ArchetypeCatalog } from "../../index.js";
3
4
  import type { ContractList } from "../../library/contract-types.js";
4
5
  import type { DebtPage, DebtRow } from "../../debt/index.js";
5
- export declare function renderAkumaCatalogue(list: AkumaList, archetype: string | null): string;
6
+ export declare function renderAkumaCatalogue(list: AkumaList, archetype: string | null, caller?: AkumaCaller): string;
6
7
  export declare function renderContractCatalogue(list: ContractList): string;
7
8
  export declare function renderArchetypeCatalogue(list: ArchetypeCatalog): string;
8
9
  export declare function renderDebtCatalogue(list: DebtPage<DebtRow>, namespace: readonly string[]): string;
@@ -1,3 +1,4 @@
1
+ import { akumaCallerKey } from "../../akuma/caller.js";
1
2
  import { abbreviateGitIds, displayGitId, gitIdsInRow, lifecycleWord, progressStrip } from "./contract-observation.js";
2
3
  import { ageText, emptyCatalogue, renderBoundedPayload, safeText } from "./terminal.js";
3
4
  import { akumaMark, contractMark } from "./marks.js";
@@ -7,11 +8,12 @@ function relativeAge(source, observedAt) {
7
8
  return null;
8
9
  return ageText(source, observedAt);
9
10
  }
10
- export function renderAkumaCatalogue(list, archetype) {
11
+ export function renderAkumaCatalogue(list, archetype, caller) {
11
12
  const rows = list.rows;
12
13
  if (rows.length === 0) {
13
14
  return emptyCatalogue("akuma");
14
15
  }
16
+ const root = caller === undefined ? undefined : akumaCallerKey(caller);
15
17
  const lines = [archetype === null ? "AKUMA // recent" : `AKUMA // ${safeText(archetype)}`, ""];
16
18
  for (const row of rows) {
17
19
  const lifeAt = "lifeAt" in row ? row.lifeAt : null;
@@ -23,7 +25,11 @@ export function renderAkumaCatalogue(list, archetype) {
23
25
  ...(activityAge === null || activityAge === lifeAge ? [] : [`activity ${activityAge}`]),
24
26
  ];
25
27
  const aliases = row.aliases.length === 0 ? "" : ` (${row.aliases.map((alias) => safeText(alias)).join(" ")})`;
26
- lines.push(`${akumaMark(row.life)} ${safeText(row.id)}${aliases} · ${row.life}${ages.length === 0 ? "" : ` · ${ages.join(" · ")}`}`);
28
+ const rowCaller = "caller" in row ? row.caller : undefined;
29
+ const by = root !== undefined && rowCaller?.kind === "akuma" && akumaCallerKey(rowCaller) !== root
30
+ ? ` · by ${safeText(rowCaller.akuId)}`
31
+ : "";
32
+ lines.push(`${akumaMark(row.life)} ${safeText(row.id)}${aliases} · ${row.life}${ages.length === 0 ? "" : ` · ${ages.join(" · ")}`}${by}`);
27
33
  }
28
34
  if (list.hasMore)
29
35
  lines.push("…");
@@ -7,9 +7,4 @@ export type SquareSeatControl = Readonly<{
7
7
  signal?: AbortSignal;
8
8
  budgetMs?: number;
9
9
  }>;
10
- /**
11
- * The Square participant name this submitting session should speak as, or
12
- * undefined when no honest choice exists. Identity preparation is optional, so it
13
- * never refuses the enclosing operation and always settles within one budget.
14
- */
15
10
  export declare function squareSeat(input: SquareSeatInput, control?: SquareSeatControl): Promise<string | undefined>;