@astrosheep/keiyaku 4.4.0 → 4.5.1

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 (68) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +4 -2
  2. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/package.json +9 -3
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +16 -5
  5. package/build/integrations/pi/keiyaku.ts +159 -0
  6. package/build/src/akuma/abort.js +9 -9
  7. package/build/src/akuma/akuma-handle.d.ts +4 -2
  8. package/build/src/akuma/akuma-handle.js +14 -2
  9. package/build/src/akuma/akuma-product.d.ts +32 -3
  10. package/build/src/akuma/akuma-product.js +44 -20
  11. package/build/src/akuma/akuma.d.ts +0 -1
  12. package/build/src/akuma/body.d.ts +1 -7
  13. package/build/src/akuma/body.js +35 -13
  14. package/build/src/akuma/heart/index.js +7 -3
  15. package/build/src/akuma/heart/rows.d.ts +1 -1
  16. package/build/src/akuma/heart/rows.js +3 -3
  17. package/build/src/akuma/heart/storage.d.ts +1 -0
  18. package/build/src/akuma/heart/storage.js +28 -6
  19. package/build/src/akuma/identity.d.ts +1 -0
  20. package/build/src/akuma/identity.js +7 -0
  21. package/build/src/akuma/index.d.ts +1 -2
  22. package/build/src/akuma/projection.d.ts +18 -2
  23. package/build/src/akuma/projection.js +11 -1
  24. package/build/src/akuma/providers/claude/index.js +68 -37
  25. package/build/src/akuma/publication.d.ts +9 -3
  26. package/build/src/akuma/publication.js +20 -3
  27. package/build/src/akuma/request-lifecycle.d.ts +45 -0
  28. package/build/src/akuma/request-lifecycle.js +171 -0
  29. package/build/src/akuma/request-serve.d.ts +9 -32
  30. package/build/src/akuma/request-serve.js +48 -178
  31. package/build/src/akuma/request-wire.js +1 -1
  32. package/build/src/akuma/requests.js +3 -2
  33. package/build/src/cli/commands/akuma-invoke.d.ts +11 -2
  34. package/build/src/cli/commands/akuma-invoke.js +29 -7
  35. package/build/src/cli/commands/akuma.d.ts +1 -0
  36. package/build/src/cli/commands/akuma.js +16 -6
  37. package/build/src/cli/commands/contract.d.ts +3 -2
  38. package/build/src/cli/commands/contract.js +5 -2
  39. package/build/src/cli/commands/status-set.d.ts +4 -0
  40. package/build/src/cli/commands/status-set.js +61 -0
  41. package/build/src/cli/invoke.js +6 -3
  42. package/build/src/cli/parse.js +1 -1
  43. package/build/src/cli/render/akuma-activity.js +11 -0
  44. package/build/src/cli/render/akuma.js +7 -0
  45. package/build/src/cli/render/status-set.d.ts +5 -0
  46. package/build/src/cli/render/status-set.js +14 -0
  47. package/build/src/cli/render/text.js +3 -0
  48. package/build/src/cli/result.d.ts +15 -1
  49. package/build/src/cli/runtime.js +2 -0
  50. package/build/src/cli/square-edge.d.ts +6 -0
  51. package/build/src/cli/square-edge.js +74 -0
  52. package/build/src/contract-worktree.js +18 -5
  53. package/build/src/library/akuma-creation.d.ts +14 -3
  54. package/build/src/library/akuma-creation.js +31 -18
  55. package/build/src/library/fleet.d.ts +12 -1
  56. package/build/src/library/fleet.js +21 -5
  57. package/build/src/library/repo.js +3 -1
  58. package/build/src/runtime/proc/process-exit.js +6 -1
  59. package/build/src/runtime/proc/run.js +6 -10
  60. package/build/src/runtime/proc/termination.d.ts +1 -0
  61. package/build/src/runtime/proc/termination.js +22 -2
  62. package/build/src/runtime/proc/windows-launch.exe +0 -0
  63. package/build/src/runtime/proc/windows-run.js +10 -7
  64. package/build/src/world.d.ts +2 -0
  65. package/build/src/world.js +4 -0
  66. package/package.json +8 -3
  67. package/build/src/akuma/result-route.d.ts +0 -2
  68. package/build/src/akuma/result-route.js +0 -1
@@ -40,13 +40,6 @@ export async function spawnDetachedProcess(input) {
40
40
  return spawnWindowsRetainedProcess(input);
41
41
  const log = await open(input.log, "a");
42
42
  let launched = false;
43
- let logClosed = false;
44
- const closeLog = async () => {
45
- if (logClosed)
46
- return;
47
- logClosed = true;
48
- await log.close();
49
- };
50
43
  try {
51
44
  const from = (await log.stat()).size;
52
45
  const child = spawn(input.argv[0], input.argv.slice(1), spawnOptionsFor(input, ["ignore", log.fd, log.fd]));
@@ -62,14 +55,16 @@ export async function spawnDetachedProcess(input) {
62
55
  const status = detachedExitStatus(code, signal);
63
56
  let failure;
64
57
  let result;
58
+ let exitLog;
65
59
  try {
66
- result = await retainDetachedExitEvidence(log, input.log, from, code, signal);
60
+ exitLog = await open(input.log, "r+");
61
+ result = await retainDetachedExitEvidence(exitLog, input.log, from, code, signal);
67
62
  }
68
63
  catch (error) {
69
64
  failure = error;
70
65
  }
71
66
  try {
72
- await closeLog();
67
+ await exitLog?.close();
73
68
  }
74
69
  catch (error) {
75
70
  failure ??= error;
@@ -89,6 +84,7 @@ export async function spawnDetachedProcess(input) {
89
84
  if (child.pid === undefined)
90
85
  throw new Error("detached process spawned without a pid");
91
86
  const pid = child.pid;
87
+ await log.close();
92
88
  launched = true;
93
89
  return {
94
90
  pid,
@@ -99,7 +95,7 @@ export async function spawnDetachedProcess(input) {
99
95
  }
100
96
  finally {
101
97
  if (!launched)
102
- await closeLog();
98
+ await log.close();
103
99
  }
104
100
  }
105
101
  function terminalOutcome(code, stdout, stderr, consuming) {
@@ -1,3 +1,4 @@
1
1
  import { type ChildProcess } from "node:child_process";
2
2
  export declare function terminateWindowsTree(pid: number): Promise<void>;
3
+ export declare function settleWindowsTermination(exit: Promise<void>): Promise<void>;
3
4
  export declare function terminateOwnedProcess(child: ChildProcess, force?: boolean): Promise<void>;
@@ -2,6 +2,7 @@ import { execFile } from "node:child_process";
2
2
  import { setTimeout as delay } from "node:timers/promises";
3
3
  import { promisify } from "node:util";
4
4
  const TERMINATION_GRACE_MS = 250;
5
+ const WINDOWS_TERMINATION_SETTLE_MS = 250;
5
6
  const WINDOWS_TERMINATION_TIMEOUT_MS = 1_000;
6
7
  const execFileAsync = promisify(execFile);
7
8
  function ignoreMissingProcess(error) {
@@ -23,6 +24,20 @@ export async function terminateWindowsTree(pid) {
23
24
  throw error;
24
25
  }
25
26
  }
27
+ export async function settleWindowsTermination(exit) {
28
+ const settled = await Promise.race([
29
+ exit.then(() => true),
30
+ delay(WINDOWS_TERMINATION_TIMEOUT_MS, false, { ref: false }).then(() => false),
31
+ ]);
32
+ if (!settled)
33
+ throw new Error("Windows process did not exit after taskkill");
34
+ await delay(WINDOWS_TERMINATION_SETTLE_MS);
35
+ }
36
+ function closeWindowsStreams(child) {
37
+ child.stdin?.destroy();
38
+ child.stdout?.destroy();
39
+ child.stderr?.destroy();
40
+ }
26
41
  export async function terminateOwnedProcess(child, force = false) {
27
42
  const pid = child.pid;
28
43
  if (pid === undefined || child.exitCode !== null || child.signalCode !== null)
@@ -39,8 +54,13 @@ export async function terminateOwnedProcess(child, force = false) {
39
54
  });
40
55
  });
41
56
  if (process.platform === "win32") {
42
- await terminateWindowsTree(pid);
43
- await exit;
57
+ try {
58
+ await terminateWindowsTree(pid);
59
+ await settleWindowsTermination(exit);
60
+ }
61
+ finally {
62
+ closeWindowsStreams(child);
63
+ }
44
64
  return;
45
65
  }
46
66
  if (force) {
@@ -3,8 +3,8 @@ import { createInterface } from "node:readline";
3
3
  import { launchFailure, spawnWindowsLauncher } from "./launch.js";
4
4
  import { detachedExitStatus, retainDetachedExitEvidence } from "./process-exit.js";
5
5
  import { createProcessLifecycle } from "./lifecycle.js";
6
- import { terminateWindowsTree } from "./termination.js";
7
- function createWindowsContext(input, log, from, closeLog) {
6
+ import { settleWindowsTermination, terminateWindowsTree } from "./termination.js";
7
+ function createWindowsContext(input, from, closeLog) {
8
8
  const child = spawnWindowsLauncher(input);
9
9
  const reader = createInterface({ input: child.stdout });
10
10
  let startedResolve;
@@ -23,7 +23,6 @@ function createWindowsContext(input, log, from, closeLog) {
23
23
  child.stderr?.setEncoding("utf8");
24
24
  const context = {
25
25
  input,
26
- log,
27
26
  from,
28
27
  closeLog,
29
28
  child,
@@ -79,14 +78,16 @@ function recordWindowsExit(context, code) {
79
78
  void (async () => {
80
79
  let failure;
81
80
  let result;
81
+ let exitLog;
82
82
  try {
83
- result = await retainDetachedExitEvidence(context.log, context.input.log, context.from, code, null);
83
+ exitLog = await open(context.input.log, "r+");
84
+ result = await retainDetachedExitEvidence(exitLog, context.input.log, context.from, code, null);
84
85
  }
85
86
  catch (error) {
86
87
  failure = error;
87
88
  }
88
89
  try {
89
- await context.closeLog();
90
+ await exitLog?.close();
90
91
  }
91
92
  catch (error) {
92
93
  failure ??= error;
@@ -98,6 +99,7 @@ function recordWindowsExit(context, code) {
98
99
  const status = detachedExitStatus(code, null);
99
100
  throw new Error(`pre-admission ${status}: run-log evidence unavailable: ${failure instanceof Error ? failure.message : String(failure)}`);
100
101
  }
102
+ await settleWindowsTermination(Promise.resolve());
101
103
  context.exitResolve(result);
102
104
  })().catch(context.exitReject);
103
105
  }
@@ -143,7 +145,7 @@ function installWindowsProtocol(context) {
143
145
  function ownedWindowsProcess(context, pid) {
144
146
  const lifecycle = createProcessLifecycle(async () => {
145
147
  await terminateWindowsTree(pid);
146
- await context.exited;
148
+ await settleWindowsTermination(context.exited.then(() => undefined));
147
149
  }, () => {
148
150
  context.released = true;
149
151
  context.reader.close();
@@ -170,10 +172,11 @@ export async function spawnWindowsRetainedProcess(input) {
170
172
  logClosed = true;
171
173
  await log.close();
172
174
  };
173
- const context = createWindowsContext(input, log, (await log.stat()).size, closeLog);
175
+ const context = createWindowsContext(input, (await log.stat()).size, closeLog);
174
176
  installWindowsProtocol(context);
175
177
  try {
176
178
  const pid = await context.startedPromise;
179
+ await closeLog();
177
180
  return ownedWindowsProcess(context, pid);
178
181
  }
179
182
  catch (error) {
@@ -11,6 +11,8 @@ export type WorldResolution = Readonly<{
11
11
  candidate: WorldRoot | null;
12
12
  establish: () => Promise<WorldRoot>;
13
13
  }>;
14
+ export declare const KEIYAKU_SQUARE_FILE = "KEIYAKU.square";
15
+ export declare function keiyakuSquarePath(worldRoot: string): string;
14
16
  export declare class WorldError extends Error {
15
17
  readonly kind: "invalid-world" | "home-world" | "root-world";
16
18
  constructor(kind: "invalid-world" | "home-world" | "root-world", message: string);
@@ -2,6 +2,10 @@ import { mkdir, realpath, stat } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
3
  import { dirname, join, parse, resolve } from "node:path";
4
4
  const WORLD_BRAND = Symbol("keiyaku.world");
5
+ export const KEIYAKU_SQUARE_FILE = "KEIYAKU.square";
6
+ export function keiyakuSquarePath(worldRoot) {
7
+ return join(worldRoot, ".square", KEIYAKU_SQUARE_FILE);
8
+ }
5
9
  export class WorldError extends Error {
6
10
  kind;
7
11
  constructor(kind, message) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/keiyaku",
3
- "version": "4.4.0",
3
+ "version": "4.5.1",
4
4
  "files": [
5
5
  "build"
6
6
  ],
@@ -35,14 +35,19 @@
35
35
  "build": "node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('build', { recursive: true, force: true })\" && tsc -p tsconfig.json && node scripts/copy-integrations.js && node scripts/build-windows-launcher.js && node --input-type=module -e \"import { chmodSync } from 'node:fs'; chmodSync('build/src/cli/index.js', 0o755)\"",
36
36
  "prepack": "node scripts/verify-package.js",
37
37
  "postpack": "node scripts/verify-package.js",
38
+ "harness:version": "node scripts/update-harness-version.js",
38
39
  "review:model-impact": "node --import tsx scripts/model-change-impact.ts",
39
40
  "format": "prettier --write --print-width 120 \"src/**/*.ts\" \"scripts/**/*.ts\"",
40
41
  "format:check": "prettier --check --print-width 120 \"src/**/*.ts\" \"scripts/**/*.ts\"",
41
- "test": "npm run format:check && npm run build && npm run test:architecture && npm run test:maintainability && npm run test:reachability && node --import tsx scripts/run-tests.mjs",
42
+ "test": "npm run format:check && npm run build && npm run test:architecture && npm run test:maintainability && npm run test:reachability && npm run test:parallel",
42
43
  "test:architecture": "node --import tsx scripts/check-architecture.ts",
43
44
  "test:e2e": "npm run build && node scripts/run-tests.mjs tests/packaged-cli.e2e.ts",
45
+ "test:local": "node scripts/run-tests.mjs --suite local",
46
+ "test:integration": "node scripts/run-tests.mjs --suite integration",
47
+ "test:parallel": "node scripts/run-tests.mjs --suite local --test-concurrency=8 && node scripts/run-tests.mjs --suite integration --test-concurrency=8",
44
48
  "test:maintainability": "node scripts/check-maintainability.js",
45
49
  "test:focused": "node scripts/run-tests.mjs",
50
+ "test:focused:serial": "node scripts/run-tests.mjs --test-concurrency=1",
46
51
  "test:reachability": "knip --include files,dependencies,unlisted,unresolved,duplicates",
47
52
  "test:typecheck": "tsc -p tsconfig.json --noEmit"
48
53
  },
@@ -59,7 +64,7 @@
59
64
  "dependencies": {
60
65
  "@agentclientprotocol/sdk": "^1.3.0",
61
66
  "@anthropic-ai/claude-agent-sdk": "^0.3.226",
62
- "@astrosheep/square": "0.3.13",
67
+ "@astrosheep/square": "0.3.23",
63
68
  "@earendil-works/pi-coding-agent": "0.80.10",
64
69
  "@opencode-ai/sdk": "1.18.3",
65
70
  "cross-spawn": "^7.0.6",
@@ -1,2 +0,0 @@
1
- /** Opaque, serializable caller result address carried across the Body process. */
2
- export type ResultRoute = Readonly<unknown>;
@@ -1 +0,0 @@
1
- export {};