@astrosheep/keiyaku 4.6.7 → 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 (165) hide show
  1. package/README.md +6 -2
  2. package/build/plugins/pi/keiyaku.ts +0 -5
  3. package/build/src/akuma/akuma-call.d.ts +2 -0
  4. package/build/src/akuma/akuma-call.js +8 -2
  5. package/build/src/akuma/akuma-instance.d.ts +12 -1
  6. package/build/src/akuma/akuma-instance.js +27 -7
  7. package/build/src/akuma/akuma-observe.js +2 -0
  8. package/build/src/akuma/akuma-owner.d.ts +3 -0
  9. package/build/src/akuma/akuma-owner.js +4 -0
  10. package/build/src/akuma/akuma.d.ts +35 -5
  11. package/build/src/akuma/akuma.js +8 -10
  12. package/build/src/akuma/archetype.d.ts +7 -15
  13. package/build/src/akuma/archetype.js +36 -35
  14. package/build/src/akuma/body.d.ts +5 -3
  15. package/build/src/akuma/body.js +29 -7
  16. package/build/src/akuma/call-request.d.ts +18 -10
  17. package/build/src/akuma/call-request.js +10 -3
  18. package/build/src/akuma/caller.d.ts +39 -0
  19. package/build/src/akuma/caller.js +36 -0
  20. package/build/src/akuma/dispatch-association.d.ts +0 -1
  21. package/build/src/akuma/dispatch-association.js +0 -3
  22. package/build/src/akuma/heart/facts.d.ts +16 -12
  23. package/build/src/akuma/heart/facts.js +8 -0
  24. package/build/src/akuma/heart/index.d.ts +2 -7
  25. package/build/src/akuma/heart/index.js +3 -17
  26. package/build/src/akuma/heart/rows.d.ts +3 -3
  27. package/build/src/akuma/heart/soul.d.ts +1 -1
  28. package/build/src/akuma/heart/soul.js +5 -24
  29. package/build/src/akuma/heart/storage.d.ts +5 -0
  30. package/build/src/akuma/heart/storage.js +7 -3
  31. package/build/src/akuma/identity.d.ts +0 -1
  32. package/build/src/akuma/identity.js +2 -5
  33. package/build/src/akuma/index.d.ts +5 -2
  34. package/build/src/akuma/index.js +1 -0
  35. package/build/src/akuma/nuke.js +6 -28
  36. package/build/src/akuma/projection.d.ts +0 -11
  37. package/build/src/akuma/provider-recipe.d.ts +7 -6
  38. package/build/src/akuma/provider-recipe.js +1 -1
  39. package/build/src/akuma/provider.d.ts +6 -10
  40. package/build/src/akuma/provider.js +6 -7
  41. package/build/src/akuma/providers/acp/events.js +5 -18
  42. package/build/src/akuma/providers/acp/index.js +2 -3
  43. package/build/src/akuma/providers/claude/events.js +35 -60
  44. package/build/src/akuma/providers/claude/index.d.ts +2 -2
  45. package/build/src/akuma/providers/claude/index.js +2 -3
  46. package/build/src/akuma/providers/codex-app-server/index.js +2 -2
  47. package/build/src/akuma/providers/decode.d.ts +25 -0
  48. package/build/src/akuma/providers/decode.js +44 -0
  49. package/build/src/akuma/providers/execution-environment.d.ts +2 -1
  50. package/build/src/akuma/providers/execution-environment.js +3 -0
  51. package/build/src/akuma/providers/grok-build/index.js +6 -22
  52. package/build/src/akuma/providers/index.js +2 -0
  53. package/build/src/akuma/providers/lifecycle.d.ts +9 -0
  54. package/build/src/akuma/providers/lifecycle.js +10 -0
  55. package/build/src/akuma/providers/opencode-sdk/events.js +1 -14
  56. package/build/src/akuma/providers/opencode-sdk/index.js +10 -17
  57. package/build/src/akuma/providers/opencode-sdk/session.js +1 -3
  58. package/build/src/akuma/providers/paseo/events.d.ts +25 -0
  59. package/build/src/akuma/providers/paseo/events.js +157 -0
  60. package/build/src/akuma/providers/paseo/index.d.ts +9 -0
  61. package/build/src/akuma/providers/paseo/index.js +353 -0
  62. package/build/src/akuma/providers/pi/events.js +5 -15
  63. package/build/src/akuma/publication.d.ts +2 -0
  64. package/build/src/akuma/publication.js +5 -0
  65. package/build/src/akuma/request-lifecycle.d.ts +3 -0
  66. package/build/src/akuma/request-lifecycle.js +25 -4
  67. package/build/src/akuma/requests.d.ts +3 -1
  68. package/build/src/akuma/requests.js +32 -0
  69. package/build/src/akuma/selection-observation.d.ts +72 -2
  70. package/build/src/akuma/selection-observation.js +0 -6
  71. package/build/src/akuma/turn-drive.js +2 -5
  72. package/build/src/cli/akuma-caller.d.ts +3 -0
  73. package/build/src/cli/akuma-caller.js +21 -0
  74. package/build/src/cli/commands/akuma-grammar.js +7 -12
  75. package/build/src/cli/commands/akuma.js +12 -4
  76. package/build/src/cli/commands/contract-grammar.d.ts +2 -1
  77. package/build/src/cli/commands/contract-grammar.js +13 -15
  78. package/build/src/cli/commands/contract-help.d.ts +2 -1
  79. package/build/src/cli/commands/contract-help.js +31 -1
  80. package/build/src/cli/commands/contract.js +24 -53
  81. package/build/src/cli/commands/task.js +18 -35
  82. package/build/src/cli/parse.d.ts +2 -2
  83. package/build/src/cli/render/catalog.d.ts +2 -5
  84. package/build/src/cli/render/contract-history.js +3 -6
  85. package/build/src/cli/render/contract.js +15 -50
  86. package/build/src/cli/render/receipt.js +3 -10
  87. package/build/src/cli/render/task.d.ts +4 -6
  88. package/build/src/cli/render/task.js +9 -26
  89. package/build/src/cli/runtime.d.ts +1 -1
  90. package/build/src/cli/runtime.js +11 -17
  91. package/build/src/cli/selectors.d.ts +3 -6
  92. package/build/src/cli/selectors.js +22 -53
  93. package/build/src/dispatch/index.d.ts +3 -3
  94. package/build/src/dispatch/index.js +1 -2
  95. package/build/src/git/observe.d.ts +6 -2
  96. package/build/src/git/observe.js +6 -2
  97. package/build/src/index.d.ts +6 -6
  98. package/build/src/index.js +2 -2
  99. package/build/src/kanshi/index.d.ts +1 -1
  100. package/build/src/kanshi/read.js +21 -6
  101. package/build/src/library/address.js +2 -4
  102. package/build/src/library/akuma-creation.d.ts +5 -1
  103. package/build/src/library/akuma-creation.js +27 -47
  104. package/build/src/library/akumas.d.ts +5 -4
  105. package/build/src/library/akumas.js +13 -9
  106. package/build/src/library/bind.d.ts +14 -14
  107. package/build/src/library/bind.js +11 -7
  108. package/build/src/library/contract-address.d.ts +9 -0
  109. package/build/src/library/contract-address.js +71 -0
  110. package/build/src/library/contract-composition.d.ts +4 -7
  111. package/build/src/library/contract-composition.js +6 -18
  112. package/build/src/library/contract-creation.d.ts +3 -5
  113. package/build/src/library/contract-creation.js +19 -17
  114. package/build/src/library/contract-execution.js +18 -23
  115. package/build/src/library/contract-outcomes.d.ts +2 -14
  116. package/build/src/library/contract-outcomes.js +2 -13
  117. package/build/src/library/contract-types.d.ts +36 -2
  118. package/build/src/library/delivery.d.ts +0 -2
  119. package/build/src/library/delivery.js +2 -5
  120. package/build/src/library/keiyaku.d.ts +0 -5
  121. package/build/src/library/keiyaku.js +6 -12
  122. package/build/src/library/outcome.d.ts +500 -499
  123. package/build/src/library/outcome.js +10 -0
  124. package/build/src/library/reconcile.js +8 -16
  125. package/build/src/library/refusal.d.ts +2 -2
  126. package/build/src/library/repo.d.ts +3 -2
  127. package/build/src/library/repo.js +10 -5
  128. package/build/src/library/selection.d.ts +4 -4
  129. package/build/src/library/selection.js +12 -12
  130. package/build/src/plugin/akuma-signals.d.ts +2 -0
  131. package/build/src/plugin/akuma-signals.js +4 -0
  132. package/build/src/plugin/public.d.ts +27 -2
  133. package/build/src/plugin/runtime.js +2 -2
  134. package/build/src/protocol/amend.js +3 -2
  135. package/build/src/protocol/attempt.js +3 -8
  136. package/build/src/protocol/bind.d.ts +2 -2
  137. package/build/src/protocol/bind.js +1 -1
  138. package/build/src/protocol/deliver.js +3 -3
  139. package/build/src/protocol/operations.d.ts +1 -3
  140. package/build/src/protocol/operations.js +0 -8
  141. package/build/src/protocol/outcome.d.ts +1 -2
  142. package/build/src/protocol/outcome.js +1 -13
  143. package/build/src/protocol/placement.js +3 -10
  144. package/build/src/protocol/read/status.js +31 -70
  145. package/build/src/protocol/reintegrate.js +3 -10
  146. package/build/src/protocol/review.js +3 -3
  147. package/build/src/protocol/run.d.ts +5 -1
  148. package/build/src/protocol/run.js +3 -3
  149. package/build/src/runtime/proc/windows-launch.exe +0 -0
  150. package/package.json +8 -4
  151. package/plugins/pi/keiyaku.ts +57 -0
  152. package/skills/keiyaku/SKILL.md +44 -0
  153. package/skills/keiyaku-akuma/SKILL.md +130 -0
  154. package/skills/keiyaku-akuma/references/automation.md +269 -0
  155. package/skills/keiyaku-babysit/SKILL.md +65 -0
  156. package/skills/keiyaku-bind/SKILL.md +216 -0
  157. package/skills/keiyaku-bind/agents/openai.yaml +4 -0
  158. package/skills/keiyaku-task/SKILL.md +104 -0
  159. package/skills/keiyaku-workflow/SKILL.md +129 -0
  160. package/build/src/cli/commands/amend.d.ts +0 -12
  161. package/build/src/cli/commands/amend.js +0 -10
  162. package/build/src/cli/commands/bind.d.ts +0 -11
  163. package/build/src/cli/commands/bind.js +0 -23
  164. package/build/src/dispatch/association.d.ts +0 -1
  165. package/build/src/dispatch/association.js +0 -1
@@ -3,7 +3,7 @@ import { type AkuId } from "../akuma/identity.js";
3
3
  import { type PrivateStateSeatCloseLag } from "../git/private-state-seat.js";
4
4
  import type { GitRepository } from "../git/process.js";
5
5
  import { type GitReadObservation } from "../git/read-observation.js";
6
- import { type DispatchAssociation } from "./association.js";
6
+ import { type DispatchAssociation } from "../akuma/dispatch-association.js";
7
7
  export type Dispatch = Readonly<{
8
8
  akuId: AkuId;
9
9
  contractId: ContractId;
@@ -16,7 +16,7 @@ export type DispatchFailure = Readonly<{
16
16
  kind: "publication-failed";
17
17
  diagnostic: string;
18
18
  }>;
19
- export type DispatchPublication = Readonly<{
19
+ type DispatchPublication = Readonly<{
20
20
  kind: "dispatched";
21
21
  dispatch: Dispatch;
22
22
  seatClose?: readonly PrivateStateSeatCloseLag[];
@@ -32,6 +32,6 @@ export declare function publishDispatch(input: Readonly<{
32
32
  akuId: AkuId;
33
33
  contractId: ContractId;
34
34
  }>): Promise<DispatchPublication>;
35
- export { dispatchAssociationSchema, NO_DISPATCH_ASSOCIATION, parseDispatchAssociation, type DispatchAssociation, } from "./association.js";
36
35
  /** Read selected associations in one targeted observation, isolating per-identity decoding failures. */
37
36
  export declare function observeDispatchAssociations(repository: GitRepository | undefined, akuIds: readonly AkuId[]): Promise<ReadonlyMap<AkuId, DispatchAssociation>>;
37
+ export {};
@@ -5,7 +5,7 @@ import { parseAkuId } from "../akuma/identity.js";
5
5
  import { appendPrivateStateSeatClose, confirmPrivateStatePublication, mergePrivateStateSeatClose, withPrivateStatePublicationSeat, } from "../git/private-state-seat.js";
6
6
  import { GIT_FORMAT_BYTES, GIT_FORMAT_PATH, GIT_REF, readBlob, readGitPaths, updateGitTree, updateRefsAtomically, writeBlob, writeStateCommit, } from "../git/repository.js";
7
7
  import { withGitDecodeChannel, withGitReadObservation, withGitTargetedReadObservation, } from "../git/read-observation.js";
8
- import { NO_DISPATCH_ASSOCIATION } from "./association.js";
8
+ import { NO_DISPATCH_ASSOCIATION } from "../akuma/dispatch-association.js";
9
9
  const DISPATCH_ROOT = "dispatch";
10
10
  const DISPATCH_PREFIX = `${DISPATCH_ROOT}/`;
11
11
  const DISPATCH_SUFFIX = ".json";
@@ -186,7 +186,6 @@ export async function publishDispatch(input) {
186
186
  };
187
187
  }));
188
188
  }
189
- export { dispatchAssociationSchema, NO_DISPATCH_ASSOCIATION, parseDispatchAssociation, } from "./association.js";
190
189
  /** Read selected associations in one targeted observation, isolating per-identity decoding failures. */
191
190
  export async function observeDispatchAssociations(repository, akuIds) {
192
191
  const requested = [...new Set(akuIds.map((akuId) => parseAkuId(akuId).id))];
@@ -49,8 +49,12 @@ export type BindTargetSelection = Readonly<{
49
49
  export declare function normalizeTargetBranch(repository: GitRepository, input: string): Promise<string | null>;
50
50
  /** Read one worktree's attached branch, or null for detached HEAD. */
51
51
  export declare function currentBranch(repository: GitRepository, path?: string): Promise<string | null>;
52
- /** Observe bind's tender start and optional reward target without creating persistent state. */
53
- export declare function observeBindCoordinates(repository: GitRepository, selection?: BindTargetSelection): Promise<BindCoordinatesObservation | UnbornHeadObservation | null>;
52
+ /**
53
+ * Observe bind's tender start and optional reward target without creating persistent state.
54
+ * `current-branch` reads the observed worktree coordinate's own attached branch; the caller names
55
+ * which coordinate it wants observed.
56
+ */
57
+ export declare function observeBindCoordinates(repository: GitRepository, selection: BindTargetSelection): Promise<BindCoordinatesObservation | UnbornHeadObservation | null>;
54
58
  export declare function observeActiveContractWorld(observation: GitReadObservation): Promise<ActiveContractWorldObservation>;
55
59
  export declare function observeContractWorld(observation: GitReadObservation, requested?: readonly ContractId[]): Promise<ContractWorldObservation>;
56
60
  export declare function observeContractsForAdmissionAt(repository: GitRepository, channel: GitDecodeChannel, ids: readonly ContractId[], selection?: GitTreeSelection): Promise<GitDecisionObservation>;
@@ -77,8 +77,12 @@ async function observeHeadCoordinates(repository) {
77
77
  throw error;
78
78
  }
79
79
  }
80
- /** Observe bind's tender start and optional reward target without creating persistent state. */
81
- export async function observeBindCoordinates(repository, selection = { kind: "targetless" }) {
80
+ /**
81
+ * Observe bind's tender start and optional reward target without creating persistent state.
82
+ * `current-branch` reads the observed worktree coordinate's own attached branch; the caller names
83
+ * which coordinate it wants observed.
84
+ */
85
+ export async function observeBindCoordinates(repository, selection) {
82
86
  if (selection.kind === "explicit") {
83
87
  const requestedTarget = selection.target;
84
88
  if (isKeiyakuOwnedRef(requestedTarget)) {
@@ -45,17 +45,17 @@ export { actorId } from "./core/facts/types.js";
45
45
  * configuration helpers stay off this entry; only CLI and Body import them.
46
46
  */
47
47
  export { z } from "zod";
48
- export { ALLOWED_ACTIONS, Akuma, AkumaBusyError, AkumaDecodeError, AkumaNotBornError, AkumaProviderError, Schema, } from "./akuma/index.js";
49
- export type { ActivityHistory, ActivityRow, AkuId, AkumaAskObservation, AkumaAskOptions, AkumaAskResult, AkumaBirthInput, AkumaHistoryOptions, AkumaIdleOptions, AkumaIdleResult, AkumaSignalOptions, AkumaStatus, AkumaTellOptions, AkumaTellResult, AllowedAction, AllowedActions, InterruptReceipt, JsonSchema, JsonSchemaDocument, KillEvidence, SchemaLike, StandardSchemaV1, } from "./akuma/index.js";
48
+ 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, AkumaTellOptions, AkumaTellResult, AllowedAction, AllowedActions, ArchetypeCatalog, ArchetypeCatalogInput, ArchetypeCatalogRow, InterruptReceipt, JsonSchema, JsonSchemaDocument, KillEvidence, SchemaLike, StandardSchemaV1, } from "./akuma/index.js";
50
50
  export type { ActorId, ChangeId, ContractHead, ContractId, ContractState, JournalEntry as Fact, FactKind, SnapshotId, } from "./core/facts/types.js";
51
- export { kanshi, observeKanshi, selectKanshi, selectRegion } from "./kanshi/index.js";
52
- export type { AkumaKanshiRow, AkumaKanshiWorld, ContractEndpointObservation, ContractHolderObservation, ContractKanshiBoard, ContractKanshiRow, KanshiInput, KanshiRegionSelection, KanshiReport, KanshiSelection, RegionDeclaration, RegionRead, Section, TaskKanshiRow, TaskKanshiWorld, } from "./kanshi/index.js";
51
+ export { kanshi, kanshiCategory, observeKanshi, selectKanshi, selectRegion } from "./kanshi/index.js";
52
+ export type { AkumaKanshiRow, AkumaKanshiWorld, ContractEndpointObservation, ContractHolderObservation, ContractKanshiBoard, ContractKanshiRow, KanshiCategoryInput, KanshiCategoryReport, KanshiInput, KanshiRegionSelection, KanshiReport, KanshiSelection, RegionDeclaration, RegionRead, Section, TaskKanshiRow, TaskKanshiWorld, } from "./kanshi/index.js";
53
53
  export { AkumaAddressError, AkumaWorldScopeError, Akumas } from "./library/akumas.js";
54
54
  export type { AkumaAddressInput, AkumaAskInput, AkumaHistoryInput, AkumaHistoryResult, AkumaKillInput, AkumaKillResult, AkumaList, AkumaListInput, AkumaObservation, AkumaObservationStage, AkumaTellInput, AkumaWaitInput, AkumaWaitResult, AkumaWorldScopeRefusal, CallInput, CallResult, CallWaitHead, CreatedTaskObservation, DispatchAssociation, DispatchStage, ForkInput, ForkResult, TellResult, TellWake, WaitObservedAkuma, WaitObserver, WaitSelectedAkuma, } from "./library/akumas.js";
55
55
  export type { ContinuationReport } from "./library/continuation.js";
56
56
  export type { KeiyakuLibrary, ReconcileInput } from "./library/contract-composition.js";
57
57
  export type { AbandonOutcome, AmendOutcome, ArcOutcome, AuditOutcome, BindOutcome, BindResult, DeliverOutcome, ReviewOutcome, } from "./library/contract-outcomes.js";
58
- export type { AbandonInput, AmendInput, ArcInput, BindInput, ContractHistory, ContractHistoryEvent, ContractList, ContractListInput, ContractObservationInput, DeliverInput, KeiyakuSelectInput, LocalContractComposition, ReviewInput, } from "./library/contract-types.js";
58
+ export type { AbandonInput, AmendInput, ArcInput, BindInput, ContractHistory, ContractHistoryEvent, ContractList, ContractListInput, ContractObservationInput, ContractResolveInput, ContractResolution, ContractSelectorRefusal, DeliverInput, KeiyakuSelectInput, LocalContractComposition, ReviewInput, } from "./library/contract-types.js";
59
59
  export { nukeKeiyaku as nuke } from "./library/nuke.js";
60
60
  export type { NukeInput, NukeResult } from "./library/nuke.js";
61
61
  export type { InvocationEffect, KeiyakuErrorCategory, PartialOutcomeEnvelope, PendingSurface, ReconciliationLag, Review, } from "./library/outcome.js";
@@ -63,7 +63,7 @@ export type { ReconcileCompletion, RepoContractReconcileReport, RepoReconcileRep
63
63
  export type { KeiyakuRefusal, KeiyakuRetryReason, NukeConfirmationRefusal, NukeConfirmationRequiredRefusal, OperationRetries, } from "./library/refusal.js";
64
64
  export type { RegionOverlap } from "./library/region.js";
65
65
  export type { RepoAtInput } from "./library/repo.js";
66
- export type { KeiyakuPlugin, PluginBodyEnd, PluginContext, PluginHooks, PluginInstance, PluginManifest, PluginOutcome, PluginSignal, PluginSignalMap, } from "./plugin/public.js";
66
+ export type { KeiyakuPlugin, PluginAkumaCaller, PluginAkumaOrigin, PluginBodyEnd, PluginContext, PluginHooks, PluginInstance, PluginManifest, PluginOutcome, PluginSignal, PluginSignalMap, } from "./plugin/public.js";
67
67
  export type { AuditReport } from "./protocol/audit.js";
68
68
  export type { IntegrationConflictMaterialized, VerificationReuse } from "./protocol/deliver.js";
69
69
  export type { ExecutionObservation, ExecutionObserver } from "./protocol/execution-observation.js";
@@ -45,8 +45,8 @@ export { actorId } from "./core/facts/types.js";
45
45
  * configuration helpers stay off this entry; only CLI and Body import them.
46
46
  */
47
47
  export { z } from "zod";
48
- export { ALLOWED_ACTIONS, Akuma, AkumaBusyError, AkumaDecodeError, AkumaNotBornError, AkumaProviderError, Schema, } from "./akuma/index.js";
49
- export { kanshi, observeKanshi, selectKanshi, selectRegion } from "./kanshi/index.js";
48
+ export { ALLOWED_ACTIONS, Akuma, AkumaArchetypeError, AkumaBusyError, AkumaDecodeError, AkumaNotBornError, AkumaProviderError, Schema, listArchetypeDefinitions, } from "./akuma/index.js";
49
+ export { kanshi, kanshiCategory, observeKanshi, selectKanshi, selectRegion } from "./kanshi/index.js";
50
50
  export { AkumaAddressError, AkumaWorldScopeError, Akumas } from "./library/akumas.js";
51
51
  export { nukeKeiyaku as nuke } from "./library/nuke.js";
52
52
  export { SettingsError, settings } from "./settings.js";
@@ -1,5 +1,5 @@
1
1
  export { kanshi, kanshiCategory, observeKanshi } from "./read.js";
2
2
  export { selectKanshi, selectRegion } from "./select.js";
3
- export type { KanshiInput } from "./read.js";
3
+ export type { KanshiCategoryInput, KanshiInput } from "./read.js";
4
4
  export type { AkumaKanshiRow, AkumaKanshiWorld, ContractEndpointObservation, ContractHolderObservation, ContractKanshiBoard, ContractKanshiRow, KanshiCategoryReport, KanshiReport, Section, TaskKanshiRow, TaskKanshiWorld, KanshiRegionSelection, RegionDeclaration, RegionOverlap, RegionRead, } from "./report.js";
5
5
  export type { KanshiSelection } from "./select.js";
@@ -89,6 +89,12 @@ function regionSelection(value) {
89
89
  }
90
90
  throw new TypeError(`kanshi region selection kind is invalid: ${selection.kind}`);
91
91
  }
92
+ function worldCoordinate(world, repo) {
93
+ if (world !== null && (typeof world !== "string" || world.trim().length === 0))
94
+ throw new TypeError("kanshi world must be a WorldRoot or null");
95
+ if (repo !== undefined && !(repo instanceof Repo))
96
+ throw new TypeError("kanshi repo must be a Repo");
97
+ }
92
98
  function coordinate(input) {
93
99
  if (typeof input !== "object" || input === null || Array.isArray(input))
94
100
  throw new TypeError("kanshi input must be an object");
@@ -97,11 +103,7 @@ function coordinate(input) {
97
103
  throw new TypeError(`kanshi input has unknown field: ${key}`);
98
104
  }
99
105
  }
100
- if (input.world !== null && (typeof input.world !== "string" || input.world.trim().length === 0)) {
101
- throw new TypeError("kanshi world must be a WorldRoot or null");
102
- }
103
- if (input.repo !== undefined && !(input.repo instanceof Repo))
104
- throw new TypeError("kanshi repo must be a Repo");
106
+ worldCoordinate(input.world, input.repo);
105
107
  return {
106
108
  ...input,
107
109
  ...(input.region === undefined ? {} : { region: regionSelection(input.region) }),
@@ -498,9 +500,22 @@ async function observeContractCategory(world, repo, limit, observedAt) {
498
500
  return report({ kind: "failed", failure: { message: diagnostic(error) } });
499
501
  }
500
502
  }
503
+ const DEFAULT_CATEGORY_LIMIT = 10;
504
+ /** Caller-invalid category requests are refused here, before any owner observation runs. */
505
+ function validateCategoryInput(input) {
506
+ const value = record(input, "category input");
507
+ exactKeys(value, ["world", "repo", "category", "limit"], "category input");
508
+ worldCoordinate(value.world, value.repo);
509
+ if (value.category !== "akuma" && value.category !== "contract")
510
+ throw new TypeError(`kanshi category is invalid: ${String(value.category)}`);
511
+ const { limit } = value;
512
+ if (limit !== undefined && (typeof limit !== "number" || !Number.isSafeInteger(limit) || limit < 1))
513
+ throw new TypeError("kanshi category limit must be a positive safe integer");
514
+ }
501
515
  export async function kanshiCategory(input) {
502
516
  const observedAt = new Date().toISOString();
503
- const { world, repo, category, limit = 10 } = input;
517
+ validateCategoryInput(input);
518
+ const { world, repo, category, limit = DEFAULT_CATEGORY_LIMIT } = input;
504
519
  if (category === "contract") {
505
520
  if (repo === undefined)
506
521
  throw new TypeError("kanshi contract category requires a Repo");
@@ -7,6 +7,7 @@ import { contractId } from "../core/facts/types.js";
7
7
  import { readDispatches } from "../dispatch/index.js";
8
8
  import { matchesAkumaGlob, parseReadableAkumaAlias, parseAkumaGlob, } from "../identity/selector.js";
9
9
  import { World } from "../world.js";
10
+ import { matchContractCandidates } from "./contract-address.js";
10
11
  import { requireInput } from "./input.js";
11
12
  import { scopeForRepo } from "./repo.js";
12
13
  export class AkumaAddressError extends Error {
@@ -69,10 +70,7 @@ export function resolveNamedAddress(input) {
69
70
  if (selector.startsWith("aku/"))
70
71
  return { kind: "akuma", id: parseAddressId(selector).id };
71
72
  const alias = parseAddressAlias(selector);
72
- const contractMatches = (input.report.contracts.kind === "present" ? input.report.contracts.value.rows : []).filter((row) => row.disposition === "active" &&
73
- row.workspace === "worktree" &&
74
- row.worktreePath !== null &&
75
- `@${row.id.slice("kei/".length)}` === alias);
73
+ const contractMatches = matchContractCandidates(input.report.contracts.kind === "present" ? input.report.contracts.value.rows : [], { selector: alias });
76
74
  const aliasId = input.aliases.kind === "present"
77
75
  ? (input.aliases.value.find((binding) => binding.alias === alias)?.akuId ?? null)
78
76
  : null;
@@ -1,3 +1,4 @@
1
+ import { type AkumaCaller } from "../akuma/caller.js";
1
2
  import { type ForkReceipt } from "../akuma/akuma-owner.js";
2
3
  import { type AskObserver } from "../akuma/akuma-selection-execution.js";
3
4
  import type { AkumaStatus } from "../akuma/akuma.js";
@@ -63,6 +64,7 @@ export type CallInput = Readonly<{
63
64
  allowed?: readonly AllowedAction[];
64
65
  schema?: Schema<unknown>;
65
66
  initiator?: string;
67
+ caller?: AkumaCaller;
66
68
  signal?: AbortSignal;
67
69
  observe?: CallWaitObserver;
68
70
  }>;
@@ -98,6 +100,8 @@ export type ForkInput = Readonly<{
98
100
  akuma: string;
99
101
  at: string;
100
102
  repo?: Repo;
103
+ caller?: AkumaCaller;
104
+ initiator?: string;
101
105
  }>;
102
106
  export type ForkResult = Readonly<{
103
107
  kind: "forked";
@@ -112,7 +116,7 @@ export type ForkResult = Readonly<{
112
116
  }>);
113
117
  export declare function callAkumas(input: CallInput & Readonly<{
114
118
  path: WorldRoot;
115
- }>, execution?: ExecutionContext): Promise<CallResult>;
119
+ }>, execution?: ExecutionContext, reportDiagnostic?: (message: string) => void): Promise<CallResult>;
116
120
  export declare function forkAkumas(input: ForkInput & Readonly<{
117
121
  path: WorldRoot;
118
122
  }>): Promise<ForkResult>;
@@ -1,11 +1,11 @@
1
1
  /** @architectureCompositionRoot */
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { appendFile } from "node:fs/promises";
4
+ import { decodeAkumaCaller } from "../akuma/caller.js";
4
5
  import { admitAkumaCall } from "../akuma/akuma-call.js";
5
6
  import { AkumaOwner } from "../akuma/akuma-owner.js";
6
7
  import { decodeAskObservation, observeAdmittedAskAkuma } from "../akuma/akuma-selection-execution.js";
7
8
  import { canonicalBirthCwd } from "../akuma/call-input.js";
8
- import { readSoul } from "../akuma/heart/index.js";
9
9
  import { pathsForAkuId } from "../akuma/identity.js";
10
10
  import { localExecutionContext } from "../akuma/requests.js";
11
11
  import { schemaJsonText } from "../akuma/schema.js";
@@ -13,7 +13,7 @@ import { moveAlias } from "../alias/index.js";
13
13
  import { AuthorityCorruptionError } from "../core/facts/errors.js";
14
14
  import { publishDispatch, readDispatch } from "../dispatch/index.js";
15
15
  import { parseAkumaAlias } from "../identity/selector.js";
16
- import { emitPluginSignal, outcomePluginObserver } from "../plugin/akuma-signals.js";
16
+ import { outcomePluginObserver } from "../plugin/akuma-signals.js";
17
17
  import { World } from "../world.js";
18
18
  import { addressAkuma } from "./address.js";
19
19
  import { requireInput } from "./input.js";
@@ -100,15 +100,11 @@ function callSeat(contract) {
100
100
  throw new TypeError("contract must be a Keiyaku");
101
101
  return seat;
102
102
  }
103
- async function requestedParent(path, id) {
104
- const soul = await readSoul(pathsForAkuId(path, id));
105
- return soul?.origin.kind === "request" ? soul.origin.parent : undefined;
106
- }
107
103
  function pluginDiagnostic(error) {
108
104
  const message = error instanceof Error ? error.message : String(error);
109
105
  return message.length <= 500 ? message : `${message.slice(0, 500)}...`;
110
106
  }
111
- async function recordPluginDiagnostic(paths, error, occurrence = "call admission") {
107
+ async function recordPluginDiagnostic(paths, error, occurrence) {
112
108
  try {
113
109
  await appendFile(paths.log, `plugin ${occurrence} failed: ${pluginDiagnostic(error)}\n`);
114
110
  }
@@ -116,30 +112,12 @@ async function recordPluginDiagnostic(paths, error, occurrence = "call admission
116
112
  // Plugin diagnostic loss must not change an already admitted call.
117
113
  }
118
114
  }
119
- /**
120
- * The submitting process's optional called-plugin observation. It runs once,
121
- * after the prepared executor returned its local value or the forwarded caller
122
- * received its exact live output, and it never gates birth, initial Tell, or a
123
- * later integration stage. A failed delivery stays a diagnostic.
124
- */
125
- async function emitCalledSignal(input) {
126
- try {
127
- const paths = pathsForAkuId(input.path, input.admitted.id);
128
- const callerAkumaId = input.admitted.requested ? await requestedParent(input.path, input.admitted.id) : undefined;
129
- await emitPluginSignal({
130
- world: input.path,
131
- ...(input.settings === undefined ? {} : { settings: input.settings }),
132
- reportDiagnostic: (message) => void recordPluginDiagnostic(paths, message),
133
- }, {
134
- kind: "akuma.called",
135
- akumaId: input.admitted.id,
136
- ...(callerAkumaId === undefined ? {} : { callerAkumaId }),
137
- ...(input.contractId === undefined ? {} : { contractId: input.contractId }),
138
- });
139
- }
140
- catch {
141
- // Plugin delivery must not change an already admitted call.
142
- }
115
+ function callDiagnosticSink(paths, occurrence, reportDiagnostic) {
116
+ return (message) => {
117
+ void recordPluginDiagnostic(paths, message, occurrence);
118
+ // Return the host's possible Promise for the plugin diagnostic isolation boundary.
119
+ return reportDiagnostic?.(message);
120
+ };
143
121
  }
144
122
  async function dispatchStage(input) {
145
123
  try {
@@ -204,6 +182,7 @@ const CALL_INPUT_KEYS = [
204
182
  "allowed",
205
183
  "schema",
206
184
  "initiator",
185
+ "caller",
207
186
  "signal",
208
187
  "observe",
209
188
  ];
@@ -214,6 +193,7 @@ async function parseCallInput(input) {
214
193
  const archetype = nonblank(values.archetype, "archetype");
215
194
  const body = callBody(values.body);
216
195
  const initiator = values.initiator === undefined ? undefined : text(values.initiator, "initiator");
196
+ const caller = decodeAkumaCaller(values.caller);
217
197
  const cwd = values.cwd === undefined ? undefined : nonblank(values.cwd, "cwd");
218
198
  const mode = callMode(values.mode);
219
199
  validateCallBodyOptions(body, values, mode);
@@ -247,6 +227,8 @@ async function parseCallInput(input) {
247
227
  ...(alias === undefined ? {} : { alias }),
248
228
  ...(values.allowed === undefined ? {} : { allowed: values.allowed }),
249
229
  seat,
230
+ ...(caller === undefined ? {} : { caller }),
231
+ ...(initiator === undefined ? {} : { initiator }),
250
232
  ...(initialTell === undefined ? {} : { initialTell }),
251
233
  ...(observe === undefined ? {} : { observe }),
252
234
  };
@@ -261,6 +243,8 @@ function callConfiguration(input, execution) {
261
243
  function callAdmissionInput(input, execution) {
262
244
  return {
263
245
  archetype: input.archetype,
246
+ ...(input.caller === undefined ? {} : { caller: input.caller }),
247
+ ...(input.initiator === undefined ? {} : { initiator: input.initiator }),
264
248
  ...(input.initialTell === undefined
265
249
  ? {}
266
250
  : {
@@ -297,7 +281,7 @@ function failedCall(result, tellId, error, tell) {
297
281
  },
298
282
  };
299
283
  }
300
- async function observeCall(input) {
284
+ async function observeCall(input, reportDiagnostic) {
301
285
  const onObserve = {
302
286
  admitted: async (receipt, id) => {
303
287
  await input.observe?.admitted?.(receipt, id, {
@@ -317,9 +301,7 @@ async function observeCall(input) {
317
301
  outcomeObserved: outcomePluginObserver({
318
302
  world: input.path,
319
303
  ...(input.settings === undefined ? {} : { settings: input.settings }),
320
- reportDiagnostic: (message) => {
321
- void recordPluginDiagnostic(pathsForAkuId(input.path, input.akuma), message, "outcome observation");
322
- },
304
+ reportDiagnostic: callDiagnosticSink(pathsForAkuId(input.path, input.akuma), "outcome observation", reportDiagnostic),
323
305
  }),
324
306
  ...(input.signal === undefined ? {} : { signal: input.signal }),
325
307
  onObserve,
@@ -352,7 +334,7 @@ function knownChildFailure(reason, akuma) {
352
334
  function asError(failure) {
353
335
  return failure instanceof Error ? failure : new Error(failure === undefined ? "call failed" : String(failure));
354
336
  }
355
- export async function callAkumas(input, execution = localExecutionContext()) {
337
+ export async function callAkumas(input, execution = localExecutionContext(), reportDiagnostic) {
356
338
  const parsed = await parseCallInput(input);
357
339
  const selectedExecution = await resolveCallExecution(parsed.cwd === undefined ? {} : { cwd: parsed.cwd });
358
340
  const seat = parsed.seat;
@@ -360,14 +342,6 @@ export async function callAkumas(input, execution = localExecutionContext()) {
360
342
  ...callAdmissionInput(parsed, selectedExecution),
361
343
  ...(seat === undefined ? {} : { contractId: seat.id }),
362
344
  }, selectedExecution === undefined ? {} : { cwdCanonical: true });
363
- // One caller-wiring sequence, after the prepared executor returned its local value
364
- // or the forwarded caller received its exact live output, before outcome observation.
365
- await emitCalledSignal({
366
- path: parsed.path,
367
- ...(parsed.settings === undefined ? {} : { settings: parsed.settings }),
368
- admitted,
369
- ...(seat === undefined ? {} : { contractId: seat.id }),
370
- });
371
345
  const dispatch = seat === undefined
372
346
  ? { kind: "none" }
373
347
  : await dispatchStage({ repository: seat.scope, akuId: admitted.id, contractId: seat.id });
@@ -404,16 +378,22 @@ export async function callAkumas(input, execution = localExecutionContext()) {
404
378
  ...(parsed.settings === undefined ? {} : { settings: parsed.settings }),
405
379
  ...(parsed.signal === undefined ? {} : { signal: parsed.signal }),
406
380
  ...(parsed.observe === undefined ? {} : { observe: parsed.observe }),
407
- });
381
+ }, reportDiagnostic);
408
382
  }
409
383
  export async function forkAkumas(input) {
410
384
  const values = requireInput(input, "Akumas.fork input");
411
- onlyKeys(values, ["path", "akuma", "at", "repo"], "Akumas.fork input");
385
+ onlyKeys(values, ["path", "akuma", "at", "repo", "caller", "initiator"], "Akumas.fork input");
386
+ const caller = decodeAkumaCaller(values.caller);
387
+ const initiator = values.initiator === undefined ? undefined : text(values.initiator, "initiator");
412
388
  const at = nonblank(values.at, "at");
413
389
  const addressed = await addressAkuma({ path: values.path, akuma: nonblank(values.akuma, "akuma") });
414
390
  const { path, id: akuma } = addressed;
415
391
  const repository = values.repo === undefined ? undefined : scopeForRepo(values.repo);
416
- const receipt = await new AkumaOwner(akuma, path).fork({ at });
392
+ const receipt = await new AkumaOwner(akuma, path).fork({
393
+ at,
394
+ ...(caller === undefined ? {} : { caller }),
395
+ ...(initiator === undefined ? {} : { initiator }),
396
+ });
417
397
  if (receipt.kind !== "forked")
418
398
  return { ...receipt, parent: akuma };
419
399
  const dispatch = repository === undefined
@@ -1,8 +1,9 @@
1
1
  /** @architectureCompositionRoot */
2
+ import { type AkumaHostOptions } from "../akuma/akuma-instance.js";
2
3
  import type { AkumaList, AkumaListInput } from "../akuma/akuma.js";
3
4
  import type { TellResult, TellWake } from "../akuma/body.js";
4
5
  import { type ExecutionContext } from "../akuma/requests.js";
5
- import type { DispatchAssociation } from "../dispatch/association.js";
6
+ import type { DispatchAssociation } from "../akuma/dispatch-association.js";
6
7
  import type { CreatedTaskObservation } from "../task/created-observation.js";
7
8
  import { type WorldRoot } from "../world.js";
8
9
  import type { AkumaAddressInput, AkumaWorldScopeRefusal } from "./address.js";
@@ -18,7 +19,7 @@ export type { WaitObservedAkuma, WaitObserver, WaitSelectedAkuma } from "../akum
18
19
  export type Akumas = AkumasHandle;
19
20
  declare class AkumasHandle {
20
21
  #private;
21
- constructor(world: WorldRoot, execution: ExecutionContext);
22
+ constructor(world: WorldRoot, execution: ExecutionContext, host?: AkumaHostOptions);
22
23
  list(input?: AkumaListInput): Promise<AkumaList>;
23
24
  call(input: CallInput): Promise<CallResult>;
24
25
  fork(input: ForkInput): Promise<ForkResult>;
@@ -34,7 +35,7 @@ declare class AkumasHandle {
34
35
  * The one Akumas composition. Public `of` fixes local execution; CLI and Body pass their captured
35
36
  * internal channel through `akumasWithExecution` instead of a second constructor.
36
37
  */
37
- export declare function akumasWithExecution(world: WorldRoot, execution: ExecutionContext): Akumas;
38
+ export declare function akumasWithExecution(world: WorldRoot, execution: ExecutionContext, host?: AkumaHostOptions): Akumas;
38
39
  export declare const Akumas: Readonly<{
39
- of(world: WorldRoot): Akumas;
40
+ of(world: WorldRoot, host?: AkumaHostOptions): Akumas;
40
41
  }>;
@@ -1,3 +1,5 @@
1
+ /** @architectureCompositionRoot */
2
+ import { captureAkumaHostDiagnostic } from "../akuma/akuma-instance.js";
1
3
  import { readAkumaRoster } from "../akuma/akuma.js";
2
4
  import { localExecutionContext } from "../akuma/requests.js";
3
5
  import { readAliases } from "../alias/index.js";
@@ -10,11 +12,13 @@ class AkumasHandle {
10
12
  #world;
11
13
  #execution;
12
14
  #selection;
13
- constructor(world, execution) {
15
+ #reportDiagnostic;
16
+ constructor(world, execution, host) {
14
17
  this.#world = world;
15
18
  this.#execution = execution;
16
19
  // One typed route seam captured at construction: no operation re-derives it.
17
- this.#selection = selectionSeam(execution);
20
+ this.#reportDiagnostic = captureAkumaHostDiagnostic(host);
21
+ this.#selection = selectionSeam(execution, this.#reportDiagnostic);
18
22
  Object.freeze(this);
19
23
  }
20
24
  async list(input) {
@@ -25,13 +29,13 @@ class AkumasHandle {
25
29
  return withRosterAliases(roster, await readAliases(path));
26
30
  }
27
31
  call(input) {
28
- return callAkumas(this.withWorld(input, "Akumas.call input"), this.#execution);
32
+ return callAkumas(this.withWorld(input, "Akumas.call input"), this.#execution, this.#reportDiagnostic);
29
33
  }
30
34
  fork(input) {
31
35
  return forkAkumas(this.withWorld(input, "Akumas.fork input"));
32
36
  }
33
37
  status(input) {
34
- return statusAkuma(this.withWorld(input, "Akumas.status input"));
38
+ return statusAkuma(this.withWorld(input, "Akumas.status input"), this.#reportDiagnostic);
35
39
  }
36
40
  tell(input) {
37
41
  return tellAkumaOn(this.#selection, this.withWorld(input, "Akumas.tell input"));
@@ -46,7 +50,7 @@ class AkumasHandle {
46
50
  return killAkumaOn(this.#selection, this.withWorld(input, "Akumas.kill input"));
47
51
  }
48
52
  history(input) {
49
- return historyAkuma(this.withWorld(input, "Akumas.history input"));
53
+ return historyAkuma(this.withWorld(input, "Akumas.history input"), this.#reportDiagnostic);
50
54
  }
51
55
  withWorld(input, label) {
52
56
  const values = requireInput(input, label);
@@ -72,13 +76,13 @@ function withRosterAliases(roster, bindings) {
72
76
  * The one Akumas composition. Public `of` fixes local execution; CLI and Body pass their captured
73
77
  * internal channel through `akumasWithExecution` instead of a second constructor.
74
78
  */
75
- export function akumasWithExecution(world, execution) {
79
+ export function akumasWithExecution(world, execution, host) {
76
80
  if (typeof world !== "string")
77
81
  throw new TypeError("Akumas.of world must be a WorldRoot");
78
- return new AkumasHandle(world, execution);
82
+ return new AkumasHandle(world, execution, host);
79
83
  }
80
84
  export const Akumas = Object.freeze({
81
- of(world) {
82
- return akumasWithExecution(world, localExecutionContext());
85
+ of(world, host) {
86
+ return akumasWithExecution(world, localExecutionContext(), host);
83
87
  },
84
88
  });
@@ -21,7 +21,7 @@ export declare function admitForkBindWithAppointment(input: Readonly<{
21
21
  scope: RepositoryScope;
22
22
  channel: GitDecodeChannel;
23
23
  sourceId: ContractId;
24
- target?: string;
24
+ target?: string | null;
25
25
  progress?: ProtocolProgress;
26
26
  actor?: ActorId;
27
27
  }>): Promise<Readonly<{
@@ -35,7 +35,7 @@ export declare function prepareMarkdownBind(input: Readonly<{
35
35
  gates: readonly import("../core/facts/types.js").Gate[];
36
36
  after: readonly ContractId[];
37
37
  workspace: "worktree";
38
- targetSelection?: BindTargetSelection;
38
+ targetSelection: BindTargetSelection;
39
39
  task?: TaskId;
40
40
  progress?: ProtocolProgress;
41
41
  actor?: ActorId;
@@ -45,16 +45,16 @@ export declare function prepareMarkdownBind(input: Readonly<{
45
45
  result: IntentOutcome<Readonly<{
46
46
  contractId: ContractId;
47
47
  }>, {
48
- kind: "contract-exists" | "invalid-after" | "unknown-prerequisite";
49
- contractId: ContractId;
50
- } | {
51
48
  kind: "verification-declaration-invalid";
52
49
  contractId?: ContractId | undefined;
50
+ } | {
51
+ kind: "target-missing" | "unborn-head" | "invalid-target";
53
52
  } | {
54
53
  kind: "fork-source-moved";
55
54
  contractId: ContractId;
56
55
  } | {
57
- kind: "unborn-head" | "target-missing" | "invalid-target";
56
+ kind: "contract-exists" | "invalid-after" | "unknown-prerequisite";
57
+ contractId: ContractId;
58
58
  } | {
59
59
  kind: "fork-source-missing" | "fork-source-unavailable";
60
60
  contractId: ContractId;
@@ -64,16 +64,16 @@ export declare function prepareMarkdownBind(input: Readonly<{
64
64
  result: IntentOutcome<Readonly<{
65
65
  contractId: ContractId;
66
66
  }>, {
67
- kind: "contract-exists" | "invalid-after" | "unknown-prerequisite";
68
- contractId: ContractId;
69
- } | {
70
67
  kind: "verification-declaration-invalid";
71
68
  contractId?: ContractId | undefined;
69
+ } | {
70
+ kind: "target-missing" | "unborn-head" | "invalid-target";
72
71
  } | {
73
72
  kind: "fork-source-moved";
74
73
  contractId: ContractId;
75
74
  } | {
76
- kind: "unborn-head" | "target-missing" | "invalid-target";
75
+ kind: "contract-exists" | "invalid-after" | "unknown-prerequisite";
76
+ contractId: ContractId;
77
77
  } | {
78
78
  kind: "fork-source-missing" | "fork-source-unavailable";
79
79
  contractId: ContractId;
@@ -84,16 +84,16 @@ export declare function prepareMarkdownBind(input: Readonly<{
84
84
  result: IntentOutcome<Readonly<{
85
85
  contractId: ContractId;
86
86
  }>, {
87
- kind: "contract-exists" | "invalid-after" | "unknown-prerequisite";
88
- contractId: ContractId;
89
- } | {
90
87
  kind: "verification-declaration-invalid";
91
88
  contractId?: ContractId | undefined;
89
+ } | {
90
+ kind: "target-missing" | "unborn-head" | "invalid-target";
92
91
  } | {
93
92
  kind: "fork-source-moved";
94
93
  contractId: ContractId;
95
94
  } | {
96
- kind: "unborn-head" | "target-missing" | "invalid-target";
95
+ kind: "contract-exists" | "invalid-after" | "unknown-prerequisite";
96
+ contractId: ContractId;
97
97
  } | {
98
98
  kind: "fork-source-missing" | "fork-source-unavailable";
99
99
  contractId: ContractId;
@@ -12,6 +12,14 @@ import { KeiyakuError } from "./outcome.js";
12
12
  import { contractTerms, documentDerivation } from "./input.js";
13
13
  const CONTRACT_ID_CODE_POINTS = 32;
14
14
  const CONTRACT_ID_ATTEMPTS = 4;
15
+ /** A fork with no target copies its source; `null` clears it and a string overrides it. */
16
+ function forkTargetSelection(target, sourceTarget) {
17
+ if (target === null)
18
+ return { kind: "targetless" };
19
+ if (target !== undefined)
20
+ return { kind: "explicit", target };
21
+ return sourceTarget === undefined ? { kind: "targetless" } : { kind: "explicit", target: sourceTarget };
22
+ }
15
23
  function drawContractSuffix() {
16
24
  return randomBytes(2).toString("hex");
17
25
  }
@@ -22,10 +30,10 @@ async function attempt(input, id) {
22
30
  terms: input.terms,
23
31
  verification: input.verification,
24
32
  workspace: input.workspace,
33
+ targetSelection: input.targetSelection,
25
34
  ...(input.coordinates === undefined ? {} : { coordinates: input.coordinates }),
26
35
  ...(input.source === undefined ? {} : { source: input.source }),
27
36
  contractId: id,
28
- ...(input.targetSelection === undefined ? {} : { targetSelection: input.targetSelection }),
29
37
  ...(input.task === undefined
30
38
  ? {}
31
39
  : {
@@ -99,11 +107,7 @@ export async function admitForkBindWithAppointment(input) {
99
107
  terms,
100
108
  verification: documentDerivation(document, terms.gates).verification,
101
109
  workspace: "worktree",
102
- targetSelection: input.target === undefined
103
- ? source.coordinates.target === undefined
104
- ? { kind: "targetless" }
105
- : { kind: "explicit", target: source.coordinates.target }
106
- : { kind: "explicit", target: input.target },
110
+ targetSelection: forkTargetSelection(input.target, source.coordinates.target),
107
111
  coordinates: { start: source.coordinates.start },
108
112
  source: {
109
113
  contractId: input.sourceId,
@@ -125,7 +129,7 @@ export async function prepareMarkdownBind(input) {
125
129
  terms,
126
130
  verification: documentDerivation(input.document, terms.gates).verification,
127
131
  workspace: input.workspace,
128
- ...(input.targetSelection === undefined ? {} : { targetSelection: input.targetSelection }),
132
+ targetSelection: input.targetSelection,
129
133
  ...(input.task === undefined ? {} : { task: input.task }),
130
134
  ...(input.actor === undefined ? {} : { actor: input.actor }),
131
135
  ...(input.progress === undefined ? {} : { progress: input.progress }),