@astrosheep/keiyaku 4.6.19 → 4.6.21

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 (102) hide show
  1. package/README.md +7 -3
  2. package/build/plugins/pi/README.md +111 -0
  3. package/build/plugins/pi/akuma-tools.ts +310 -0
  4. package/build/plugins/pi/keiyaku.ts +2 -0
  5. package/build/src/akuma/akuma-call.d.ts +4 -1
  6. package/build/src/akuma/akuma-call.js +24 -7
  7. package/build/src/akuma/akuma-instance.d.ts +28 -8
  8. package/build/src/akuma/akuma-instance.js +85 -42
  9. package/build/src/akuma/akuma-owner.d.ts +18 -9
  10. package/build/src/akuma/akuma-owner.js +31 -25
  11. package/build/src/akuma/akuma-selection-execution.d.ts +4 -1
  12. package/build/src/akuma/akuma-selection-execution.js +8 -4
  13. package/build/src/akuma/akuma.d.ts +17 -0
  14. package/build/src/akuma/body-launch.d.ts +107 -0
  15. package/build/src/akuma/body-launch.js +57 -0
  16. package/build/src/akuma/body-supervisor.d.ts +7 -6
  17. package/build/src/akuma/body-supervisor.js +17 -12
  18. package/build/src/akuma/body.d.ts +13 -94
  19. package/build/src/akuma/body.js +124 -296
  20. package/build/src/akuma/call-input.d.ts +2 -0
  21. package/build/src/akuma/call-input.js +21 -1
  22. package/build/src/akuma/call-request.d.ts +6 -2
  23. package/build/src/akuma/call-request.js +9 -1
  24. package/build/src/akuma/detached-body.d.ts +9 -0
  25. package/build/src/akuma/detached-body.js +94 -0
  26. package/build/src/akuma/execution-instructions.d.ts +8 -0
  27. package/build/src/akuma/execution-instructions.js +17 -0
  28. package/build/src/akuma/heart/facts.d.ts +21 -1
  29. package/build/src/akuma/heart/facts.js +10 -0
  30. package/build/src/akuma/heart/index.d.ts +16 -1
  31. package/build/src/akuma/heart/index.js +95 -30
  32. package/build/src/akuma/heart/rows.d.ts +14 -2
  33. package/build/src/akuma/heart/rows.js +48 -4
  34. package/build/src/akuma/heart/schema.d.ts +4 -1
  35. package/build/src/akuma/heart/schema.js +87 -30
  36. package/build/src/akuma/heart/soul.js +4 -0
  37. package/build/src/akuma/heart/storage.d.ts +1 -0
  38. package/build/src/akuma/heart/storage.js +59 -15
  39. package/build/src/akuma/heart/timeline.d.ts +6 -4
  40. package/build/src/akuma/heart/timeline.js +14 -4
  41. package/build/src/akuma/in-process-body.d.ts +5 -0
  42. package/build/src/akuma/in-process-body.js +57 -0
  43. package/build/src/akuma/index.d.ts +1 -1
  44. package/build/src/akuma/projection.d.ts +44 -1
  45. package/build/src/akuma/projection.js +13 -1
  46. package/build/src/akuma/provider.d.ts +4 -0
  47. package/build/src/akuma/providers/acp/core.js +1 -1
  48. package/build/src/akuma/providers/acp/index.js +3 -0
  49. package/build/src/akuma/providers/claude/index.js +8 -5
  50. package/build/src/akuma/providers/codex-app-server/configuration.d.ts +5 -0
  51. package/build/src/akuma/providers/codex-app-server/configuration.js +72 -0
  52. package/build/src/akuma/providers/codex-app-server/index.js +7 -10
  53. package/build/src/akuma/providers/execution-environment.d.ts +8 -2
  54. package/build/src/akuma/providers/execution-environment.js +10 -1
  55. package/build/src/akuma/providers/grok-build/index.js +23 -16
  56. package/build/src/akuma/providers/index.d.ts +1 -0
  57. package/build/src/akuma/providers/index.js +8 -0
  58. package/build/src/akuma/providers/opencode-sdk/index.js +9 -0
  59. package/build/src/akuma/providers/opencode-sdk/session.d.ts +6 -0
  60. package/build/src/akuma/providers/opencode-sdk/session.js +4 -3
  61. package/build/src/akuma/providers/paseo/index.js +8 -2
  62. package/build/src/akuma/providers/pi/index.js +27 -10
  63. package/build/src/akuma/publication.d.ts +4 -3
  64. package/build/src/akuma/publication.js +21 -18
  65. package/build/src/akuma/selection-observation.d.ts +51 -0
  66. package/build/src/akuma/selection-owner-port.d.ts +2 -1
  67. package/build/src/akuma/selection-owner-port.js +4 -3
  68. package/build/src/akuma/turn-drive.d.ts +4 -2
  69. package/build/src/akuma/turn-drive.js +58 -21
  70. package/build/src/akuma-body.d.ts +0 -10
  71. package/build/src/akuma-body.js +13 -118
  72. package/build/src/body-host.d.ts +28 -0
  73. package/build/src/body-host.js +86 -0
  74. package/build/src/body-requests.d.ts +13 -0
  75. package/build/src/body-requests.js +131 -0
  76. package/build/src/cli/commands/akuma-grammar.d.ts +8 -1
  77. package/build/src/cli/commands/akuma-grammar.js +36 -6
  78. package/build/src/cli/commands/akuma.js +11 -0
  79. package/build/src/cli/commands/report.d.ts +6 -0
  80. package/build/src/cli/commands/report.js +31 -0
  81. package/build/src/cli/parse.js +1 -0
  82. package/build/src/cli/render/akuma-activity.js +27 -17
  83. package/build/src/cli/render/terminal.d.ts +5 -0
  84. package/build/src/cli/render/terminal.js +7 -0
  85. package/build/src/cli/runtime.js +4 -1
  86. package/build/src/index.d.ts +3 -1
  87. package/build/src/index.js +1 -0
  88. package/build/src/library/akuma-creation.d.ts +7 -3
  89. package/build/src/library/akuma-creation.js +23 -11
  90. package/build/src/library/akumas.d.ts +4 -1
  91. package/build/src/library/akumas.js +13 -5
  92. package/build/src/library/selection.d.ts +2 -1
  93. package/build/src/library/selection.js +6 -3
  94. package/build/src/plugin/public.d.ts +11 -0
  95. package/build/src/plugin/runtime.js +1 -0
  96. package/build/src/runtime/proc/windows-launch.exe +0 -0
  97. package/package.json +3 -3
  98. package/plugins/paseo/plugin/server/keiyaku.ts +1 -0
  99. package/plugins/paseo/plugin/server/mirror.ts +2 -2
  100. package/plugins/pi/README.md +111 -0
  101. package/plugins/pi/akuma-tools.ts +310 -0
  102. package/plugins/pi/keiyaku.ts +2 -0
@@ -2,7 +2,7 @@ import { defaultWaitComplete } from "../../akuma/akuma-observe.js";
2
2
  import { renderDiffstat, toolContent, toolRepr } from "./akuma-tool.js";
3
3
  import { dispositionText } from "./debt.js";
4
4
  import { debtDispositionMark } from "./marks.js";
5
- import { DEFAULT_CLI_COLUMNS, displayColumns, padToDisplay, renderBoundedPayload, renderBoundedTextBlock, safeText, takeDisplayColumns, takeDisplayColumnsFromEnd, truncateDisplayText, truncateMiddleDisplayText, } from "./terminal.js";
5
+ import { DEFAULT_CLI_COLUMNS, diagnosticLines, displayColumns, padToDisplay, renderBoundedPayload, renderBoundedTextBlock, safeText, takeDisplayColumns, takeDisplayColumnsFromEnd, truncateDisplayText, truncateMiddleDisplayText, } from "./terminal.js";
6
6
  export const DEFAULT_CONTEXT = { columns: DEFAULT_CLI_COLUMNS, color: false };
7
7
  /** The action family's fixed 3-cell state margin and minimum verb cell. */
8
8
  const ACTION_MARGIN = " ";
@@ -64,8 +64,8 @@ export function panelHead(id, alias, contract, context = []) {
64
64
  export function actionReceipt(input) {
65
65
  return [`${input.mark} ${input.verb} ${identity(input.id, input.alias)}`, ...subordinateLines(input.evidence ?? [])];
66
66
  }
67
- function contractFacts(contract) {
68
- return contract.kind === "failed" ? [`! contract failed ${safeText(contract.diagnostic)}`] : [];
67
+ function contractFacts(contract, columns) {
68
+ return contract.kind === "failed" ? diagnosticLines("contract association", contract.diagnostic, columns) : [];
69
69
  }
70
70
  /** The action family's one mark cell: a settled success stays blank, and live is only a frame mark. */
71
71
  function toolMargin(row, live) {
@@ -76,7 +76,8 @@ function toolMargin(row, live) {
76
76
  return row.state.status === "ok" ? ACTION_MARGIN : "! ";
77
77
  }
78
78
  function rowText(row, tool) {
79
- if (row.kind === "said" ||
79
+ if (row.kind === "report" ||
80
+ row.kind === "said" ||
80
81
  row.kind === "thought" ||
81
82
  row.kind === "note" ||
82
83
  row.kind === "call" ||
@@ -89,7 +90,7 @@ function rowText(row, tool) {
89
90
  if (row.kind === "outcome")
90
91
  return row.outcome.kind === "answered"
91
92
  ? { text: row.outcome.answer, lines: 3 }
92
- : { text: row.outcome.diagnostic, lines: 2 };
93
+ : { text: row.outcome.kind === "canceled" ? "canceled" : row.outcome.diagnostic, lines: 2 };
93
94
  if (row.kind === "turn")
94
95
  return { text: "", lines: 1 };
95
96
  if (row.kind !== "tool")
@@ -118,10 +119,15 @@ function rowPrefix(row, tool, columns, view) {
118
119
  return actionPrefix(toolMargin(row, view.live === true), tool.label, columns);
119
120
  if (row.kind === "thought")
120
121
  return actionPrefix(ACTION_MARGIN, "think", columns);
122
+ if (row.kind === "report")
123
+ return actionPrefix(ACTION_MARGIN, "report", columns);
121
124
  if (row.kind === "note")
122
125
  return actionPrefix(ACTION_MARGIN, "note", columns);
123
126
  if (row.kind === "outcome")
124
- return { first: row.outcome.kind === "answered" ? TOP_ANSWER : TOP_FAILURE, continuation: TOP_CONTINUATION };
127
+ return {
128
+ first: row.outcome.kind === "answered" ? TOP_ANSWER : row.outcome.kind === "canceled" ? "○ " : TOP_FAILURE,
129
+ continuation: TOP_CONTINUATION,
130
+ };
125
131
  if (row.kind === "said")
126
132
  return { first: TOP_SPEAK, continuation: TOP_SPEAK };
127
133
  if (row.kind === "tell" && row.state === "pending")
@@ -724,7 +730,7 @@ function renderDebtContextLines(created, columns) {
724
730
  if (created === undefined)
725
731
  return [];
726
732
  if (created.kind === "failed")
727
- return [`! debts failed ${safeText(created.diagnostic)}`];
733
+ return diagnosticLines("created debts", created.diagnostic, columns);
728
734
  if (created.rows.length === 0)
729
735
  return [];
730
736
  return [`debts ${created.rows.length}`, ...created.rows.flatMap((row) => renderDebtRow(row, columns))];
@@ -736,7 +742,10 @@ function snapshotCore(view, context, options) {
736
742
  ...(options.showAllowed === true ? [`allowed ${view.status.allowed.join(", ") || "none"}`] : []),
737
743
  ...(view.status.cwd === undefined ? [] : [safeText(view.status.cwd)]),
738
744
  ];
739
- const facts = [...contractFacts(view.contract), ...(options.facts ?? [])];
745
+ const facts = [
746
+ ...contractFacts(view.contract, context.columns - SUBORDINATE_INDENT.length),
747
+ ...(options.facts ?? []),
748
+ ];
740
749
  return {
741
750
  activity,
742
751
  head: panelHead(view.status.id, options.alias, view.contract, contextFacts),
@@ -803,7 +812,7 @@ export function historyRawAnswer(result, historyId) {
803
812
  if (historyId === undefined || result.kind !== "exact")
804
813
  return undefined;
805
814
  const outcome = result.outcome.outcome;
806
- return outcome.kind === "answered" ? outcome.answer : outcome.diagnostic;
815
+ return outcome.kind === "answered" ? outcome.answer : outcome.kind === "canceled" ? "canceled" : outcome.diagnostic;
807
816
  }
808
817
  /** One compact wait result line: an observed life mark, the complete identity, a bounded summary. */
809
818
  const WAIT_SUMMARY_SEPARATOR = " — ";
@@ -844,7 +853,9 @@ function waitSummary(status) {
844
853
  ? { mark: "✓", state: "completed" }
845
854
  : outcome.kind === "answered"
846
855
  ? { mark: "✓", state: preview(outcome.answer, "answered") }
847
- : { mark: "!", state: preview(outcome.diagnostic, "failed") });
856
+ : outcome.kind === "canceled"
857
+ ? { mark: "○", state: "canceled" }
858
+ : { mark: "!", state: preview(outcome.diagnostic, "failed") });
848
859
  return {
849
860
  ...observed,
850
861
  summary: [
@@ -875,7 +886,9 @@ export function historyText(result, presentation, context) {
875
886
  return `${result.kind === "unknown-history" ? result.historyId : "unknown"} has no matching retained outcome`;
876
887
  return result.outcome.outcome.kind === "answered"
877
888
  ? result.outcome.outcome.answer
878
- : result.outcome.outcome.diagnostic;
889
+ : result.outcome.outcome.kind === "canceled"
890
+ ? "canceled"
891
+ : result.outcome.outcome.diagnostic;
879
892
  }
880
893
  if (presentation.last === true)
881
894
  return result.kind === "last" ? result.answer : "no answer retained";
@@ -945,14 +958,11 @@ export function tellText(result, alias, context) {
945
958
  if (wake.kind !== "failed")
946
959
  return verdict;
947
960
  const child = "child" in wake ? wake.child : undefined;
948
- const failure = `! tell delivery failed · ${safeText(wake.diagnostic)}${child === undefined ? "" : ` · log ${child.log.path} ${child.log.from}..${child.log.to}`}`;
949
961
  return [
950
962
  verdict,
951
- ...subordinateLines(renderBoundedTextBlock(failure, {
952
- first: "",
953
- continuation: "",
954
- columns: context.columns - SUBORDINATE_INDENT.length,
955
- lines: Number.MAX_SAFE_INTEGER,
963
+ ...subordinateLines(diagnosticLines("tell delivery", wake.diagnostic, context.columns - SUBORDINATE_INDENT.length, {
964
+ state: "failed",
965
+ suffix: child === undefined ? "" : ` · log ${child.log.path} ${child.log.from}..${child.log.to}`,
956
966
  })),
957
967
  ].join("\n");
958
968
  }
@@ -31,6 +31,11 @@ export declare function renderBoundedTextBlock(value: string, input: Readonly<{
31
31
  lines?: number;
32
32
  truncated?: boolean;
33
33
  }>): readonly string[];
34
+ /** A tool diagnostic names its subject first; only its bounded payload carries the cause. */
35
+ export declare function diagnosticLines(subject: string, cause: string, columns: number, options?: Readonly<{
36
+ state?: "unavailable" | "failed";
37
+ suffix?: string;
38
+ }>): readonly string[];
34
39
  type BoundedPayload = Readonly<{
35
40
  text: string;
36
41
  first: string;
@@ -168,6 +168,13 @@ export function renderBoundedTextBlock(value, input) {
168
168
  }
169
169
  return lines.length === 0 ? [input.first.trimEnd()] : lines;
170
170
  }
171
+ /** A tool diagnostic names its subject first; only its bounded payload carries the cause. */
172
+ export function diagnosticLines(subject, cause, columns, options = {}) {
173
+ return [
174
+ `! ${safeText(subject)} ${options.state ?? "unavailable"}${safeText(options.suffix ?? "")}`,
175
+ ...renderBoundedTextBlock(cause, { first: " ", continuation: " ", columns }),
176
+ ];
177
+ }
171
178
  export function renderBoundedPayload(input) {
172
179
  return input.preserveLines === true ? renderPreservedPayload(input) : renderFoldedPayload(input);
173
180
  }
@@ -7,6 +7,7 @@ import { isParsedAkumaCommand, runAkumaCommand } from "./commands/akuma.js";
7
7
  import { admitBindMarkdown, runContractCommand, runContractHistoryCommand } from "./commands/contract.js";
8
8
  import { installOutcome, installHarnesses, renderInstallText } from "./commands/install.js";
9
9
  import { runDebtCommand } from "./commands/debt.js";
10
+ import { runReport } from "./commands/report.js";
10
11
  import { runWhoami } from "./commands/whoami.js";
11
12
  import { resolveInvocationCoordinates } from "./coordinates.js";
12
13
  import { BindDraftError } from "./draft.js";
@@ -167,7 +168,7 @@ async function runCommand(invocation, input) {
167
168
  try {
168
169
  const runtime = {
169
170
  environment,
170
- execution: command.command === "whoami"
171
+ execution: ["whoami", "report"].includes(command.command)
171
172
  ? localExecutionContext()
172
173
  : await executionForEnvironment(environment, cancellation.signal),
173
174
  ...(home === undefined ? {} : { home }),
@@ -187,6 +188,8 @@ async function runCommand(invocation, input) {
187
188
  ...(invocation.cwd === undefined ? {} : { cwd: invocation.cwd }),
188
189
  });
189
190
  }
191
+ if (command.command === "report")
192
+ return await runReport(command, runtime);
190
193
  const coordinates = await invocationCoordinates(invocation, input, environment);
191
194
  if (command.command === "whoami")
192
195
  return await runWhoami(command, coordinates, environment);
@@ -8,6 +8,8 @@
8
8
  * Settings, and Plugin surfaces. Internal composition, execution, and
9
9
  * configuration helpers stay off this entry; only CLI and Body import them.
10
10
  */
11
+ export { AkumaHost } from "./body-host.js";
12
+ export type { AkumaHostScope } from "./body-host.js";
11
13
  export { AuthorityCorruptionError } from "./core/facts/errors.js";
12
14
  /**
13
15
  * The package root: the curated Contract, Akuma, Akumas, Debt, Kanshi, World,
@@ -46,7 +48,7 @@ export { actorId } from "./core/facts/types.js";
46
48
  */
47
49
  export { z } from "zod";
48
50
  export { ALLOWED_ACTIONS, Akuma, AkumaArchetypeError, AkumaBusyError, AkumaDecodeError, AkumaNotBornError, AkumaProviderError, Schema, listArchetypeDefinitions, } from "./akuma/index.js";
49
- export type { AkumaCaller, AkumaOrigin, ActivityHistory, ActivityRow, AkuId, AkumaAskObservation, AkumaAskOptions, AkumaAskResult, AkumaBirthInput, AkumaHistoryOptions, AkumaHostOptions, AkumaIdleOptions, AkumaIdleResult, AkumaSignalOptions, AkumaStatus, AkumaStreamSnapshot, AkumaTellOptions, AkumaTellResult, AllowedAction, AllowedActions, ArchetypeCatalog, ArchetypeCatalogInput, ArchetypeCatalogRow, InterruptReceipt, JsonSchema, JsonSchemaDocument, KillEvidence, SchemaLike, StandardSchemaV1, } from "./akuma/index.js";
51
+ export type { AkumaCaller, AkumaOrigin, ActivityHistory, ActivityRow, AkuId, AkumaAskObservation, AkumaAskOptions, AkumaAskResult, AkumaBirthInput, AkumaHistoryOptions, AkumaHostOptions, AkumaIdleOptions, AkumaIdleResult, AkumaReportOptions, AkumaReportResult, AkumaSignalOptions, AkumaStatus, AkumaStreamSnapshot, AkumaTellOptions, AkumaTellResult, AllowedAction, AllowedActions, ArchetypeCatalog, ArchetypeCatalogInput, ArchetypeCatalogRow, InterruptReceipt, JsonSchema, JsonSchemaDocument, KillEvidence, SchemaLike, StandardSchemaV1, } from "./akuma/index.js";
50
52
  export type { ActorId, ChangeId, ContractHead, ContractId, ContractState, JournalEntry as Fact, FactKind, SnapshotId, } from "./core/facts/types.js";
51
53
  export { kanshi, kanshiCategory, observeKanshi, selectKanshi, selectRegion } from "./kanshi/index.js";
52
54
  export type { AkumaKanshiRow, AkumaKanshiWorld, ContractEndpointObservation, ContractHolderObservation, ContractKanshiBoard, ContractKanshiRow, KanshiCategoryInput, KanshiCategoryReport, KanshiInput, KanshiRegionSelection, KanshiReport, KanshiSelection, RegionDeclaration, RegionRead, Section, DebtKanshiRow, DebtKanshiWorld, } from "./kanshi/index.js";
@@ -8,6 +8,7 @@
8
8
  * Settings, and Plugin surfaces. Internal composition, execution, and
9
9
  * configuration helpers stay off this entry; only CLI and Body import them.
10
10
  */
11
+ export { AkumaHost } from "./body-host.js";
11
12
  export { AuthorityCorruptionError } from "./core/facts/errors.js";
12
13
  /**
13
14
  * The package root: the curated Contract, Akuma, Akumas, Debt, Kanshi, World,
@@ -1,11 +1,12 @@
1
1
  import { type AkumaCaller } from "../akuma/caller.js";
2
2
  import { type ForkReceipt } from "../akuma/akuma-owner.js";
3
+ import type { BodyLauncher } from "../akuma/body-launch.js";
3
4
  import { type AskObserver } from "../akuma/akuma-selection-execution.js";
4
5
  import type { AkumaStatus } from "../akuma/akuma.js";
5
6
  import type { AllowedAction } from "../akuma/allowed.js";
6
7
  import type { TellResult } from "../akuma/body.js";
7
8
  import { type AkuId } from "../akuma/identity.js";
8
- import { type ExecutionContext } from "../akuma/requests.js";
9
+ import type { ExecutionContext } from "../akuma/requests.js";
9
10
  import { type Schema } from "../akuma/schema.js";
10
11
  import type { AkumaAskResult } from "../akuma/selection-observation.js";
11
12
  import { type AliasBinding } from "../alias/index.js";
@@ -62,6 +63,8 @@ export type CallInput = Readonly<{
62
63
  contract?: Keiyaku;
63
64
  alias?: AkumaAlias;
64
65
  allowed?: readonly AllowedAction[];
66
+ writableDirectories?: readonly string[];
67
+ bare?: boolean;
65
68
  schema?: Schema<unknown>;
66
69
  initiator?: string;
67
70
  caller?: AkumaCaller;
@@ -99,6 +102,7 @@ export type CallResult = Readonly<{
99
102
  export type ForkInput = Readonly<{
100
103
  akuma: string;
101
104
  at: string;
105
+ bare?: boolean;
102
106
  repo?: Repo;
103
107
  caller?: AkumaCaller;
104
108
  initiator?: string;
@@ -116,8 +120,8 @@ export type ForkResult = Readonly<{
116
120
  }>);
117
121
  export declare function callAkumas(input: CallInput & Readonly<{
118
122
  path: WorldRoot;
119
- }>, execution?: ExecutionContext, reportDiagnostic?: (message: string) => void): Promise<CallResult>;
123
+ }>, execution: ExecutionContext, spawn: BodyLauncher, reportDiagnostic?: (message: string) => void): Promise<CallResult>;
120
124
  export declare function forkAkumas(input: ForkInput & Readonly<{
121
125
  path: WorldRoot;
122
- }>): Promise<ForkResult>;
126
+ }>, spawn: BodyLauncher): Promise<ForkResult>;
123
127
  export {};
@@ -5,9 +5,8 @@ import { decodeAkumaCaller } from "../akuma/caller.js";
5
5
  import { admitAkumaCall } from "../akuma/akuma-call.js";
6
6
  import { AkumaOwner } from "../akuma/akuma-owner.js";
7
7
  import { decodeAskObservation, observeAdmittedAskAkuma } from "../akuma/akuma-selection-execution.js";
8
- import { canonicalBirthCwd } from "../akuma/call-input.js";
8
+ import { bareOption, canonicalBirthCwd } from "../akuma/call-input.js";
9
9
  import { pathsForAkuId } from "../akuma/identity.js";
10
- import { localExecutionContext } from "../akuma/requests.js";
11
10
  import { schemaJsonText } from "../akuma/schema.js";
12
11
  import { moveAlias } from "../alias/index.js";
13
12
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
@@ -180,6 +179,8 @@ const CALL_INPUT_KEYS = [
180
179
  "contract",
181
180
  "alias",
182
181
  "allowed",
182
+ "writableDirectories",
183
+ "bare",
183
184
  "schema",
184
185
  "initiator",
185
186
  "caller",
@@ -194,6 +195,7 @@ async function parseCallInput(input) {
194
195
  const body = callBody(values.body);
195
196
  const initiator = values.initiator === undefined ? undefined : text(values.initiator, "initiator");
196
197
  const caller = decodeAkumaCaller(values.caller);
198
+ const bare = bareOption(values.bare);
197
199
  const cwd = values.cwd === undefined ? undefined : nonblank(values.cwd, "cwd");
198
200
  const mode = callMode(values.mode);
199
201
  validateCallBodyOptions(body, values, mode);
@@ -211,8 +213,7 @@ async function parseCallInput(input) {
211
213
  : {
212
214
  tellId: randomUUID(),
213
215
  body,
214
- ...(schemaJson === undefined ? {} : { schemaJson }),
215
- ...(schema === undefined ? {} : { schema }),
216
+ ...(schema === undefined ? {} : { schema, schemaJson: schemaJson }),
216
217
  ...(initiator === undefined ? {} : { initiator }),
217
218
  };
218
219
  return {
@@ -226,6 +227,10 @@ async function parseCallInput(input) {
226
227
  ...(settings === undefined ? {} : { settings }),
227
228
  ...(alias === undefined ? {} : { alias }),
228
229
  ...(values.allowed === undefined ? {} : { allowed: values.allowed }),
230
+ ...(values.writableDirectories === undefined
231
+ ? {}
232
+ : { writableDirectories: values.writableDirectories }),
233
+ ...(bare === undefined ? {} : { bare }),
229
234
  seat,
230
235
  ...(caller === undefined ? {} : { caller }),
231
236
  ...(initiator === undefined ? {} : { initiator }),
@@ -256,6 +261,8 @@ function callAdmissionInput(input, execution) {
256
261
  },
257
262
  }),
258
263
  ...(input.allowed === undefined ? {} : { allowed: input.allowed }),
264
+ ...(input.writableDirectories === undefined ? {} : { writableDirectories: input.writableDirectories }),
265
+ ...(input.bare === undefined ? {} : { bare: input.bare }),
259
266
  ...(execution === undefined ? {} : { cwd: execution.cwd }),
260
267
  ...(input.signal === undefined ? {} : { signal: input.signal }),
261
268
  };
@@ -320,13 +327,16 @@ async function observeCall(input, reportDiagnostic) {
320
327
  }
321
328
  catch (error) {
322
329
  if (input.signal?.aborted)
323
- throw error;
330
+ throw knownChildFailure(error, input.akuma);
324
331
  return failedCall(input.result, input.initialTell.tellId, error, input.receipt);
325
332
  }
326
333
  }
327
334
  /** One failure that still names the child the invocation durably admitted. */
328
335
  function knownChildFailure(reason, akuma) {
329
- const error = reason instanceof Error ? reason : new Error(reason === undefined ? "call aborted" : String(reason));
336
+ const cause = reason instanceof Error ? reason : new Error(reason === undefined ? "call aborted" : String(reason));
337
+ // One AbortSignal may cancel several parallel calls. Never overwrite another
338
+ // invocation's leading birth on its shared reason object.
339
+ const error = Object.isExtensible(cause) && !Object.hasOwn(cause, "akuma") ? cause : new Error(cause.message, { cause });
330
340
  Object.defineProperty(error, "akuma", { value: akuma, enumerable: false });
331
341
  return error;
332
342
  }
@@ -334,14 +344,14 @@ function knownChildFailure(reason, akuma) {
334
344
  function asError(failure) {
335
345
  return failure instanceof Error ? failure : new Error(failure === undefined ? "call failed" : String(failure));
336
346
  }
337
- export async function callAkumas(input, execution = localExecutionContext(), reportDiagnostic) {
347
+ export async function callAkumas(input, execution, spawn, reportDiagnostic) {
338
348
  const parsed = await parseCallInput(input);
339
349
  const selectedExecution = await resolveCallExecution(parsed.cwd === undefined ? {} : { cwd: parsed.cwd });
340
350
  const seat = parsed.seat;
341
351
  const admitted = await admitAkumaCall(parsed.path, callConfiguration(parsed, execution), {
342
352
  ...callAdmissionInput(parsed, selectedExecution),
343
353
  ...(seat === undefined ? {} : { contractId: seat.id }),
344
- }, selectedExecution === undefined ? {} : { cwdCanonical: true });
354
+ }, selectedExecution === undefined ? {} : { cwdCanonical: true }, spawn);
345
355
  const dispatch = seat === undefined
346
356
  ? { kind: "none" }
347
357
  : await dispatchStage({ repository: seat.scope, akuId: admitted.id, contractId: seat.id });
@@ -380,9 +390,10 @@ export async function callAkumas(input, execution = localExecutionContext(), rep
380
390
  ...(parsed.observe === undefined ? {} : { observe: parsed.observe }),
381
391
  }, reportDiagnostic);
382
392
  }
383
- export async function forkAkumas(input) {
393
+ export async function forkAkumas(input, spawn) {
384
394
  const values = requireInput(input, "Akumas.fork input");
385
- onlyKeys(values, ["path", "akuma", "at", "repo", "caller", "initiator"], "Akumas.fork input");
395
+ onlyKeys(values, ["path", "akuma", "at", "bare", "repo", "caller", "initiator"], "Akumas.fork input");
396
+ const bare = bareOption(values.bare);
386
397
  const caller = decodeAkumaCaller(values.caller);
387
398
  const initiator = values.initiator === undefined ? undefined : text(values.initiator, "initiator");
388
399
  const at = nonblank(values.at, "at");
@@ -391,9 +402,10 @@ export async function forkAkumas(input) {
391
402
  const repository = values.repo === undefined ? undefined : scopeForRepo(values.repo);
392
403
  const receipt = await new AkumaOwner(akuma, path).fork({
393
404
  at,
405
+ ...(bare === undefined ? {} : { bare }),
394
406
  ...(caller === undefined ? {} : { caller }),
395
407
  ...(initiator === undefined ? {} : { initiator }),
396
- });
408
+ }, spawn);
397
409
  if (receipt.kind !== "forked")
398
410
  return { ...receipt, parent: akuma };
399
411
  const dispatch = repository === undefined
@@ -3,6 +3,7 @@ import { type AkumaHostOptions } from "../akuma/akuma-instance.js";
3
3
  import type { AkumaList, AkumaListInput } from "../akuma/akuma.js";
4
4
  import type { TellResult, TellWake } from "../akuma/body.js";
5
5
  import { type ExecutionContext } from "../akuma/requests.js";
6
+ import type { BodyLauncher } from "../akuma/body-launch.js";
6
7
  import type { DispatchAssociation } from "../akuma/dispatch-association.js";
7
8
  import type { CreatedDebtObservation } from "../debt/created-observation.js";
8
9
  import { type WorldRoot } from "../world.js";
@@ -18,7 +19,7 @@ export type { AkumaAskResult, AkumaHistoryResult, AkumaKillResult, AkumaList, Ak
18
19
  export type Akumas = AkumasHandle;
19
20
  declare class AkumasHandle {
20
21
  #private;
21
- constructor(world: WorldRoot, execution: ExecutionContext, host?: AkumaHostOptions);
22
+ constructor(world: WorldRoot, execution: ExecutionContext, spawn: BodyLauncher, host?: AkumaHostOptions);
22
23
  list(input?: AkumaListInput): Promise<AkumaList>;
23
24
  call(input: CallInput): Promise<CallResult>;
24
25
  fork(input: ForkInput): Promise<ForkResult>;
@@ -43,6 +44,8 @@ declare class AkumasHandle {
43
44
  * internal channel through `akumasWithExecution` instead of a second constructor.
44
45
  */
45
46
  export declare function akumasWithExecution(world: WorldRoot, execution: ExecutionContext, host?: AkumaHostOptions): Akumas;
47
+ /** Internal host composition, independent of the captured request route. */
48
+ export declare function akumasWithLauncher(world: WorldRoot, execution: ExecutionContext, spawn: BodyLauncher, host?: AkumaHostOptions): Akumas;
46
49
  export declare const Akumas: Readonly<{
47
50
  of(world: WorldRoot, host?: AkumaHostOptions): Akumas;
48
51
  }>;
@@ -2,6 +2,7 @@
2
2
  import { captureAkumaHostDiagnostic } from "../akuma/akuma-instance.js";
3
3
  import { readAkumaRoster } from "../akuma/akuma.js";
4
4
  import { localExecutionContext } from "../akuma/requests.js";
5
+ import { captureAkumaHost } from "../body-host.js";
5
6
  import { readAliases } from "../alias/index.js";
6
7
  import { World } from "../world.js";
7
8
  import { sweepInactiveAkumas } from "./akuma-retention.js";
@@ -12,15 +13,17 @@ export { AkumaAddressError, AkumaWorldScopeError } from "./address.js";
12
13
  class AkumasHandle {
13
14
  #world;
14
15
  #execution;
16
+ #spawn;
15
17
  #selection;
16
18
  #reportDiagnostic;
17
19
  #maintenance;
18
- constructor(world, execution, host) {
20
+ constructor(world, execution, spawn, host) {
19
21
  this.#world = world;
20
22
  this.#execution = execution;
23
+ this.#spawn = spawn;
21
24
  // One typed route seam captured at construction: no operation re-derives it.
22
25
  this.#reportDiagnostic = captureAkumaHostDiagnostic(host);
23
- this.#selection = selectionSeam(execution, this.#reportDiagnostic);
26
+ this.#selection = selectionSeam(execution, spawn, this.#reportDiagnostic);
24
27
  Object.freeze(this);
25
28
  }
26
29
  async list(input) {
@@ -31,14 +34,14 @@ class AkumasHandle {
31
34
  return withRosterAliases(roster, await readAliases(path));
32
35
  }
33
36
  call(input) {
34
- const pending = callAkumas(this.withWorld(input, "Akumas.call input"), this.#execution, this.#reportDiagnostic);
37
+ const pending = callAkumas(this.withWorld(input, "Akumas.call input"), this.#execution, this.#spawn, this.#reportDiagnostic);
35
38
  return pending.then(async (result) => {
36
39
  await this.maintain(this.#world);
37
40
  return result;
38
41
  });
39
42
  }
40
43
  fork(input) {
41
- const pending = forkAkumas(this.withWorld(input, "Akumas.fork input"));
44
+ const pending = forkAkumas(this.withWorld(input, "Akumas.fork input"), this.#spawn);
42
45
  return pending.then(async (result) => {
43
46
  if (result.kind === "forked")
44
47
  await this.maintain(this.#world);
@@ -117,9 +120,14 @@ function withRosterAliases(roster, bindings) {
117
120
  * internal channel through `akumasWithExecution` instead of a second constructor.
118
121
  */
119
122
  export function akumasWithExecution(world, execution, host) {
123
+ const { spawn, reportDiagnostic } = captureAkumaHost(host);
124
+ return akumasWithLauncher(world, execution, spawn, reportDiagnostic === undefined ? undefined : { reportDiagnostic });
125
+ }
126
+ /** Internal host composition, independent of the captured request route. */
127
+ export function akumasWithLauncher(world, execution, spawn, host) {
120
128
  if (typeof world !== "string")
121
129
  throw new TypeError("Akumas.of world must be a WorldRoot");
122
- return new AkumasHandle(world, execution, host);
130
+ return new AkumasHandle(world, execution, spawn, host);
123
131
  }
124
132
  export const Akumas = Object.freeze({
125
133
  of(world, host) {
@@ -1,3 +1,4 @@
1
+ import type { BodyLauncher } from "../akuma/body-launch.js";
1
2
  import { type AskObserver } from "../akuma/akuma-selection-execution.js";
2
3
  import { type AkumaStatus } from "../akuma/akuma.js";
3
4
  import { type ActivityHistory, type OutcomeRow } from "../akuma/projection.js";
@@ -32,7 +33,7 @@ export type SelectionSeam = Readonly<{
32
33
  tell(addressed: AddressedAkuma, input: AkumaTellInput, signal?: AbortSignal): Promise<AkumaTellResult>;
33
34
  ask<T>(addressed: AddressedAkuma, input: AkumaAskInput<T>, signal?: AbortSignal): Promise<AkumaAskResult<T>>;
34
35
  }>;
35
- export declare function selectionSeam(execution: ExecutionContext, reportDiagnostic?: (message: string) => void): SelectionSeam;
36
+ export declare function selectionSeam(execution: ExecutionContext, spawn: BodyLauncher, reportDiagnostic?: (message: string) => void): SelectionSeam;
36
37
  export type AkumaWaitInput = AkumaSetAddressInput & Readonly<{
37
38
  completion?: "any" | "all";
38
39
  timeoutMs?: number;
@@ -17,11 +17,11 @@ import { outcomePluginObserver, stderrPluginDiagnostic, withAkumaWaitDeferrals }
17
17
  import { addressAkuma, addressAkumaSet, resolveAkuma, resolveAkumaSet, } from "./address.js";
18
18
  import { requireInput } from "./input.js";
19
19
  import { scopeForRepo } from "./repo.js";
20
- export function selectionSeam(execution, reportDiagnostic = stderrPluginDiagnostic) {
20
+ export function selectionSeam(execution, spawn, reportDiagnostic = stderrPluginDiagnostic) {
21
21
  const channel = executionChannel(execution);
22
22
  const routed = channel.kind === "body-request"
23
23
  ? forwardedSelection(channel.directory, reportDiagnostic)
24
- : localSelection(reportDiagnostic);
24
+ : localSelection(spawn, reportDiagnostic);
25
25
  return {
26
26
  ...routed,
27
27
  wait: async (addressed, mode) => await withAkumaWaitDeferrals({ world: addressed.path, reportDiagnostic }, addressed.orderedIds, mode.signal, async () => {
@@ -68,17 +68,19 @@ function forwardedSelection(directory, reportDiagnostic) {
68
68
  },
69
69
  };
70
70
  }
71
- function localSelection(reportDiagnostic) {
71
+ function localSelection(spawn, reportDiagnostic) {
72
72
  return {
73
73
  addressOne: addressAkuma,
74
74
  addressSet: addressAkumaSet,
75
75
  wait: async (addressed, mode) => await localWait(addressed, mode),
76
76
  kill: async (addressed, abort) => await executeKillAkuma({
77
+ spawn,
77
78
  path: addressed.path,
78
79
  ids: addressed.orderedIds,
79
80
  ...(abort === undefined ? {} : { signal: abort }),
80
81
  }),
81
82
  tell: async (addressed, input, abort) => await executeTellAkuma({
83
+ spawn,
82
84
  path: addressed.path,
83
85
  id: addressed.id,
84
86
  body: input.body,
@@ -88,6 +90,7 @@ function localSelection(reportDiagnostic) {
88
90
  }),
89
91
  ask: async (addressed, input, abort) => {
90
92
  const result = await executeAskAkuma({
93
+ spawn,
91
94
  path: addressed.path,
92
95
  id: addressed.id,
93
96
  outcomeObserved: outcomePluginObserver({ world: addressed.path, reportDiagnostic }),
@@ -13,6 +13,8 @@ export type PluginOutcome = Readonly<{
13
13
  }> | Readonly<{
14
14
  kind: "failed";
15
15
  reason: string;
16
+ }> | Readonly<{
17
+ kind: "canceled";
16
18
  }>;
17
19
  export type PluginBodyEnd = "exited" | "broke-off" | "put-down" | "hung";
18
20
  export type PluginAkumaOrigin = Readonly<{
@@ -54,6 +56,15 @@ export type PluginSignalMap = Readonly<{
54
56
  historyId: string;
55
57
  observer?: string;
56
58
  }>;
59
+ "akuma.reported": Readonly<{
60
+ akumaId: string;
61
+ bodySequence: number;
62
+ turnSequence: number;
63
+ reportSequence: number;
64
+ text: string;
65
+ at: string;
66
+ initiator?: string;
67
+ }>;
57
68
  "akuma.turn-started": Readonly<{
58
69
  akumaId: string;
59
70
  turnSequence: number;
@@ -206,6 +206,7 @@ const SIGNAL_KINDS = Object.freeze({
206
206
  "akuma.initiating": true,
207
207
  "akuma.born": true,
208
208
  "akuma.outcome-observed": true,
209
+ "akuma.reported": true,
209
210
  "akuma.turn-started": true,
210
211
  "akuma.turn-outcome": true,
211
212
  "akuma.body-ended": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/keiyaku",
3
- "version": "4.6.19",
3
+ "version": "4.6.21",
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.13",
85
- "@astrosheep/square": "latest",
84
+ "@astrosheep/keiyaku-plugin-square": "0.1.15",
85
+ "@astrosheep/square": "^0.3.98",
86
86
  "@getpaseo/client": "0.11.2",
87
87
  "@getpaseo/protocol": "0.11.2",
88
88
  "@opencode/client": "2.0.20",
@@ -24,6 +24,7 @@ const row = z.union([
24
24
  z.object({ kind: z.literal("answered"), historyId: z.string().optional(), answer: z.string(),
25
25
  foldedSaidSequence: count.optional() }),
26
26
  z.object({ kind: z.literal("failed"), historyId: z.string().optional(), diagnostic: z.string() }),
27
+ z.object({ kind: z.literal("canceled"), historyId: z.string().optional() }),
27
28
  ]) }),
28
29
  base.extend({ kind: z.literal("tool"), ...turnFields, name: z.string(), toolId: z.string().optional(), call,
29
30
  state: z.union([z.enum(["active", "unsettled"]), z.object({ status: z.enum(["ok", "error"]),
@@ -170,11 +170,11 @@ export function createMirror(sessionId: string, emit: Emit) {
170
170
  } else if (row.kind === "note") {
171
171
  item({ type: "notification", id: `keiyaku:note/${row.sequence}`, level: "info", message: row.text }, row.at);
172
172
  } else if (row.kind === "outcome") {
173
- item(row.outcome.kind === "answered"
173
+ if (row.outcome.kind !== "canceled") item(row.outcome.kind === "answered"
174
174
  ? { type: "assistant_message", id: row.outcome.foldedSaidSequence === undefined
175
175
  ? `keiyaku:turn/${row.turnSequence}` : `keiyaku:said/${row.outcome.foldedSaidSequence}`, text: row.outcome.answer }
176
176
  : { type: "error", id: `keiyaku:turn/${row.turnSequence}`, message: row.outcome.diagnostic }, row.at);
177
- finish(openTurns.get(row.turnSequence), row.outcome.kind === "answered" ? "completed" : "failed",
177
+ finish(openTurns.get(row.turnSequence), row.outcome.kind === "answered" ? "completed" : row.outcome.kind,
178
178
  row.outcome.kind === "failed" ? row.outcome.diagnostic : undefined);
179
179
  }
180
180
  }