@astrosheep/keiyaku 4.6.13 → 4.6.15

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
@@ -216,8 +216,10 @@ function isSignalKind(kind) {
216
216
  function handlers(pluginId, instance) {
217
217
  if (!object(instance))
218
218
  throw new TypeError("plugin activation must return an object");
219
- if (Object.keys(instance).some((name) => name !== "signals"))
219
+ if (Object.keys(instance).some((name) => name !== "signals" && name !== "waitDeferral"))
220
220
  throw new TypeError("plugin instance has unknown fields");
221
+ if (instance.waitDeferral !== undefined && typeof instance.waitDeferral !== "function")
222
+ throw new TypeError("plugin waitDeferral must be a function");
221
223
  if (instance.signals === undefined)
222
224
  return [];
223
225
  if (!object(instance.signals))
@@ -283,11 +285,11 @@ async function activate(input) {
283
285
  catch (error) {
284
286
  markStarted();
285
287
  reportPluginDiagnostic(report, selected.id, "import", error);
286
- return [];
288
+ return { handlers: [] };
287
289
  }
288
290
  if (cancellation.aborted) {
289
291
  markStarted();
290
- return [];
292
+ return { handlers: [] };
291
293
  }
292
294
  let candidate;
293
295
  try {
@@ -301,7 +303,7 @@ async function activate(input) {
301
303
  catch (error) {
302
304
  markStarted();
303
305
  reportPluginDiagnostic(report, selected.id, "validation", error);
304
- return [];
306
+ return { handlers: [] };
305
307
  }
306
308
  let context;
307
309
  try {
@@ -320,24 +322,27 @@ async function activate(input) {
320
322
  catch (error) {
321
323
  markStarted();
322
324
  reportPluginDiagnostic(report, selected.id, "validation", error);
323
- return [];
325
+ return { handlers: [] };
324
326
  }
325
327
  if (cancellation.aborted) {
326
328
  markStarted();
327
- return [];
329
+ return { handlers: [] };
328
330
  }
329
331
  markStarted();
330
332
  try {
331
333
  const instance = await candidate.activate(context, cancellation);
332
334
  if (cancellation.aborted)
333
- return [];
335
+ return { handlers: [] };
334
336
  const registered = handlers(candidate.manifest.id, instance);
335
337
  activated.add(candidate.manifest.id);
336
- return registered;
338
+ return {
339
+ handlers: registered,
340
+ ...(instance.waitDeferral === undefined ? {} : { waitDeferral: instance.waitDeferral.bind(instance) }),
341
+ };
337
342
  }
338
343
  catch (error) {
339
344
  reportPluginDiagnostic(report, selected.id, "activation", error);
340
- return [];
345
+ return { handlers: [] };
341
346
  }
342
347
  }
343
348
  async function deliver(entry, signal, reportDiagnostic, timeoutMs) {
@@ -347,6 +352,60 @@ async function deliver(entry, signal, reportDiagnostic, timeoutMs) {
347
352
  if (result.kind === "failed")
348
353
  reportPluginDiagnostic(reportDiagnostic, entry.pluginId, "signal", result.error);
349
354
  }
355
+ async function closeWaitDeferral(pluginId, scope, reportDiagnostic) {
356
+ const result = await boundedAttempt((cancellation) => scope.close(cancellation));
357
+ if (result.kind === "timed-out")
358
+ reportPluginDiagnostic(reportDiagnostic, pluginId, "wait deferral cleanup", timeoutError());
359
+ if (result.kind === "failed")
360
+ reportPluginDiagnostic(reportDiagnostic, pluginId, "wait deferral cleanup", result.error);
361
+ }
362
+ async function acquireWaitDeferral({ pluginId, activation }, input, signal, deadline, reportDiagnostic) {
363
+ const controller = new AbortController();
364
+ const abort = () => controller.abort(signal?.reason);
365
+ if (signal?.aborted)
366
+ abort();
367
+ else
368
+ signal?.addEventListener("abort", abort, { once: true });
369
+ const cancellation = controller.signal;
370
+ const timer = setTimeout(() => controller.abort(timeoutError()), Math.max(0, deadline - performance.now()));
371
+ const completed = activation
372
+ .then(async (activated) => {
373
+ if (cancellation.aborted)
374
+ return undefined;
375
+ const value = await activated.waitDeferral?.(input, cancellation);
376
+ return value === undefined ? undefined : waitDeferralResource(value, pluginId, reportDiagnostic);
377
+ })
378
+ .catch((error) => {
379
+ if (!cancellation.aborted)
380
+ reportPluginDiagnostic(reportDiagnostic, pluginId, "wait deferral", error);
381
+ return undefined;
382
+ });
383
+ const result = await settleBefore(completed, cancellation);
384
+ clearTimeout(timer);
385
+ signal?.removeEventListener("abort", abort);
386
+ if (result !== ABORTED)
387
+ return result;
388
+ if (signal?.aborted !== true)
389
+ reportPluginDiagnostic(reportDiagnostic, pluginId, "wait deferral", timeoutError());
390
+ void completed.then(async (scope) => {
391
+ if (scope !== undefined)
392
+ await closeWaitDeferral(pluginId, scope, reportDiagnostic);
393
+ });
394
+ return undefined;
395
+ }
396
+ function waitDeferralResource(value, pluginId, reportDiagnostic) {
397
+ try {
398
+ const close = object(value) ? value.close : undefined;
399
+ if (typeof close !== "function")
400
+ throw new TypeError("resource close must be a function");
401
+ // Normal and late acquisition have disjoint cleanup paths; the plugin owns its close lifetime.
402
+ return { close: close.bind(value) };
403
+ }
404
+ catch (error) {
405
+ reportPluginDiagnostic(reportDiagnostic, pluginId, "wait deferral validation", error);
406
+ return undefined;
407
+ }
408
+ }
350
409
  export async function pluginRuntime(input) {
351
410
  let runtime = PROCESS_RUNTIMES.get(input.world);
352
411
  if (runtime === undefined) {
@@ -369,12 +428,12 @@ async function createPluginRuntime(input) {
369
428
  const result = await boundedAttempt((cancellation) => activate({ selected: entry, world: input.world, activated, report, cancellation, onStarted: releaseStarted }));
370
429
  releaseStarted();
371
430
  if (result.kind === "settled")
372
- return { handlers: result.value, timedOut: false };
431
+ return { ...result.value, timedOut: false };
373
432
  reportPluginDiagnostic(report, entry.id, "activation", result.kind === "failed" ? result.error : timeoutError());
374
433
  return { handlers: [], timedOut: result.kind === "timed-out" };
375
434
  });
376
435
  previousStarted = started;
377
- return activation;
436
+ return { pluginId: entry.id, activation };
378
437
  });
379
438
  return Object.freeze({
380
439
  async emit(signal, reportDiagnostic = report) {
@@ -382,10 +441,10 @@ async function createPluginRuntime(input) {
382
441
  const expiry = new AbortController();
383
442
  const timer = setTimeout(() => expiry.abort(), PLUGIN_ATTEMPT_TIMEOUT_MS);
384
443
  try {
385
- await Promise.all(activations.map(async (activation, index) => {
444
+ await Promise.all(activations.map(async ({ pluginId, activation }) => {
386
445
  const activated = await settleBefore(activation, expiry.signal);
387
446
  if (activated === ABORTED || activated.timedOut) {
388
- reportPluginDiagnostic(reportDiagnostic, selected[index].id, "signal", timeoutError());
447
+ reportPluginDiagnostic(reportDiagnostic, pluginId, "signal", timeoutError());
389
448
  if (activated === ABORTED)
390
449
  return;
391
450
  }
@@ -398,5 +457,18 @@ async function createPluginRuntime(input) {
398
457
  clearTimeout(timer);
399
458
  }
400
459
  },
460
+ async withWaitDeferrals(akumaIds, signal, action, reportDiagnostic = report) {
461
+ signal?.throwIfAborted();
462
+ const input = Object.freeze({ akumaIds: Object.freeze([...akumaIds]) });
463
+ const deadline = performance.now() + PLUGIN_ATTEMPT_TIMEOUT_MS;
464
+ const scopes = await Promise.all(activations.map((plugin) => acquireWaitDeferral(plugin, input, signal, deadline, reportDiagnostic)));
465
+ try {
466
+ signal?.throwIfAborted();
467
+ return await action();
468
+ }
469
+ finally {
470
+ await Promise.all(scopes.map((scope, index) => scope === undefined ? undefined : closeWaitDeferral(activations[index].pluginId, scope, reportDiagnostic)));
471
+ }
472
+ },
401
473
  });
402
474
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/keiyaku",
3
- "version": "4.6.13",
3
+ "version": "4.6.15",
4
4
  "workspaces": [
5
5
  "plugins/square",
6
6
  "plugins/paseo/forwarder"
@@ -81,8 +81,8 @@
81
81
  "dependencies": {
82
82
  "@agentclientprotocol/sdk": "^1.3.0",
83
83
  "@anthropic-ai/claude-agent-sdk": "^0.3.226",
84
- "@astrosheep/keiyaku-plugin-square": "0.1.10",
85
- "@astrosheep/square": "^0.3.81",
84
+ "@astrosheep/keiyaku-plugin-square": "0.1.11",
85
+ "@astrosheep/square": "^0.3.91",
86
86
  "@getpaseo/client": "0.11.2",
87
87
  "@getpaseo/protocol": "0.11.2",
88
88
  "@opencode/client": "2.0.20",
@@ -36,6 +36,9 @@ selects the Contract repository; it never retargets that World. `call
36
36
  cwd). Without it, the call uses the invocation cwd; `--contract` associates a
37
37
  Contract without selecting an execution directory.
38
38
 
39
+ An Akuma caller can find its own delegation tree with `keiyaku ls --mine` (or an
40
+ explicit `--caller`); see `keiyaku-akuma` for the roster and its text labels.
41
+
39
42
  `deliver` and `review` may take time after their leading admission.
40
43
  `deliver --dry-run` only observes the prospective candidate, standing evidence,
41
44
  and target followability; it never runs Verification or hooks or admits work.
@@ -87,16 +87,36 @@ keiyaku status
87
87
  keiyaku status @reviewer
88
88
  keiyaku ls aku/
89
89
  keiyaku ls "aku/*/*"
90
+ keiyaku ls --mine
90
91
  keiyaku history @reviewer --last
91
92
  keiyaku history @reviewer --id turn/3
92
93
  ```
93
94
 
94
95
  `status` shows the roster or one Akuma's workdir and effective actions.
95
96
  `ls aku/` shows callable names; `ls "aku/*/*"` shows born workers.
97
+ `ls --mine` narrows that roster to Akumas this process's caller called and their
98
+ transitive descendants in this World; `--caller <integration>:<id>` or
99
+ `--caller aku/<name>/<suffix>` names the same selection explicitly. Descendant
100
+ rows show their immediate caller as `· by <akuId>`. The selection is World-local
101
+ and refuses for Contract, Debt, and Akuma-name catalogues.
96
102
  `history --last` reads the latest complete answer; `history --id` reads an
97
103
  exact result named by status or wait. Use the complete AkuId when an alias may
98
104
  move; see `history --help` for paging.
99
105
 
106
+ ## Retention
107
+
108
+ An Akuma whose latest durable life or activity is older than fifteen days may be
109
+ retired after a later successful `call` or `fork`. Retention keeps any running
110
+ Akuma, any current alias target, any Akuma dispatched to a Contract that is not
111
+ yet `claimed` or `abandoned`, unborn or stillborn allocations, and unreadable
112
+ entries. It removes only the Akuma's recognized runtime custody; it never kills
113
+ a worker and never rewrites another Akuma, Contract, Debt, or alias. Its final
114
+ Heart re-read keeps a pending Tell it observes; a Tell admitted immediately
115
+ after that read is best-effort, not atomic. There is no timer and no prune
116
+ command. Observation and Tell commands (`status`, `ls`, `history`, `wait`,
117
+ `tell`, `ask`) never trigger retention; without a later successful
118
+ `call`/`fork`, old entries simply remain.
119
+
100
120
  ## Stop and branch
101
121
 
102
122
  ```bash