@astrosheep/keiyaku 4.6.6 → 4.6.8

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 (217) hide show
  1. package/README.md +11 -0
  2. package/build/{integrations/marketplace → plugins}/.agents/plugins/marketplace.json +1 -1
  3. package/build/plugins/.claude-plugin/marketplace.json +5 -0
  4. package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/.claude-plugin/plugin.json +1 -1
  5. package/build/{integrations/marketplace/plugins/keiyaku → plugins/codex}/.codex-plugin/plugin.json +1 -1
  6. package/build/plugins/codex/skills/keiyaku/SKILL.md +44 -0
  7. package/build/plugins/codex/skills/keiyaku-akuma/SKILL.md +130 -0
  8. package/build/plugins/codex/skills/keiyaku-akuma/references/automation.md +269 -0
  9. package/build/plugins/codex/skills/keiyaku-babysit/SKILL.md +65 -0
  10. package/build/plugins/codex/skills/keiyaku-bind/SKILL.md +216 -0
  11. package/build/plugins/codex/skills/keiyaku-bind/agents/openai.yaml +4 -0
  12. package/build/plugins/codex/skills/keiyaku-task/SKILL.md +104 -0
  13. package/build/plugins/codex/skills/keiyaku-workflow/SKILL.md +129 -0
  14. package/build/plugins/opencode/package.json +6 -0
  15. package/build/plugins/opencode/skills/keiyaku/SKILL.md +44 -0
  16. package/build/plugins/opencode/skills/keiyaku-akuma/SKILL.md +130 -0
  17. package/build/plugins/opencode/skills/keiyaku-akuma/references/automation.md +269 -0
  18. package/build/plugins/opencode/skills/keiyaku-babysit/SKILL.md +65 -0
  19. package/build/plugins/opencode/skills/keiyaku-bind/SKILL.md +216 -0
  20. package/build/plugins/opencode/skills/keiyaku-bind/agents/openai.yaml +4 -0
  21. package/build/plugins/opencode/skills/keiyaku-task/SKILL.md +104 -0
  22. package/build/plugins/opencode/skills/keiyaku-workflow/SKILL.md +129 -0
  23. package/build/{integrations → plugins}/pi/keiyaku.ts +0 -5
  24. package/build/skills/keiyaku/SKILL.md +44 -0
  25. package/build/skills/keiyaku-akuma/SKILL.md +130 -0
  26. package/build/skills/keiyaku-akuma/references/automation.md +269 -0
  27. package/build/skills/keiyaku-babysit/SKILL.md +65 -0
  28. package/build/skills/keiyaku-bind/SKILL.md +216 -0
  29. package/build/skills/keiyaku-bind/agents/openai.yaml +4 -0
  30. package/build/skills/keiyaku-task/SKILL.md +104 -0
  31. package/build/skills/keiyaku-workflow/SKILL.md +129 -0
  32. package/build/src/akuma/akuma-call.d.ts +2 -0
  33. package/build/src/akuma/akuma-call.js +8 -2
  34. package/build/src/akuma/akuma-instance.d.ts +12 -1
  35. package/build/src/akuma/akuma-instance.js +31 -9
  36. package/build/src/akuma/akuma-observe.d.ts +4 -0
  37. package/build/src/akuma/akuma-observe.js +14 -0
  38. package/build/src/akuma/akuma-owner.d.ts +6 -2
  39. package/build/src/akuma/akuma-owner.js +32 -10
  40. package/build/src/akuma/akuma-selection-execution.d.ts +3 -1
  41. package/build/src/akuma/akuma-selection-execution.js +2 -1
  42. package/build/src/akuma/akuma-wait.d.ts +2 -1
  43. package/build/src/akuma/akuma-wait.js +4 -1
  44. package/build/src/akuma/akuma.d.ts +35 -5
  45. package/build/src/akuma/akuma.js +8 -10
  46. package/build/src/akuma/archetype.d.ts +7 -15
  47. package/build/src/akuma/archetype.js +36 -35
  48. package/build/src/akuma/body.d.ts +6 -3
  49. package/build/src/akuma/body.js +41 -30
  50. package/build/src/akuma/call-request.d.ts +18 -10
  51. package/build/src/akuma/call-request.js +10 -3
  52. package/build/src/akuma/caller.d.ts +39 -0
  53. package/build/src/akuma/caller.js +36 -0
  54. package/build/src/akuma/dispatch-association.d.ts +0 -1
  55. package/build/src/akuma/dispatch-association.js +0 -3
  56. package/build/src/akuma/heart/facts.d.ts +16 -12
  57. package/build/src/akuma/heart/facts.js +8 -0
  58. package/build/src/akuma/heart/index.d.ts +2 -7
  59. package/build/src/akuma/heart/index.js +3 -17
  60. package/build/src/akuma/heart/rows.d.ts +3 -3
  61. package/build/src/akuma/heart/soul.d.ts +1 -1
  62. package/build/src/akuma/heart/soul.js +5 -24
  63. package/build/src/akuma/heart/storage.d.ts +5 -0
  64. package/build/src/akuma/heart/storage.js +7 -3
  65. package/build/src/akuma/identity.d.ts +0 -1
  66. package/build/src/akuma/identity.js +2 -5
  67. package/build/src/akuma/index.d.ts +5 -2
  68. package/build/src/akuma/index.js +1 -0
  69. package/build/src/akuma/nuke.js +6 -28
  70. package/build/src/akuma/projection.d.ts +0 -11
  71. package/build/src/akuma/provider-recipe.d.ts +7 -6
  72. package/build/src/akuma/provider-recipe.js +1 -1
  73. package/build/src/akuma/provider.d.ts +6 -10
  74. package/build/src/akuma/provider.js +6 -7
  75. package/build/src/akuma/providers/acp/events.js +5 -18
  76. package/build/src/akuma/providers/acp/index.js +2 -3
  77. package/build/src/akuma/providers/claude/events.js +35 -60
  78. package/build/src/akuma/providers/claude/index.d.ts +2 -2
  79. package/build/src/akuma/providers/claude/index.js +2 -3
  80. package/build/src/akuma/providers/codex-app-server/index.js +2 -2
  81. package/build/src/akuma/providers/decode.d.ts +25 -0
  82. package/build/src/akuma/providers/decode.js +44 -0
  83. package/build/src/akuma/providers/execution-environment.d.ts +2 -1
  84. package/build/src/akuma/providers/execution-environment.js +3 -0
  85. package/build/src/akuma/providers/grok-build/index.js +6 -22
  86. package/build/src/akuma/providers/index.js +2 -0
  87. package/build/src/akuma/providers/lifecycle.d.ts +9 -0
  88. package/build/src/akuma/providers/lifecycle.js +10 -0
  89. package/build/src/akuma/providers/opencode-sdk/events.js +1 -14
  90. package/build/src/akuma/providers/opencode-sdk/index.js +10 -17
  91. package/build/src/akuma/providers/opencode-sdk/session.js +1 -3
  92. package/build/src/akuma/providers/paseo/events.d.ts +25 -0
  93. package/build/src/akuma/providers/paseo/events.js +157 -0
  94. package/build/src/akuma/providers/paseo/index.d.ts +9 -0
  95. package/build/src/akuma/providers/paseo/index.js +353 -0
  96. package/build/src/akuma/providers/pi/events.js +5 -15
  97. package/build/src/akuma/publication.d.ts +2 -0
  98. package/build/src/akuma/publication.js +5 -0
  99. package/build/src/akuma/request-lifecycle.d.ts +3 -0
  100. package/build/src/akuma/request-lifecycle.js +25 -4
  101. package/build/src/akuma/requests.d.ts +3 -1
  102. package/build/src/akuma/requests.js +32 -0
  103. package/build/src/akuma/selection-observation.d.ts +72 -2
  104. package/build/src/akuma/selection-observation.js +0 -6
  105. package/build/src/akuma/selection-owner-port.d.ts +1 -1
  106. package/build/src/akuma/selection-owner-port.js +16 -5
  107. package/build/src/akuma/selection-request.d.ts +2 -0
  108. package/build/src/akuma/selection-request.js +2 -1
  109. package/build/src/akuma/turn-drive.js +2 -5
  110. package/build/src/akuma-body.js +4 -2
  111. package/build/src/cli/akuma-caller.d.ts +3 -0
  112. package/build/src/cli/akuma-caller.js +21 -0
  113. package/build/src/cli/commands/akuma-grammar.js +7 -12
  114. package/build/src/cli/commands/akuma.js +18 -16
  115. package/build/src/cli/commands/contract-grammar.d.ts +2 -1
  116. package/build/src/cli/commands/contract-grammar.js +13 -15
  117. package/build/src/cli/commands/contract-help.d.ts +2 -1
  118. package/build/src/cli/commands/contract-help.js +31 -1
  119. package/build/src/cli/commands/contract.js +24 -53
  120. package/build/src/cli/commands/install.js +3 -3
  121. package/build/src/cli/commands/task.js +18 -35
  122. package/build/src/cli/parse.d.ts +2 -2
  123. package/build/src/cli/render/catalog.d.ts +2 -5
  124. package/build/src/cli/render/contract-history.js +3 -6
  125. package/build/src/cli/render/contract.js +15 -50
  126. package/build/src/cli/render/receipt.js +3 -10
  127. package/build/src/cli/render/task.d.ts +4 -6
  128. package/build/src/cli/render/task.js +9 -26
  129. package/build/src/cli/runtime.d.ts +1 -1
  130. package/build/src/cli/runtime.js +11 -17
  131. package/build/src/cli/selectors.d.ts +3 -6
  132. package/build/src/cli/selectors.js +22 -53
  133. package/build/src/dispatch/index.d.ts +3 -3
  134. package/build/src/dispatch/index.js +1 -2
  135. package/build/src/git/observe.d.ts +6 -2
  136. package/build/src/git/observe.js +6 -2
  137. package/build/src/index.d.ts +6 -6
  138. package/build/src/index.js +2 -2
  139. package/build/src/kanshi/index.d.ts +1 -1
  140. package/build/src/kanshi/read.js +21 -6
  141. package/build/src/library/address.js +2 -4
  142. package/build/src/library/akuma-creation.d.ts +5 -1
  143. package/build/src/library/akuma-creation.js +33 -51
  144. package/build/src/library/akumas.d.ts +5 -4
  145. package/build/src/library/akumas.js +13 -9
  146. package/build/src/library/bind.d.ts +14 -14
  147. package/build/src/library/bind.js +11 -7
  148. package/build/src/library/contract-address.d.ts +9 -0
  149. package/build/src/library/contract-address.js +71 -0
  150. package/build/src/library/contract-composition.d.ts +4 -7
  151. package/build/src/library/contract-composition.js +6 -18
  152. package/build/src/library/contract-creation.d.ts +3 -5
  153. package/build/src/library/contract-creation.js +19 -17
  154. package/build/src/library/contract-execution.js +18 -23
  155. package/build/src/library/contract-outcomes.d.ts +2 -14
  156. package/build/src/library/contract-outcomes.js +2 -13
  157. package/build/src/library/contract-types.d.ts +36 -2
  158. package/build/src/library/delivery.d.ts +0 -2
  159. package/build/src/library/delivery.js +2 -5
  160. package/build/src/library/keiyaku.d.ts +0 -5
  161. package/build/src/library/keiyaku.js +6 -12
  162. package/build/src/library/outcome.d.ts +500 -499
  163. package/build/src/library/outcome.js +10 -0
  164. package/build/src/library/reconcile.js +8 -16
  165. package/build/src/library/refusal.d.ts +2 -2
  166. package/build/src/library/repo.d.ts +3 -2
  167. package/build/src/library/repo.js +10 -5
  168. package/build/src/library/selection.d.ts +4 -4
  169. package/build/src/library/selection.js +12 -9
  170. package/build/src/plugin/akuma-signals.d.ts +12 -11
  171. package/build/src/plugin/akuma-signals.js +40 -20
  172. package/build/src/plugin/public.d.ts +32 -2
  173. package/build/src/plugin/runtime.d.ts +1 -0
  174. package/build/src/plugin/runtime.js +35 -24
  175. package/build/src/protocol/amend.js +3 -2
  176. package/build/src/protocol/attempt.js +3 -8
  177. package/build/src/protocol/bind.d.ts +2 -2
  178. package/build/src/protocol/bind.js +1 -1
  179. package/build/src/protocol/deliver.js +3 -3
  180. package/build/src/protocol/operations.d.ts +1 -3
  181. package/build/src/protocol/operations.js +0 -8
  182. package/build/src/protocol/outcome.d.ts +1 -2
  183. package/build/src/protocol/outcome.js +1 -13
  184. package/build/src/protocol/placement.js +3 -10
  185. package/build/src/protocol/read/status.js +31 -70
  186. package/build/src/protocol/reintegrate.js +3 -10
  187. package/build/src/protocol/review.js +3 -3
  188. package/build/src/protocol/run.d.ts +5 -1
  189. package/build/src/protocol/run.js +3 -3
  190. package/build/src/runtime/proc/windows-launch.exe +0 -0
  191. package/package.json +12 -8
  192. package/plugins/pi/keiyaku.ts +57 -0
  193. package/skills/keiyaku/SKILL.md +44 -0
  194. package/skills/keiyaku-akuma/SKILL.md +130 -0
  195. package/skills/keiyaku-akuma/references/automation.md +269 -0
  196. package/skills/keiyaku-babysit/SKILL.md +65 -0
  197. package/skills/keiyaku-bind/SKILL.md +216 -0
  198. package/skills/keiyaku-bind/agents/openai.yaml +4 -0
  199. package/skills/keiyaku-task/SKILL.md +104 -0
  200. package/skills/keiyaku-workflow/SKILL.md +129 -0
  201. package/build/integrations/marketplace/.claude-plugin/marketplace.json +0 -5
  202. package/build/integrations/marketplace/plugins/keiyaku/package.json +0 -17
  203. package/build/src/cli/commands/amend.d.ts +0 -12
  204. package/build/src/cli/commands/amend.js +0 -10
  205. package/build/src/cli/commands/bind.d.ts +0 -11
  206. package/build/src/cli/commands/bind.js +0 -23
  207. package/build/src/dispatch/association.d.ts +0 -1
  208. package/build/src/dispatch/association.js +0 -1
  209. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku/SKILL.md +0 -0
  210. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-akuma/SKILL.md +0 -0
  211. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-akuma/references/automation.md +0 -0
  212. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-babysit/SKILL.md +0 -0
  213. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-bind/SKILL.md +0 -0
  214. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-bind/agents/openai.yaml +0 -0
  215. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-task/SKILL.md +0 -0
  216. /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-workflow/SKILL.md +0 -0
  217. /package/build/{integrations/marketplace/plugins/keiyaku/opencode.js → plugins/opencode/index.js} +0 -0
@@ -257,6 +257,16 @@ export function errorCategory(error) {
257
257
  return "aborted";
258
258
  return "internal";
259
259
  }
260
+ export async function readContractValue(operation) {
261
+ try {
262
+ return await operation();
263
+ }
264
+ catch (error) {
265
+ if (error instanceof KeiyakuError)
266
+ throw error;
267
+ throw new KeiyakuError(errorCategory(error), errorDiagnostic(error), { cause: error });
268
+ }
269
+ }
260
270
  function errorDiagnostic(error) {
261
271
  return error instanceof Error ? error.message : String(error);
262
272
  }
@@ -54,11 +54,8 @@ function registerLag(scope, error) {
54
54
  function emptySettlement() {
55
55
  return { actions: [], lags: [] };
56
56
  }
57
- function isManagedWorktree(state) {
58
- return state.coordinates.workspace === "worktree";
59
- }
60
57
  function isManagedTerminal(state) {
61
- return state !== null && isManagedWorktree(state) && state.terminal !== null;
58
+ return state !== null && state.terminal !== null;
62
59
  }
63
60
  /**
64
61
  * Terminal cleanup retires the appointed worktree. Name its short name only when this
@@ -76,7 +73,7 @@ function terminalWorktreeOutcome(scope, cleanup, place) {
76
73
  return undefined;
77
74
  }
78
75
  function appointableManagedContracts(states) {
79
- return states.filter((state) => isManagedWorktree(state) && state.terminal === null).map((state) => state.id);
76
+ return states.filter((state) => state.terminal === null).map((state) => state.id);
80
77
  }
81
78
  async function appointPlaces(scope, states) {
82
79
  return await appointManagedWorktrees(scope, appointableManagedContracts(states));
@@ -114,8 +111,6 @@ async function observeState(scope, channel, contractId) {
114
111
  }
115
112
  }
116
113
  async function appointForContract(scope, state) {
117
- if (!isManagedWorktree(state))
118
- return {};
119
114
  try {
120
115
  const register = await appointPlaces(scope, [state]);
121
116
  const place = register.byContract.get(state.id)?.place;
@@ -265,16 +260,13 @@ export async function completeRepoReconcile(input) {
265
260
  if (!isOperationalFailure(error))
266
261
  throw error;
267
262
  const lag = registerLag(input.scope, error);
268
- const contracts = [];
269
- for (const state of states) {
270
- contracts.push({
263
+ return {
264
+ kind: "completed",
265
+ contracts: states.map((state) => ({
271
266
  contractId: state.id,
272
- report: isManagedWorktree(state)
273
- ? { effects: [], lag: [lag], settlement: emptySettlement() }
274
- : await completeReconcile({ ...input, contractId: state.id }),
275
- });
276
- }
277
- return { kind: "completed", contracts };
267
+ report: { effects: [], lag: [lag], settlement: emptySettlement() },
268
+ })),
269
+ };
278
270
  }
279
271
  const places = new Map(appointed.appointments.map((appointment) => [appointment.contract, appointment.place]));
280
272
  const retained = await reconcileAllOperation({
@@ -177,8 +177,8 @@ export declare const keiyakuRefusalSchema: z.ZodUnion<readonly [z.ZodUnion<reado
177
177
  }, z.core.$strict>>>;
178
178
  }, z.core.$strict>]>, z.ZodObject<{
179
179
  kind: z.ZodEnum<{
180
- "unborn-head": "unborn-head";
181
180
  "target-missing": "target-missing";
181
+ "unborn-head": "unborn-head";
182
182
  "invalid-target": "invalid-target";
183
183
  }>;
184
184
  }, z.core.$strict>, z.ZodObject<{
@@ -302,8 +302,8 @@ export declare const operationRefusalSchemas: {
302
302
  contractId: z.ZodPipe<z.ZodString, z.ZodTransform<import("../index.js").ContractId, string>>;
303
303
  }, z.core.$strict>, z.ZodObject<{
304
304
  kind: z.ZodEnum<{
305
- "unborn-head": "unborn-head";
306
305
  "target-missing": "target-missing";
306
+ "unborn-head": "unborn-head";
307
307
  "invalid-target": "invalid-target";
308
308
  }>;
309
309
  }, z.core.$strict>, z.ZodUnion<readonly [z.ZodObject<{
@@ -1,8 +1,9 @@
1
1
  import { type RepositoryScope } from "../protocol/operations.js";
2
2
  import { NoGitWorldError } from "../git/repository.js";
3
3
  export { NoGitWorldError };
4
+ /** One explicit worktree coordinate. The caller resolves relative and ambient spellings, never this edge. */
4
5
  export type RepoAtInput = Readonly<{
5
- path?: string;
6
+ path: string;
6
7
  gitPath?: string;
7
8
  }>;
8
9
  /** One Git world shared by its worktrees; it is coordinate proof and branch observation only. */
@@ -10,7 +11,7 @@ export declare class Repo {
10
11
  readonly root: string;
11
12
  readonly cwd: string;
12
13
  private constructor();
13
- static at(input?: RepoAtInput): Promise<Repo>;
14
+ static at(input: RepoAtInput): Promise<Repo>;
14
15
  currentBranch(): Promise<string | null>;
15
16
  }
16
17
  export declare function scopeForRepo(value: unknown): RepositoryScope;
@@ -1,13 +1,19 @@
1
1
  import { realpath } from "node:fs/promises";
2
- import { resolve } from "node:path";
2
+ import { isAbsolute } from "node:path";
3
3
  import { currentBranchOperation, scopeOperation } from "../protocol/operations.js";
4
4
  import { NoGitWorldError } from "../git/repository.js";
5
5
  import { optionalNonblank, requireInput } from "./input.js";
6
6
  export { NoGitWorldError };
7
7
  const REPO_SCOPES = new WeakMap();
8
- async function resolvePinnedScope(path, gitPath) {
8
+ async function resolvePinnedScope(values) {
9
+ const path = values.path;
10
+ if (typeof path !== "string" || path.trim().length === 0)
11
+ throw new TypeError("repository path must be a nonblank string");
12
+ if (!isAbsolute(path))
13
+ throw new TypeError(`repository path must be absolute: ${path}`);
14
+ const gitPath = optionalNonblank(values.gitPath, "Git executable path");
9
15
  return await scopeOperation({
10
- coordinate: await realpath(resolve(path === undefined ? process.cwd() : path)),
16
+ coordinate: await realpath(path),
11
17
  ...(gitPath === undefined ? {} : { gitPath }),
12
18
  });
13
19
  }
@@ -21,8 +27,7 @@ export class Repo {
21
27
  REPO_SCOPES.set(this, scope);
22
28
  }
23
29
  static async at(input) {
24
- const values = input === undefined ? undefined : requireInput(input, "Repo.at input");
25
- return new Repo(await resolvePinnedScope(optionalNonblank(values?.path, "repository path"), optionalNonblank(values?.gitPath, "Git executable path")));
30
+ return new Repo(await resolvePinnedScope(requireInput(input, "Repo.at input", ["path", "gitPath"])));
26
31
  }
27
32
  async currentBranch() {
28
33
  return await currentBranchOperation({ scope: scopeForRepo(this) });
@@ -5,7 +5,7 @@ import { type ActivityHistory, type OutcomeRow } from "../akuma/projection.js";
5
5
  import { type ExecutionContext } from "../akuma/requests.js";
6
6
  import { type Schema } from "../akuma/schema.js";
7
7
  import type { AkumaAskResult, AkumaKillResult, AkumaObservation, AkumaTellResult, AkumaWaitResult } from "../akuma/selection-observation.js";
8
- import { type DispatchAssociation } from "../dispatch/index.js";
8
+ import type { DispatchAssociation } from "../akuma/dispatch-association.js";
9
9
  import type { WorldRoot } from "../world.js";
10
10
  import { addressAkuma, addressAkumaSet, type AkumaAddressInput, type AkumaSetAddressInput } from "./address.js";
11
11
  import { type Repo } from "./repo.js";
@@ -33,7 +33,7 @@ export type SelectionSeam = Readonly<{
33
33
  tell(addressed: AddressedAkuma, input: AkumaTellInput, signal?: AbortSignal): Promise<AkumaTellResult>;
34
34
  ask<T>(addressed: AddressedAkuma, input: AkumaAskInput<T>, signal?: AbortSignal): Promise<AkumaAskResult<T>>;
35
35
  }>;
36
- export declare function selectionSeam(execution: ExecutionContext): SelectionSeam;
36
+ export declare function selectionSeam(execution: ExecutionContext, reportDiagnostic?: (message: string) => void): SelectionSeam;
37
37
  export type AkumaWaitInput = AkumaSetAddressInput & Readonly<{
38
38
  completion?: "any" | "all";
39
39
  timeoutMs?: number;
@@ -94,7 +94,7 @@ export type AkumaHistoryResult = Readonly<{
94
94
  }>;
95
95
  export declare function statusAkuma(input: AkumaAddressInput & Readonly<{
96
96
  path: WorldRoot;
97
- }>): Promise<AkumaObservation>;
97
+ }>, reportDiagnostic?: (message: string) => void): Promise<AkumaObservation>;
98
98
  export declare function waitAkumaOn(seam: SelectionSeam, input: AkumaWaitInput & Readonly<{
99
99
  path: WorldRoot;
100
100
  }>): Promise<AkumaWaitResult>;
@@ -109,4 +109,4 @@ export declare function askAkumaOn<T = string>(seam: SelectionSeam, input: Akuma
109
109
  }>): Promise<AkumaAskResult<T>>;
110
110
  export declare function historyAkuma(input: AkumaHistoryInput & Readonly<{
111
111
  path: WorldRoot;
112
- }>): Promise<AkumaHistoryResult>;
112
+ }>, reportDiagnostic?: (message: string) => void): Promise<AkumaHistoryResult>;
@@ -13,12 +13,13 @@ import { observeDispatchAssociations } from "../dispatch/index.js";
13
13
  import { observeContractAt } from "../git/observe.js";
14
14
  import { withGitDecodeChannel } from "../git/read-observation.js";
15
15
  import { observeCreatedTaskObservations } from "../task/created-observation.js";
16
+ import { outcomePluginObserver, stderrPluginDiagnostic } from "../plugin/akuma-signals.js";
16
17
  import { addressAkuma, addressAkumaSet, resolveAkuma, resolveAkumaSet, } from "./address.js";
17
18
  import { requireInput } from "./input.js";
18
19
  import { scopeForRepo } from "./repo.js";
19
- export function selectionSeam(execution) {
20
+ export function selectionSeam(execution, reportDiagnostic = stderrPluginDiagnostic) {
20
21
  const channel = executionChannel(execution);
21
- return channel.kind === "body-request" ? forwardedSelection(channel.directory) : localSelection();
22
+ return channel.kind === "body-request" ? forwardedSelection(channel.directory) : localSelection(reportDiagnostic);
22
23
  }
23
24
  function forwardedSelection(directory) {
24
25
  return {
@@ -55,11 +56,11 @@ function forwardedSelection(directory) {
55
56
  },
56
57
  };
57
58
  }
58
- function localSelection() {
59
+ function localSelection(reportDiagnostic) {
59
60
  return {
60
61
  addressOne: addressAkuma,
61
62
  addressSet: addressAkumaSet,
62
- wait: async (addressed, mode, observer) => await localWait(addressed, { ...mode, ...(observer === undefined ? {} : { observer }) }),
63
+ wait: async (addressed, mode, observer) => await localWait(addressed, { ...mode, ...(observer === undefined ? {} : { observer }) }, reportDiagnostic),
63
64
  kill: async (addressed, abort) => await executeKillAkuma({
64
65
  path: addressed.path,
65
66
  ids: addressed.orderedIds,
@@ -77,6 +78,7 @@ function localSelection() {
77
78
  const result = await executeAskAkuma({
78
79
  path: addressed.path,
79
80
  id: addressed.id,
81
+ outcomeObserved: outcomePluginObserver({ world: addressed.path, reportDiagnostic }),
80
82
  body: input.body,
81
83
  ...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
82
84
  ...(input.schema === undefined ? {} : { schemaJson: schemaJsonText(input.schema) }),
@@ -236,10 +238,10 @@ function setAddress(values) {
236
238
  ...(values.repo === undefined ? {} : { repo: values.repo }),
237
239
  };
238
240
  }
239
- export async function statusAkuma(input) {
241
+ export async function statusAkuma(input, reportDiagnostic = stderrPluginDiagnostic) {
240
242
  const addressed = await addressAkuma(input);
241
243
  try {
242
- return await observeAkuma(await new AkumaOwner(addressed.id, addressed.path).status(), addressed.path, input.repo);
244
+ return await observeAkuma(await new AkumaOwner(addressed.id, addressed.path, outcomePluginObserver({ world: addressed.path, reportDiagnostic })).status(), addressed.path, input.repo);
243
245
  }
244
246
  catch (error) {
245
247
  throw observationError(addressed.id, error);
@@ -262,12 +264,13 @@ async function forwardedWait(addressed, input) {
262
264
  ...(input.signal === undefined ? {} : { signal: input.signal }),
263
265
  }));
264
266
  }
265
- async function localWait(addressed, input) {
267
+ async function localWait(addressed, input, reportDiagnostic) {
266
268
  try {
267
269
  return await composeWaitResult(addressed.path, input.repo, await executeWaitAkuma({
268
270
  path: addressed.path,
269
271
  ids: addressed.orderedIds,
270
272
  completion: input.completion,
273
+ outcomeObserved: outcomePluginObserver({ world: addressed.path, reportDiagnostic }),
271
274
  ...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
272
275
  ...(input.signal === undefined ? {} : { signal: input.signal }),
273
276
  identity: waitIdentityFacts(addressed.path, input.repo, addressed.orderedIds),
@@ -385,12 +388,12 @@ function validateHistoryInput(values) {
385
388
  (values.last === true || values.before !== undefined || values.since !== undefined || values.limit !== undefined))
386
389
  throw new TypeError("id cannot be combined with last, before, since, or limit");
387
390
  }
388
- export async function historyAkuma(input) {
391
+ export async function historyAkuma(input, reportDiagnostic = stderrPluginDiagnostic) {
389
392
  const values = requireInput(input, "Akumas.history input");
390
393
  validateHistoryInput(values);
391
394
  const addressed = await addressAkuma(directAddress(values));
392
395
  try {
393
- const handle = new AkumaOwner(addressed.id, addressed.path);
396
+ const handle = new AkumaOwner(addressed.id, addressed.path, outcomePluginObserver({ world: addressed.path, reportDiagnostic }));
394
397
  const contract = (await dispatchAssociations(values.repo, [addressed.id])).get(addressed.id);
395
398
  if (values.last === true) {
396
399
  const answer = await handle.lastAnswer();
@@ -1,18 +1,19 @@
1
1
  import type { Settings } from "../settings.js";
2
2
  import type { WorldRoot } from "../world.js";
3
- type CalledPluginSignalInput = Readonly<{
3
+ import type { PluginSignal } from "./public.js";
4
+ /** The local library observation default; CLI and Body supply their own sinks. */
5
+ export declare function stderrPluginDiagnostic(message: string): void;
6
+ type PluginSignalEmission = Readonly<{
4
7
  world: WorldRoot;
5
8
  settings?: Settings;
6
9
  reportDiagnostic: (message: string) => void;
7
- akumaId: string;
8
- callerAkumaId?: string;
9
- contractId?: string;
10
10
  }>;
11
- export declare function emitInitiatingPluginSignal(input: Readonly<{
12
- world: WorldRoot;
13
- settings?: Settings;
14
- initiator: string;
15
- reportDiagnostic: (message: string) => void;
16
- }>): Promise<void>;
17
- export declare function emitCalledPluginSignal(input: CalledPluginSignalInput): Promise<void>;
11
+ /**
12
+ * The one producer entry for plugin signals. It owns runtime acquisition, the
13
+ * World Settings fallback, bounded delivery, and failure isolation: a delivery
14
+ * problem reaches the producer only as a diagnostic, never as a thrown error.
15
+ */
16
+ export declare function emitPluginSignal(emission: PluginSignalEmission, signal: PluginSignal): Promise<void>;
17
+ /** One public observer operation, never shared with incidental owner reads. */
18
+ export declare function outcomePluginObserver(emission: PluginSignalEmission, observer?: string): (akumaId: string, historyIds: readonly string[]) => Promise<void>;
18
19
  export {};
@@ -1,22 +1,42 @@
1
- import { pluginRuntime } from "./runtime.js";
2
- export async function emitInitiatingPluginSignal(input) {
3
- const runtime = await pluginRuntime({
4
- world: input.world,
5
- ...(input.settings === undefined ? {} : { settings: input.settings }),
6
- reportDiagnostic: input.reportDiagnostic,
7
- });
8
- await runtime.emit({ kind: "akuma.initiating", initiator: input.initiator }, input.reportDiagnostic);
1
+ import { pluginRuntime, reportPluginDiagnostic } from "./runtime.js";
2
+ /** The local library observation default; CLI and Body supply their own sinks. */
3
+ export function stderrPluginDiagnostic(message) {
4
+ console.error(message);
9
5
  }
10
- export async function emitCalledPluginSignal(input) {
11
- const runtime = await pluginRuntime({
12
- world: input.world,
13
- ...(input.settings === undefined ? {} : { settings: input.settings }),
14
- reportDiagnostic: input.reportDiagnostic,
15
- });
16
- await runtime.emit({
17
- kind: "akuma.called",
18
- akumaId: input.akumaId,
19
- ...(input.callerAkumaId === undefined ? {} : { callerAkumaId: input.callerAkumaId }),
20
- ...(input.contractId === undefined ? {} : { contractId: input.contractId }),
21
- }, input.reportDiagnostic);
6
+ /**
7
+ * The one producer entry for plugin signals. It owns runtime acquisition, the
8
+ * World Settings fallback, bounded delivery, and failure isolation: a delivery
9
+ * problem reaches the producer only as a diagnostic, never as a thrown error.
10
+ */
11
+ export async function emitPluginSignal(emission, signal) {
12
+ try {
13
+ const runtime = await pluginRuntime({
14
+ world: emission.world,
15
+ ...(emission.settings === undefined ? {} : { settings: emission.settings }),
16
+ reportDiagnostic: emission.reportDiagnostic,
17
+ });
18
+ await runtime.emit(signal, emission.reportDiagnostic);
19
+ }
20
+ catch (error) {
21
+ reportPluginDiagnostic(emission.reportDiagnostic, "runtime", "signal", error);
22
+ }
23
+ }
24
+ /** One public observer operation, never shared with incidental owner reads. */
25
+ export function outcomePluginObserver(emission, observer) {
26
+ const seen = new Set();
27
+ return async (akumaId, historyIds) => {
28
+ const fresh = historyIds.filter((historyId) => {
29
+ const key = `${akumaId}:${historyId}`;
30
+ if (seen.has(key))
31
+ return false;
32
+ seen.add(key);
33
+ return true;
34
+ });
35
+ await Promise.all(fresh.map(async (historyId) => await emitPluginSignal(emission, {
36
+ kind: "akuma.outcome-observed",
37
+ akumaId,
38
+ historyId,
39
+ ...(observer === undefined ? {} : { observer }),
40
+ })));
41
+ };
22
42
  }
@@ -15,15 +15,45 @@ export type PluginOutcome = Readonly<{
15
15
  reason: string;
16
16
  }>;
17
17
  export type PluginBodyEnd = "exited" | "broke-off" | "put-down" | "hung";
18
+ export type PluginAkumaOrigin = Readonly<{
19
+ kind: "direct";
20
+ }> | Readonly<{
21
+ kind: "request";
22
+ parent: string;
23
+ requestId: string;
24
+ }> | Readonly<{
25
+ kind: "fork";
26
+ parent: string;
27
+ at: string;
28
+ }>;
29
+ export type PluginAkumaCaller = Readonly<{
30
+ kind: "akuma";
31
+ world: string;
32
+ akuId: string;
33
+ }> | Readonly<{
34
+ kind: "external";
35
+ integration: string;
36
+ id: string;
37
+ }> | Readonly<{
38
+ kind: "unknown";
39
+ }>;
18
40
  export type PluginSignalMap = Readonly<{
19
41
  "akuma.initiating": Readonly<{
20
42
  initiator: string;
21
43
  }>;
22
- "akuma.called": Readonly<{
44
+ "akuma.born": Readonly<{
23
45
  akumaId: string;
24
- callerAkumaId?: string;
46
+ bodySequence: number;
47
+ origin: PluginAkumaOrigin;
48
+ caller: PluginAkumaCaller;
49
+ initiator?: string;
25
50
  contractId?: string;
26
51
  }>;
52
+ "akuma.outcome-observed": Readonly<{
53
+ akumaId: string;
54
+ historyId: string;
55
+ observer?: string;
56
+ }>;
27
57
  "akuma.turn-outcome": Readonly<{
28
58
  akumaId: string;
29
59
  bodySequence: number;
@@ -10,5 +10,6 @@ type PluginRuntimeInput = Readonly<{
10
10
  export type PluginRuntime = Readonly<{
11
11
  emit(signal: PluginSignal, reportDiagnostic?: PluginDiagnostic): Promise<void>;
12
12
  }>;
13
+ export declare function reportPluginDiagnostic(report: PluginDiagnostic | undefined, subject: string, stage: string, error: unknown): void;
13
14
  export declare function pluginRuntime(input: PluginRuntimeInput): Promise<PluginRuntime>;
14
15
  export {};
@@ -18,11 +18,11 @@ function object(value) {
18
18
  function message(error) {
19
19
  return (error instanceof Error ? error.message : String(error)).slice(0, DIAGNOSTIC_LIMIT);
20
20
  }
21
- function diagnostic(report, subject, stage, error) {
21
+ export function reportPluginDiagnostic(report, subject, stage, error) {
22
22
  if (report === undefined)
23
23
  return;
24
24
  try {
25
- report(`plugin ${subject} ${stage}: ${message(error)}`.slice(0, DIAGNOSTIC_LIMIT));
25
+ void Promise.resolve(report(`plugin ${subject} ${stage}: ${message(error)}`.slice(0, DIAGNOSTIC_LIMIT))).catch(() => { });
26
26
  }
27
27
  catch {
28
28
  // Diagnostics are optional side effects too.
@@ -67,30 +67,30 @@ async function settleBefore(promise, cancellation) {
67
67
  }
68
68
  function selectedEntry(entry, report) {
69
69
  if (entry.name.trim().length === 0) {
70
- diagnostic(report, "<unnamed>", "selection", new TypeError("plugin setting name must be nonblank"));
70
+ reportPluginDiagnostic(report, "<unnamed>", "selection", new TypeError("plugin setting name must be nonblank"));
71
71
  return null;
72
72
  }
73
73
  if (!object(entry.value)) {
74
- diagnostic(report, entry.name, "selection", new TypeError("plugin setting must be an object"));
74
+ reportPluginDiagnostic(report, entry.name, "selection", new TypeError("plugin setting must be an object"));
75
75
  return null;
76
76
  }
77
77
  const names = Object.keys(entry.value);
78
78
  if (!names.includes("package") || names.some((name) => !["package", "enabled", "config"].includes(name))) {
79
- diagnostic(report, entry.name, "selection", new TypeError("plugin setting has unknown or missing fields"));
79
+ reportPluginDiagnostic(report, entry.name, "selection", new TypeError("plugin setting has unknown or missing fields"));
80
80
  return null;
81
81
  }
82
82
  const packageName = entry.value.package;
83
83
  if (typeof packageName !== "string" || packageName.trim().length === 0) {
84
- diagnostic(report, entry.name, "selection", new TypeError("plugin package must be a nonblank string"));
84
+ reportPluginDiagnostic(report, entry.name, "selection", new TypeError("plugin package must be a nonblank string"));
85
85
  return null;
86
86
  }
87
87
  if (packageName !== packageName.trim()) {
88
- diagnostic(report, entry.name, "selection", new TypeError("plugin package must not have surrounding whitespace"));
88
+ reportPluginDiagnostic(report, entry.name, "selection", new TypeError("plugin package must not have surrounding whitespace"));
89
89
  return null;
90
90
  }
91
91
  const enabled = entry.value.enabled;
92
92
  if (enabled !== undefined && typeof enabled !== "boolean") {
93
- diagnostic(report, entry.name, "selection", new TypeError("plugin enabled must be a boolean"));
93
+ reportPluginDiagnostic(report, entry.name, "selection", new TypeError("plugin enabled must be a boolean"));
94
94
  return null;
95
95
  }
96
96
  if (enabled === false)
@@ -101,7 +101,7 @@ function selectedPlugins(input, report) {
101
101
  const view = input.namespace(PLUGIN_NAMESPACE);
102
102
  if (view.kind === "failed") {
103
103
  for (const failure of view.failures)
104
- diagnostic(report, "settings", failure.scope, failure.diagnostic);
104
+ reportPluginDiagnostic(report, "settings", failure.scope, failure.diagnostic);
105
105
  return BUILTIN_PLUGINS;
106
106
  }
107
107
  if (view.entries.length === 0)
@@ -197,6 +197,21 @@ async function writablePaths(world, declarations) {
197
197
  }
198
198
  return paths;
199
199
  }
200
+ /**
201
+ * The runtime's acceptance vocabulary. The record is exhaustive over the public
202
+ * payload map, so adding a signal there fails compilation here until the same
203
+ * kind is accepted.
204
+ */
205
+ const SIGNAL_KINDS = Object.freeze({
206
+ "akuma.initiating": true,
207
+ "akuma.born": true,
208
+ "akuma.outcome-observed": true,
209
+ "akuma.turn-outcome": true,
210
+ "akuma.body-ended": true,
211
+ });
212
+ function isSignalKind(kind) {
213
+ return Object.hasOwn(SIGNAL_KINDS, kind);
214
+ }
200
215
  function handlers(pluginId, instance) {
201
216
  if (!object(instance))
202
217
  throw new TypeError("plugin activation must return an object");
@@ -208,12 +223,8 @@ function handlers(pluginId, instance) {
208
223
  throw new TypeError("plugin signals must be an object");
209
224
  const registered = [];
210
225
  for (const [kind, handler] of Object.entries(instance.signals)) {
211
- if (kind !== "akuma.initiating" &&
212
- kind !== "akuma.called" &&
213
- kind !== "akuma.turn-outcome" &&
214
- kind !== "akuma.body-ended") {
226
+ if (!isSignalKind(kind))
215
227
  throw new TypeError(`plugin signal is unknown: ${kind}`);
216
- }
217
228
  if (typeof handler !== "function")
218
229
  throw new TypeError(`plugin signal handler is not a function: ${kind}`);
219
230
  registered.push({ pluginId, kind, handler });
@@ -270,7 +281,7 @@ async function activate(input) {
270
281
  }
271
282
  catch (error) {
272
283
  markStarted();
273
- diagnostic(report, selected.id, "import", error);
284
+ reportPluginDiagnostic(report, selected.id, "import", error);
274
285
  return [];
275
286
  }
276
287
  if (cancellation.aborted) {
@@ -288,7 +299,7 @@ async function activate(input) {
288
299
  }
289
300
  catch (error) {
290
301
  markStarted();
291
- diagnostic(report, selected.id, "validation", error);
302
+ reportPluginDiagnostic(report, selected.id, "validation", error);
292
303
  return [];
293
304
  }
294
305
  let context;
@@ -307,7 +318,7 @@ async function activate(input) {
307
318
  }
308
319
  catch (error) {
309
320
  markStarted();
310
- diagnostic(report, selected.id, "validation", error);
321
+ reportPluginDiagnostic(report, selected.id, "validation", error);
311
322
  return [];
312
323
  }
313
324
  if (cancellation.aborted) {
@@ -324,16 +335,16 @@ async function activate(input) {
324
335
  return registered;
325
336
  }
326
337
  catch (error) {
327
- diagnostic(report, selected.id, "activation", error);
338
+ reportPluginDiagnostic(report, selected.id, "activation", error);
328
339
  return [];
329
340
  }
330
341
  }
331
342
  async function deliver(entry, signal, reportDiagnostic, timeoutMs) {
332
343
  const result = await boundedAttempt((cancellation) => entry.handler(signal, cancellation), timeoutMs);
333
344
  if (result.kind === "timed-out")
334
- diagnostic(reportDiagnostic, entry.pluginId, "signal", timeoutError());
345
+ reportPluginDiagnostic(reportDiagnostic, entry.pluginId, "signal", timeoutError());
335
346
  if (result.kind === "failed")
336
- diagnostic(reportDiagnostic, entry.pluginId, "signal", result.error);
347
+ reportPluginDiagnostic(reportDiagnostic, entry.pluginId, "signal", result.error);
337
348
  }
338
349
  export async function pluginRuntime(input) {
339
350
  let runtime = PROCESS_RUNTIMES.get(input.world);
@@ -358,7 +369,7 @@ async function createPluginRuntime(input) {
358
369
  releaseStarted();
359
370
  if (result.kind === "settled")
360
371
  return { handlers: result.value, timedOut: false };
361
- diagnostic(report, entry.id, "activation", result.kind === "failed" ? result.error : timeoutError());
372
+ reportPluginDiagnostic(report, entry.id, "activation", result.kind === "failed" ? result.error : timeoutError());
362
373
  return { handlers: [], timedOut: result.kind === "timed-out" };
363
374
  });
364
375
  previousStarted = started;
@@ -372,16 +383,16 @@ async function createPluginRuntime(input) {
372
383
  const activatedHandlers = await settleBefore(activation, cancellation);
373
384
  if (activatedHandlers !== ABORTED) {
374
385
  if (activatedHandlers.timedOut)
375
- diagnostic(reportDiagnostic, selected[index].id, "signal", timeoutError());
386
+ reportPluginDiagnostic(reportDiagnostic, selected[index].id, "signal", timeoutError());
376
387
  return activatedHandlers.handlers;
377
388
  }
378
- diagnostic(reportDiagnostic, selected[index].id, "signal", timeoutError());
389
+ reportPluginDiagnostic(reportDiagnostic, selected[index].id, "signal", timeoutError());
379
390
  return [];
380
391
  }));
381
392
  return handlers.flat();
382
393
  });
383
394
  if (activated.kind === "failed") {
384
- diagnostic(reportDiagnostic, "runtime", "signal", activated.error);
395
+ reportPluginDiagnostic(reportDiagnostic, "runtime", "signal", activated.error);
385
396
  return;
386
397
  }
387
398
  if (activated.kind === "timed-out")
@@ -3,7 +3,8 @@ import { withPrivateStatePublicationSeat } from "../git/private-state-seat.js";
3
3
  import { contractState } from "../core/facts/observation.js";
4
4
  import { decideAmend } from "../core/verbs/amend.js";
5
5
  import { admitDecidedOffer, mintAttempts } from "./attempt.js";
6
- import { admitted, intentOutcomeWithSeatClose } from "./outcome.js";
6
+ import { admitted } from "./outcome.js";
7
+ import { protocolSeatClose } from "./run.js";
7
8
  import { timestamp } from "./operations.js";
8
9
  async function extendPrerequisiteClosureAt(channel, observation, seeds) {
9
10
  let current = observation;
@@ -34,7 +35,7 @@ export async function amendOperation(input) {
34
35
  const amendment = source === undefined || input.deriveAmendment === undefined
35
36
  ? undefined
36
37
  : { source, ...input.deriveAmendment(source) };
37
- return intentOutcomeWithSeatClose(await withPrivateStatePublicationSeat(input.scope, async (seat) => {
38
+ return protocolSeatClose(await withPrivateStatePublicationSeat(input.scope, async (seat) => {
38
39
  const attempts = mintAttempts({ entryCount: 1 });
39
40
  for (let index = 0; index < attempts.length; index += 1) {
40
41
  let observation = await observeContractsForAdmissionAt(input.scope, input.channel, [input.contractId]);
@@ -65,12 +65,6 @@ function validateOffer(offer, attempt) {
65
65
  if (actualEntries.size === 0)
66
66
  throw new Error("offer requires at least one current-attempt ULID");
67
67
  }
68
- function snapshotFor(append, journals, head) {
69
- const record = journals.get(append.contractId);
70
- if (record === undefined)
71
- throw new Error("accepted offer contract is missing from its observation");
72
- return foldJournal(append.contractId, [...record.entries, ...append.entries], head);
73
- }
74
68
  function journalFor(append, journals) {
75
69
  const record = journals.get(append.contractId);
76
70
  if (record === undefined)
@@ -118,11 +112,12 @@ export async function admitDecidedOffer(input) {
118
112
  if (admission.kind === "accepted") {
119
113
  confirmPrivateStatePublication(seat);
120
114
  input.progress?.recordPublication(primaryContract, admission.heads[primary.contractId], offerEntries(offer));
115
+ const journal = journalFor(primary, decisionObservation.journals);
121
116
  const accepted = {
122
117
  kind: "accepted",
123
118
  facts: offerEntries(offer),
124
- state: snapshotFor(primary, decisionObservation.journals, admission.heads[primary.contractId]),
125
- journal: journalFor(primary, decisionObservation.journals),
119
+ state: foldJournal(primary.contractId, journal, admission.heads[primary.contractId]),
120
+ journal,
126
121
  };
127
122
  input.progress?.recordAdmission(accepted);
128
123
  return accepted;
@@ -11,8 +11,8 @@ export type { BindRefusal } from "../core/verbs/bind.js";
11
11
  export type { BindTargetSelection };
12
12
  export declare const targetInputRefusalSchema: z.ZodObject<{
13
13
  kind: z.ZodEnum<{
14
- "unborn-head": "unborn-head";
15
14
  "target-missing": "target-missing";
15
+ "unborn-head": "unborn-head";
16
16
  "invalid-target": "invalid-target";
17
17
  }>;
18
18
  }, z.core.$strict>;
@@ -27,7 +27,7 @@ type BindOperationInput = Readonly<{
27
27
  channel: GitDecodeChannel;
28
28
  terms: BindData["terms"];
29
29
  verification: VerificationDeclarationPreparation;
30
- targetSelection?: BindTargetSelection;
30
+ targetSelection: BindTargetSelection;
31
31
  workspace: "worktree";
32
32
  actor?: ActorId;
33
33
  decorateOffer?: CompanionDecorator;