@astrosheep/keiyaku 2.9.2 → 2.9.4

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 (238) hide show
  1. package/build/.tsbuildinfo +1 -1
  2. package/build/agents/call-terms.js +13 -94
  3. package/build/agents/harness/activity-values.js +16 -0
  4. package/build/agents/harness/event-persistence.js +218 -0
  5. package/build/agents/harness/execution-handle.js +25 -222
  6. package/build/agents/harness/index.js +3 -1
  7. package/build/agents/harness/outcome.js +47 -4
  8. package/build/agents/harness/projection.js +42 -59
  9. package/build/agents/harness/provider-adapter.js +58 -0
  10. package/build/agents/harness/router.js +11 -187
  11. package/build/agents/harness/runtime.js +17 -4
  12. package/build/agents/harness/session.js +15 -0
  13. package/build/agents/launch-snapshot/model.js +15 -236
  14. package/build/agents/launch-snapshot/parse-helpers.js +114 -0
  15. package/build/agents/launch-snapshot/resolve.js +17 -43
  16. package/build/agents/opencode-sdk.js +45 -12
  17. package/build/agents/providers/claude-agent-sdk/adapter.js +6 -17
  18. package/build/agents/providers/claude-agent-sdk/registration.js +214 -0
  19. package/build/agents/providers/claude-agent-sdk/session-schema.js +2 -0
  20. package/build/agents/providers/codex-app-server/adapter.js +50 -53
  21. package/build/agents/providers/codex-app-server/events.js +67 -11
  22. package/build/agents/providers/codex-app-server/identity.js +1 -0
  23. package/build/agents/providers/codex-app-server/policy.js +29 -0
  24. package/build/agents/providers/codex-app-server/registration.js +248 -0
  25. package/build/agents/providers/codex-app-server/session-schema.js +6 -0
  26. package/build/agents/providers/codex-app-server/session.js +23 -17
  27. package/build/agents/providers/codex-app-server/terms.js +1 -0
  28. package/build/agents/providers/opencode-sdk/adapter.js +94 -50
  29. package/build/agents/providers/opencode-sdk/events.js +8 -4
  30. package/build/agents/providers/opencode-sdk/registration.js +135 -0
  31. package/build/agents/providers/opencode-sdk/session-schema.js +2 -0
  32. package/build/agents/providers/opencode-sdk/session.js +21 -14
  33. package/build/agents/providers/opencode-sdk/terms.js +1 -0
  34. package/build/agents/providers/pi/adapter.js +36 -30
  35. package/build/agents/providers/pi/registration.js +145 -0
  36. package/build/agents/providers/pi/session-schema.js +6 -0
  37. package/build/agents/providers/pi/terms.js +1 -0
  38. package/build/agents/providers/registration.js +7 -0
  39. package/build/agents/providers/registry.js +76 -0
  40. package/build/agents/selector.js +2 -29
  41. package/build/agents/types.js +8 -0
  42. package/build/artifact-directories.js +22 -0
  43. package/build/cli/commands/akuma/akuma/handler.js +1 -1
  44. package/build/cli/commands/akuma/view/handler.js +5 -4
  45. package/build/cli/commands/akuma.js +6 -0
  46. package/build/cli/commands/contract/amend/handler.js +8 -2
  47. package/build/cli/commands/contract/arc/handler.js +7 -1
  48. package/build/cli/commands/contract/bind/handler.js +4 -3
  49. package/build/cli/commands/contract/bind/meta.js +34 -7
  50. package/build/cli/commands/contract/forfeit/handler.js +4 -4
  51. package/build/cli/commands/contract/log/handler.js +3 -3
  52. package/build/cli/commands/contract/petition/handler.js +4 -6
  53. package/build/cli/commands/contract/renew/handler.js +20 -6
  54. package/build/cli/commands/contract/renew/meta.js +5 -4
  55. package/build/cli/commands/metadata.js +58 -0
  56. package/build/cli/commands/projection/call/handler.js +6 -5
  57. package/build/cli/commands/projection/kill/handler.js +16 -32
  58. package/build/cli/commands/projection/shared.js +28 -0
  59. package/build/cli/commands/projection/status/handler.js +1 -1
  60. package/build/cli/commands/projection/tell/handler.js +14 -30
  61. package/build/cli/commands/projection/wait/handler.js +45 -22
  62. package/build/cli/commands/projection/wait/meta.js +7 -2
  63. package/build/cli/commands/registry.js +2 -54
  64. package/build/cli/commands/shared.js +10 -7
  65. package/build/cli/commands/skills/install/handler.js +2 -5
  66. package/build/cli/commands/skills/skills/handler.js +1 -1
  67. package/build/cli/commands/task/add/handler.js +5 -13
  68. package/build/cli/commands/task/catalog.js +2 -2
  69. package/build/cli/commands/task/done/handler.js +4 -8
  70. package/build/cli/commands/task/drop/handler.js +3 -6
  71. package/build/cli/commands/task/ls/handler.js +3 -5
  72. package/build/cli/commands/task/shared.js +14 -4
  73. package/build/cli/commands/task/{view → show}/handler.js +7 -8
  74. package/build/cli/commands/task/show/meta.js +8 -0
  75. package/build/cli/commands/task/start/handler.js +3 -6
  76. package/build/cli/commands/task/stop/handler.js +4 -8
  77. package/build/cli/commands/task/task/handler.js +1 -1
  78. package/build/cli/commands/task/update/handler.js +4 -8
  79. package/build/cli/completion.js +1 -1
  80. package/build/cli/flags.js +19 -3
  81. package/build/cli/help.js +4 -11
  82. package/build/cli/index.js +57 -83
  83. package/build/cli/missing-subcommand.js +7 -0
  84. package/build/cli/parse-flags.js +14 -5
  85. package/build/cli/parse-metadata.js +3 -3
  86. package/build/cli/parse-selectors.js +1 -1
  87. package/build/cli/parse.js +21 -0
  88. package/build/cli/projection-address.js +7 -12
  89. package/build/cli/render/address.js +41 -37
  90. package/build/cli/render/arc.js +24 -12
  91. package/build/cli/render/call.js +11 -5
  92. package/build/cli/render/errors.js +36 -1
  93. package/build/cli/render/format.js +4 -0
  94. package/build/cli/render/kanshi.js +6 -12
  95. package/build/cli/render/kill.js +9 -0
  96. package/build/cli/render/misc.js +55 -12
  97. package/build/cli/render/petition.js +20 -15
  98. package/build/cli/render/projection-activity.js +3 -0
  99. package/build/cli/render/shared.js +60 -21
  100. package/build/cli/render/skills.js +10 -0
  101. package/build/cli/render/status.js +37 -14
  102. package/build/cli/render/success-response.js +77 -0
  103. package/build/cli/render/task.js +54 -14
  104. package/build/cli/render/tell.js +5 -0
  105. package/build/cli/render/terminal-failure.js +58 -0
  106. package/build/cli/render/types.js +1 -0
  107. package/build/cli/render/wait.js +38 -36
  108. package/build/cli/types.js +1 -1
  109. package/build/config/akuma-loader.js +0 -5
  110. package/build/config/env-keys.js +1 -1
  111. package/build/config/provider-policy-ownership.js +3 -32
  112. package/build/config/settings/disease.js +8 -1
  113. package/build/config/settings/loader.js +11 -3
  114. package/build/config/settings/provider-instance-fields.js +51 -0
  115. package/build/config/settings/schema.js +6 -176
  116. package/build/core/addressing.js +66 -16
  117. package/build/core/amend.js +135 -18
  118. package/build/core/arc.js +19 -67
  119. package/build/core/bind.js +82 -35
  120. package/build/core/call/call.js +15 -9
  121. package/build/core/call/context.js +6 -51
  122. package/build/core/call/execution.js +21 -22
  123. package/build/core/call/prompt.js +14 -1
  124. package/build/core/command-markdown.js +45 -0
  125. package/build/core/context.js +13 -7
  126. package/build/core/contract-view.js +20 -3
  127. package/build/core/draft.js +123 -16
  128. package/build/core/forfeit.js +5 -2
  129. package/build/core/hints.js +1 -75
  130. package/build/core/identifier-slug.js +66 -0
  131. package/build/core/ids.js +25 -11
  132. package/build/core/ledger.js +20 -68
  133. package/build/core/markdown/parser.js +0 -68
  134. package/build/core/markdown/render.js +68 -0
  135. package/build/core/markdown/sections.js +2 -1
  136. package/build/core/outcome-base.js +4 -0
  137. package/build/core/projection/generation/database.js +2 -38
  138. package/build/core/projection/generation/model.js +42 -1
  139. package/build/core/{projection-generation-continuation.js → projection/generation/projection-generation-continuation.js} +3 -3
  140. package/build/core/{projection-generation-execution.js → projection/generation/projection-generation-execution.js} +19 -49
  141. package/build/core/{projection-generation-identity.js → projection/generation/projection-generation-identity.js} +2 -2
  142. package/build/core/{projection-generation-launcher.js → projection/generation/projection-generation-launcher.js} +5 -3
  143. package/build/core/{projection-generation-process.js → projection/generation/projection-generation-process.js} +2 -1
  144. package/build/core/{projection-generation-runner.js → projection/generation/projection-generation-runner.js} +18 -3
  145. package/build/core/projection/generation/store.js +2 -2
  146. package/build/core/projection/generation/transitions.js +1 -1
  147. package/build/core/projection/index.js +27 -0
  148. package/build/core/{projection-activity.js → projection/projection-activity.js} +12 -0
  149. package/build/core/{projection-alias.js → projection/projection-alias.js} +14 -19
  150. package/build/core/projection/projection-coordinate.js +61 -0
  151. package/build/core/{projection-core.js → projection/projection-core.js} +9 -9
  152. package/build/core/{projection-execution-observer.js → projection/projection-execution-observer.js} +2 -57
  153. package/build/core/{projection-identity.js → projection/projection-identity.js} +1 -1
  154. package/build/core/{projection-kill.js → projection/projection-kill.js} +1 -1
  155. package/build/core/{projection-life-observer.js → projection/projection-life-observer.js} +1 -1
  156. package/build/core/{projection-mint.js → projection/projection-mint.js} +10 -10
  157. package/build/core/{projection-status.js → projection/projection-status.js} +98 -25
  158. package/build/core/projection/projection-terminal-failure.js +104 -0
  159. package/build/core/projection/projection-wait.js +278 -0
  160. package/build/core/projection/projection-wake.js +203 -0
  161. package/build/core/provider-loader-ports.js +4 -0
  162. package/build/core/ref-log.js +65 -40
  163. package/build/core/registry.js +63 -10
  164. package/build/core/renew-session.js +128 -0
  165. package/build/core/renew.js +426 -99
  166. package/build/core/scope.js +143 -66
  167. package/build/core/seal.js +139 -35
  168. package/build/core/{claim-delivery.js → settlement/claim-delivery.js} +9 -14
  169. package/build/core/{claim.js → settlement/claim.js} +32 -8
  170. package/build/core/settlement/index.js +19 -0
  171. package/build/core/{petition-claim-gates.js → settlement/petition-claim-gates.js} +22 -18
  172. package/build/core/{petition-forfeit.js → settlement/petition-forfeit.js} +2 -2
  173. package/build/core/{petition-head-guard.js → settlement/petition-head-guard.js} +2 -2
  174. package/build/core/settlement/petition-io.js +1 -0
  175. package/build/core/{petition-preview.js → settlement/petition-preview.js} +1 -1
  176. package/build/core/{petition.js → settlement/petition.js} +12 -10
  177. package/build/core/settlement/queue-read-model.js +143 -0
  178. package/build/core/settlement/queue-seat-allocation.js +156 -0
  179. package/build/core/settlement/queue.js +6 -0
  180. package/build/core/settlement/settlement.js +295 -0
  181. package/build/core/{verdict.js → settlement/verdict.js} +9 -37
  182. package/build/core/status/board.js +24 -23
  183. package/build/core/status/ledger-batch.js +158 -0
  184. package/build/core/status/reconciliation.js +10 -4
  185. package/build/core/stored-agent-event.js +3 -0
  186. package/build/core/task/board.js +40 -4
  187. package/build/core/task/commands.js +30 -21
  188. package/build/core/task/index.js +12 -0
  189. package/build/core/task/settlement-git.js +157 -65
  190. package/build/core/task/settlement-policy.js +54 -24
  191. package/build/core/task/task-contract.js +370 -0
  192. package/build/core/{task-doctor.js → task/task-doctor.js} +1 -1
  193. package/build/core/{task-git-actor.js → task/task-git-actor.js} +5 -10
  194. package/build/core/{task-git-runtime.js → task/task-git-runtime.js} +3 -3
  195. package/build/core/{task-git-store.js → task/task-git-store.js} +92 -29
  196. package/build/core/task/task-repository.js +1 -0
  197. package/build/core/{task-store-repository.js → task/task-store-repository.js} +5 -4
  198. package/build/core/task/task-store.js +1 -0
  199. package/build/core/{task-worktree.js → task/task-worktree.js} +2 -1
  200. package/build/core/task/task.js +5 -0
  201. package/build/core/task/tree.js +37 -0
  202. package/build/core/task/validation.js +5 -2
  203. package/build/core/transcripts.js +15 -3
  204. package/build/core/worktree-path.js +43 -28
  205. package/build/flow-error.js +85 -39
  206. package/build/fs/durability.js +27 -0
  207. package/build/fs/path-coordinate.js +16 -0
  208. package/build/generated/version.js +3 -2
  209. package/build/git/branches.js +2 -1
  210. package/build/git/core.js +106 -2
  211. package/build/git/path-coordinate.js +17 -0
  212. package/build/git/refs.js +57 -0
  213. package/build/git/streaming-batch.js +301 -0
  214. package/build/git/worktree.js +65 -0
  215. package/build/index.js +3 -3
  216. package/build/keiyaku.js +2 -36
  217. package/package.json +4 -10
  218. package/skills/keiyaku/SKILL.md +3 -2
  219. package/skills/keiyaku-akuma/SKILL.md +10 -6
  220. package/skills/keiyaku-task/SKILL.md +1 -1
  221. package/skills/keiyaku-workflow/SKILL.md +25 -4
  222. package/build/agents/providers/codex-sdk.js +0 -329
  223. package/build/cli/commands/task/view/meta.js +0 -8
  224. package/build/core/command-io.js +0 -174
  225. package/build/core/path-coordinate.js +0 -27
  226. package/build/core/petition-claim.js +0 -117
  227. package/build/core/petition-run.js +0 -83
  228. package/build/core/projection-coordinate.js +0 -63
  229. package/build/core/projection-wait.js +0 -165
  230. package/build/core/projection-wake.js +0 -136
  231. package/build/core/queue.js +0 -343
  232. package/build/core/task-contract.js +0 -237
  233. package/build/core/task.js +0 -4
  234. /package/build/{core/task-repository.js → agents/launch-snapshot/types.js} +0 -0
  235. /package/build/{core/task-store.js → agents/providers/claude-agent-sdk/terms.js} +0 -0
  236. /package/build/core/{projection-generation-runtime.js → projection/generation/projection-generation-runtime.js} +0 -0
  237. /package/build/core/{projection-life-protocol.js → projection/projection-life-protocol.js} +0 -0
  238. /package/build/core/{projection-runner-lock.js → projection/projection-runner-lock.js} +0 -0
@@ -1,185 +1,15 @@
1
1
  import { z } from "zod";
2
- import { CLAUDE_SETTING_SOURCES, } from "../../keiyaku.js";
3
- const tomlValueSchema = z.lazy(() => z.union([
4
- z.string(),
5
- z.number(),
6
- z.boolean(),
7
- z.array(tomlValueSchema),
8
- z.record(z.string(), tomlValueSchema),
9
- ]));
10
- const structuredTomlValueSchema = z.record(z.string(), tomlValueSchema);
11
- const executableSchema = z.string().superRefine((value, ctx) => {
12
- if (value.length === 0 || /\s/.test(value) || value.includes("\"") || value.includes("'")) {
13
- ctx.addIssue({
14
- code: z.ZodIssueCode.custom,
15
- message: "must be a single program name token with no spaces or quotes.",
16
- });
17
- }
18
- });
19
- const nonblankOptionalString = z
20
- .string()
21
- .refine((value) => value.trim().length > 0, { message: "must not be blank." })
22
- .optional();
23
- const providerCommonFields = {
24
- description: nonblankOptionalString,
25
- };
26
- const providerEnvSchema = z.record(z.string(), z.string()).optional();
27
- function refinePiEnvInjection(data, ctx) {
28
- if (data.env === undefined)
29
- return;
30
- if (Object.keys(data.env).length === 0)
31
- return;
32
- ctx.addIssue({
33
- code: z.ZodIssueCode.custom,
34
- path: ["env"],
35
- message: "env injection not supported for provider pi",
36
- });
37
- }
38
- const CODEX_SDK_FORBIDDEN_THREAD_OPTIONS_KEYS = [
39
- "model",
40
- "modelReasoningEffort",
41
- "sandboxMode",
42
- "networkAccessEnabled",
43
- "webSearchMode",
44
- "approvalPolicy",
45
- "workingDirectory",
46
- "skipGitRepoCheck",
47
- ];
48
- const FORBIDDEN_CONFIG_KEYS = [
49
- "model",
50
- "model_reasoning_effort",
51
- ];
52
- function isConfigKeyForbidden(key) {
53
- return FORBIDDEN_CONFIG_KEYS.includes(key);
54
- }
55
- const claudeSettingSourceSchema = z.enum(CLAUDE_SETTING_SOURCES);
56
- const writableRootItemSchema = z.string().superRefine((value, ctx) => {
57
- if (value.length === 0) {
58
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not be empty." });
59
- return;
60
- }
61
- if (value.startsWith("~")) {
62
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not begin with tilde." });
63
- return;
64
- }
65
- if (value.includes("\0")) {
66
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not contain NUL byte." });
67
- return;
68
- }
69
- if (/\$[A-Za-z_]/.test(value) || /\$\{/.test(value)) {
70
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must not contain environment variable references." });
71
- return;
72
- }
73
- });
74
- export const providerInstanceSchema = z.discriminatedUnion("kind", [
75
- z
76
- .object({
77
- kind: z.literal("codex-sdk"),
78
- ...providerCommonFields,
79
- executable: executableSchema.optional(),
80
- profile: z.string().optional(),
81
- config: structuredTomlValueSchema.optional(),
82
- threadOptions: structuredTomlValueSchema.optional(),
83
- })
84
- .strict()
85
- .superRefine((data, ctx) => {
86
- if (data.threadOptions) {
87
- for (const key of CODEX_SDK_FORBIDDEN_THREAD_OPTIONS_KEYS) {
88
- if (key in data.threadOptions) {
89
- ctx.addIssue({
90
- code: z.ZodIssueCode.custom,
91
- path: ["threadOptions", key],
92
- message: `"${key}" is owned by the Akuma or Keiyaku execution, not this provider instance`,
93
- });
94
- }
95
- }
96
- }
97
- if (data.config) {
98
- for (const key of Object.keys(data.config)) {
99
- if (isConfigKeyForbidden(key)) {
100
- ctx.addIssue({
101
- code: z.ZodIssueCode.custom,
102
- path: ["config", key],
103
- message: `"${key}" is owned by the Akuma or Keiyaku execution, not this provider instance`,
104
- });
105
- }
106
- }
107
- }
108
- }),
109
- z
110
- .object({
111
- kind: z.literal("codex-app-server"),
112
- ...providerCommonFields,
113
- executable: executableSchema.optional(),
114
- profile: z.string().optional(),
115
- config: structuredTomlValueSchema.optional(),
116
- writableRoots: z.array(writableRootItemSchema).optional(),
117
- env: providerEnvSchema,
118
- })
119
- .strict()
120
- .superRefine((data, ctx) => {
121
- if (data.config) {
122
- for (const key of Object.keys(data.config)) {
123
- if (isConfigKeyForbidden(key)) {
124
- ctx.addIssue({
125
- code: z.ZodIssueCode.custom,
126
- path: ["config", key],
127
- message: `"${key}" is owned by the Akuma or Keiyaku execution, not this provider instance`,
128
- });
129
- }
130
- }
131
- }
132
- }),
133
- z
134
- .object({
135
- kind: z.literal("claude-agent-sdk"),
136
- ...providerCommonFields,
137
- executable: executableSchema.optional(),
138
- settingSources: z.array(claudeSettingSourceSchema).optional(),
139
- env: providerEnvSchema,
140
- })
141
- .strict(),
142
- z
143
- .object({
144
- kind: z.literal("opencode-sdk"),
145
- ...providerCommonFields,
146
- executable: executableSchema.optional(),
147
- env: providerEnvSchema,
148
- })
149
- .strict(),
150
- z
151
- .object({
152
- kind: z.literal("pi"),
153
- ...providerCommonFields,
154
- env: providerEnvSchema,
155
- })
156
- .strict()
157
- .superRefine(refinePiEnvInjection),
158
- ]);
2
+ import { BUILTIN_PROVIDER_INSTANCE_CANDIDATES as REGISTRY_BUILTIN_PROVIDER_INSTANCE_CANDIDATES, providerInstanceSchema as registryProviderInstanceSchema, } from "../../agents/providers/registry.js";
3
+ export const providerInstanceSchema = registryProviderInstanceSchema;
4
+ export const BUILTIN_PROVIDER_INSTANCE_CANDIDATES = REGISTRY_BUILTIN_PROVIDER_INSTANCE_CANDIDATES;
159
5
  const PROVIDER_INSTANCE_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
160
6
  export const providerInstanceNameSchema = z
161
7
  .string()
162
8
  .max(64, "must contain at most 64 characters.")
163
9
  .regex(PROVIDER_INSTANCE_NAME_PATTERN, "must match ^[a-z0-9][a-z0-9-]*$.");
164
- export const BUILTIN_PROVIDER_INSTANCE_CANDIDATES = {
165
- "codex-sdk": { name: "codex-sdk", source: "builtin", instance: { kind: "codex-sdk", executable: "codex" } },
166
- "codex-app-server": {
167
- name: "codex-app-server",
168
- source: "builtin",
169
- instance: { kind: "codex-app-server", executable: "codex" },
170
- },
171
- "claude-agent-sdk": {
172
- name: "claude-agent-sdk",
173
- source: "builtin",
174
- instance: { kind: "claude-agent-sdk", executable: "claude", settingSources: ["project"] },
175
- },
176
- "opencode-sdk": {
177
- name: "opencode-sdk",
178
- source: "builtin",
179
- instance: { kind: "opencode-sdk", executable: "opencode" },
180
- },
181
- pi: { name: "pi", source: "builtin", instance: { kind: "pi" } },
182
- };
10
+ // Ensure the registry builtin map stays keyed by the closed provider vocabulary.
11
+ const _builtinCheck = BUILTIN_PROVIDER_INSTANCE_CANDIDATES;
12
+ void _builtinCheck;
183
13
  const AGENT_NAME_PATTERN = /^[a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*$/;
184
14
  export const agentNameSchema = z
185
15
  .string()
@@ -1,9 +1,13 @@
1
1
  import * as path from "node:path";
2
2
  import { FlowError } from "../flow-error.js";
3
+ import { errorContainsAnyPattern, NOT_GIT_REPOSITORY_PATTERNS } from "../git/core.js";
4
+ import { commissionSlug, formatCommissionCoordinate } from "./ids.js";
3
5
  import { listContractIds, readLedger } from "./ledger.js";
4
6
  import { isResponseArtifactId } from "./response-artifact-id.js";
5
- import { stableRepoRoot } from "./worktree-path.js";
7
+ import { activeContractBoundByEngineWorktree, stableRepoRoot } from "./worktree-path.js";
8
+ import { assertNoRenewRecoverySession } from "./renew-session.js";
6
9
  import { deriveContractState, isTerminalState, } from "./status/lifecycle.js";
10
+ const AMBIGUOUS_OBJECTIVE_MAX_CHARS = 60;
7
11
  function normalizeAddressQuery(raw) {
8
12
  const value = raw.startsWith("@") ? raw.slice(1) : raw;
9
13
  if (!value.trim()) {
@@ -17,8 +21,8 @@ class AddressedFlowError extends FlowError {
17
21
  constructor(error, carrier) {
18
22
  super(error.code, error.message, {
19
23
  cause: error,
20
- hints: error.hints,
21
24
  review: error.review,
25
+ facts: error.facts,
22
26
  });
23
27
  this.name = error.name;
24
28
  this[ADDRESS_CARRIER] = carrier;
@@ -108,9 +112,8 @@ export class AddressResolutionError extends FlowError {
108
112
  this.attempt = attempt;
109
113
  }
110
114
  }
111
- function contractSlug(contractId) {
112
- const separator = contractId.indexOf(".");
113
- return separator > 0 ? contractId.slice(0, separator) : contractId;
115
+ function candidatePlace(candidate) {
116
+ return candidate.bind.data.workspace === "worktree" ? candidate.bind.data.place ?? null : null;
114
117
  }
115
118
  async function commissionAddressCandidates(cwd) {
116
119
  const ids = await listContractIds(cwd);
@@ -135,9 +138,9 @@ function matchCommissionAddress(query, candidates) {
135
138
  const matches = [];
136
139
  if (candidate.contractId === query)
137
140
  matches.push({ ...candidate, spelling: "id" });
138
- if (contractSlug(candidate.contractId) === query)
141
+ if (commissionSlug(candidate.contractId) === query)
139
142
  matches.push({ ...candidate, spelling: "slug" });
140
- if (candidate.active && candidate.bind.data.place === query) {
143
+ if (candidate.active && candidatePlace(candidate) === query) {
141
144
  matches.push({ ...candidate, spelling: "place" });
142
145
  }
143
146
  return matches;
@@ -146,16 +149,28 @@ function matchCommissionAddress(query, candidates) {
146
149
  function attemptCandidates(candidates) {
147
150
  return candidates.map((candidate) => ({
148
151
  commissionId: candidate.contractId,
149
- place: candidate.bind.data.place ?? null,
152
+ place: candidatePlace(candidate),
150
153
  name: candidate.bind.data.name,
151
154
  objective: candidate.bind.data.objective,
152
155
  lifecycle: candidate.lifecycle,
153
156
  target: { branch: candidate.bind.data.target },
154
157
  }));
155
158
  }
156
- function formatAmbiguousCandidate(query, candidate) {
157
- const label = candidate.matchedBy ?? "candidate";
158
- return `${label} @${query}: ${candidate.name} · ${candidate.objective} · commission ${candidate.commissionId}`;
159
+ function truncateObjectiveFirstLine(objective) {
160
+ const firstLine = objective.split(/\r?\n/, 1)[0]?.trim() ?? "";
161
+ if (firstLine.length <= AMBIGUOUS_OBJECTIVE_MAX_CHARS)
162
+ return firstLine;
163
+ return `${firstLine.slice(0, AMBIGUOUS_OBJECTIVE_MAX_CHARS)}…`;
164
+ }
165
+ function formatAmbiguousCandidate(candidate) {
166
+ const coordinate = formatCommissionCoordinate({
167
+ commissionId: candidate.commissionId,
168
+ place: candidate.place,
169
+ });
170
+ const objective = truncateObjectiveFirstLine(candidate.objective);
171
+ return [coordinate, objective || undefined, candidate.commissionId]
172
+ .filter((part) => part !== undefined)
173
+ .join(" · ");
159
174
  }
160
175
  async function resolvedRepository(cwd) {
161
176
  return { kind: "repository", stableRoot: await stableRepoRoot(cwd) };
@@ -166,7 +181,7 @@ function buildResolvedAddress(input) {
166
181
  workdir: input.workdir,
167
182
  binding: { kind: "commission", commissionId: input.candidate.contractId },
168
183
  spelling: { asTyped: input.asTyped, source: input.source, matchedBy: input.matchedBy },
169
- place: { display: input.candidate.bind.data.place ?? null },
184
+ place: { display: candidatePlace(input.candidate) },
170
185
  lifecycle: input.candidate.lifecycle,
171
186
  target: { branch: input.candidate.bind.data.target },
172
187
  inference: input.inference,
@@ -188,8 +203,8 @@ export function buildUncommissionedRepositoryAddress(input) {
188
203
  inference: input.inference,
189
204
  });
190
205
  }
191
- export async function resolveContractAddress(cwd, raw, source = raw?.startsWith("@") ? "at-address" : "explicit-flag") {
192
- const workdir = path.resolve(cwd);
206
+ export async function resolveContractAddress(cwd, raw, source = raw?.startsWith("@") ? "at-address" : "explicit-flag", effectiveDirectory = cwd) {
207
+ const workdir = path.resolve(effectiveDirectory);
193
208
  const repo = await resolvedRepository(cwd);
194
209
  if (raw !== undefined) {
195
210
  const query = normalizeAddressQuery(raw);
@@ -216,7 +231,7 @@ export async function resolveContractAddress(cwd, raw, source = raw?.startsWith(
216
231
  candidates: matches.map((candidate) => ({
217
232
  commissionId: candidate.contractId,
218
233
  matchedBy: candidate.spelling,
219
- place: candidate.bind.data.place ?? null,
234
+ place: candidatePlace(candidate),
220
235
  name: candidate.bind.data.name,
221
236
  objective: candidate.bind.data.objective,
222
237
  lifecycle: candidate.lifecycle,
@@ -238,9 +253,34 @@ export async function resolveContractAddress(cwd, raw, source = raw?.startsWith(
238
253
  if (matches.length === 0) {
239
254
  throw new AddressResolutionError("EMPTY_PARAM", `unknown contract address @${query}`, attempt);
240
255
  }
241
- throw new AddressResolutionError("CONTRACT_AMBIGUOUS", `contract address @${query} is ambiguous: ${attempt.candidates.map((candidate) => formatAmbiguousCandidate(query, candidate)).join("; ")}; use a full @commission-id`, attempt);
256
+ throw new AddressResolutionError("CONTRACT_AMBIGUOUS", `@${query} is ambiguous:\n${attempt.candidates.map((candidate) => ` ${formatAmbiguousCandidate(candidate)}`).join("\n")}\nuse a full @commission-id`, attempt);
242
257
  }
243
258
  const candidates = await commissionAddressCandidates(repo.stableRoot);
259
+ let boundContractId;
260
+ try {
261
+ const effectiveRepo = await stableRepoRoot(workdir);
262
+ if (effectiveRepo === repo.stableRoot) {
263
+ boundContractId = await activeContractBoundByEngineWorktree(workdir);
264
+ }
265
+ }
266
+ catch (error) {
267
+ if (!errorContainsAnyPattern(error, NOT_GIT_REPOSITORY_PATTERNS))
268
+ throw error;
269
+ }
270
+ if (boundContractId) {
271
+ const candidate = candidates.find((item) => item.active && item.contractId === boundContractId);
272
+ if (candidate) {
273
+ return buildResolvedAddress({
274
+ repo,
275
+ workdir,
276
+ candidate,
277
+ matchedBy: "id",
278
+ asTyped: `@${candidate.contractId}`,
279
+ source: "worktree-binding",
280
+ inference: ["verified active engine worktree binding"],
281
+ });
282
+ }
283
+ }
244
284
  const operable = candidates.filter((candidate) => candidate.active);
245
285
  const attempt = {
246
286
  repo,
@@ -265,3 +305,13 @@ export async function resolveContractAddress(cwd, raw, source = raw?.startsWith(
265
305
  ? "no active commission found; pass @place, @slug, full @commission-id, or --contract <addr>"
266
306
  : "multiple active commissions found; pass @place, @slug, full @commission-id, or --contract <addr>", attempt);
267
307
  }
308
+ export async function resolveExistingContractAddress(cwd, raw, source = raw?.startsWith("@") ? "at-address" : "explicit-flag", effectiveDirectory = cwd) {
309
+ const address = await resolveContractAddress(cwd, raw, source, effectiveDirectory);
310
+ try {
311
+ await assertNoRenewRecoverySession(address.repo.stableRoot, address.binding.commissionId);
312
+ }
313
+ catch (error) {
314
+ throw attachAddressCarrier(error, address);
315
+ }
316
+ return address;
317
+ }
@@ -1,15 +1,137 @@
1
- import * as fs from "node:fs/promises";
2
- import * as path from "node:path";
3
1
  import { FlowError } from "../flow-error.js";
4
2
  import { getCurrentBranch, requireGitRepository } from "../git/branches.js";
5
- import { KEIYAKU_FILE } from "../keiyaku.js";
6
- import { activeLedgerRef, appendWithRetry, readLedger } from "./ledger.js";
7
- import { renderContractView } from "./contract-view.js";
3
+ import { activeLedgerRef, appendWithRetry } from "./ledger.js";
4
+ import { refreshContractView } from "./contract-view.js";
8
5
  import { deriveContractState, isTerminalState } from "./status/lifecycle.js";
9
6
  import { resolveContractAddress, withResolvedAddress } from "./addressing.js";
10
7
  import { resolveDefaultBranch } from "./status/drift.js";
11
- import { assertCleanWorkingTree } from "./arc.js";
12
- import { normalizeScopePattern } from "./scope.js";
8
+ import { isBlankScopePatternLine, normalizeScopePattern, ScopePatternError } from "./scope.js";
9
+ import { normalizeSectionTitle } from "./markdown/titles.js";
10
+ import { resolveContractDeliveryCwd } from "./worktree-path.js";
11
+ function amendScopeHeading(name) {
12
+ return `## Scope ${name[0].toUpperCase()}${name.slice(1)}`;
13
+ }
14
+ function sourceLines(markdown) {
15
+ const lines = [];
16
+ let start = 0;
17
+ while (start < markdown.length) {
18
+ const newline = markdown.indexOf("\n", start);
19
+ const end = newline === -1 ? markdown.length : newline;
20
+ const next = newline === -1 ? markdown.length : newline + 1;
21
+ const raw = markdown.slice(start, end);
22
+ lines.push({ text: raw.endsWith("\r") ? raw.slice(0, -1) : raw, start, end, next });
23
+ start = next;
24
+ }
25
+ return lines;
26
+ }
27
+ function amendHeading(line) {
28
+ const match = line.match(/^ {0,3}(#{1,2})(?!#)[ \t]+(.+)$/);
29
+ if (!match)
30
+ return null;
31
+ const level = match[1].length;
32
+ const title = normalizeSectionTitle(match[2]);
33
+ if (level !== 2)
34
+ return { level };
35
+ if (title === "scope add")
36
+ return { level, name: "add" };
37
+ return { level };
38
+ }
39
+ function amendFence(line) {
40
+ const match = line.match(/^ {0,3}(`{3,}|~{3,})/);
41
+ if (!match)
42
+ return null;
43
+ const marker = match[1];
44
+ return { delimiter: marker[0], length: marker.length };
45
+ }
46
+ function parseAmendScopeSections(markdown) {
47
+ const lines = sourceLines(markdown);
48
+ const sections = {};
49
+ let active;
50
+ let fence;
51
+ let frontMatterEnd = -1;
52
+ if (lines[0]?.text.trim() === "---") {
53
+ for (let index = 1; index < lines.length; index += 1) {
54
+ if (lines[index].text.trim() === "---") {
55
+ frontMatterEnd = index;
56
+ break;
57
+ }
58
+ }
59
+ }
60
+ const finishActive = (end) => {
61
+ if (!active)
62
+ return;
63
+ sections[active.name] = { ...active, end };
64
+ active = undefined;
65
+ };
66
+ for (let index = 0; index < lines.length; index += 1) {
67
+ const line = lines[index];
68
+ if (index <= frontMatterEnd)
69
+ continue;
70
+ const marker = amendFence(line.text);
71
+ if (fence) {
72
+ if (marker && marker.delimiter === fence.delimiter && marker.length >= fence.length) {
73
+ fence = undefined;
74
+ }
75
+ continue;
76
+ }
77
+ if (marker) {
78
+ fence = marker;
79
+ continue;
80
+ }
81
+ const heading = amendHeading(line.text);
82
+ if (!heading)
83
+ continue;
84
+ finishActive(line.start);
85
+ if (!heading.name)
86
+ continue;
87
+ if (sections[heading.name]) {
88
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", `amend input may contain ${amendScopeHeading(heading.name)} at most once`);
89
+ }
90
+ active = { name: heading.name, start: line.start, contentStart: line.next };
91
+ }
92
+ finishActive(markdown.length);
93
+ let prose = markdown;
94
+ for (const section of Object.values(sections).sort((left, right) => right.start - left.start)) {
95
+ prose = `${prose.slice(0, section.start)}${prose.slice(section.end)}`;
96
+ }
97
+ return { sections, prose };
98
+ }
99
+ function scopePatterns(markdown, section) {
100
+ const patterns = markdown
101
+ .slice(section.contentStart, section.end)
102
+ .split(/\r?\n/)
103
+ .filter((line) => !isBlankScopePatternLine(line))
104
+ .map((pattern) => {
105
+ try {
106
+ // Do not trim() here: gitignore treats trailing `\ ` as a significant space.
107
+ return normalizeScopePattern(pattern);
108
+ }
109
+ catch (error) {
110
+ if (error instanceof ScopePatternError) {
111
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", error.message);
112
+ }
113
+ throw error;
114
+ }
115
+ });
116
+ if (patterns.length === 0) {
117
+ throw new FlowError("INVALID_KEIYAKU_DRAFT", `amend input has an empty ${amendScopeHeading(section.name)} section`);
118
+ }
119
+ return patterns;
120
+ }
121
+ export function buildAmendInput(markdown, cwd, contractId, contractAddressSource) {
122
+ const { sections, prose } = parseAmendScopeSections(markdown);
123
+ const add = sections.add ? scopePatterns(markdown, sections.add) : [];
124
+ if (!prose.trim()) {
125
+ throw new FlowError("EMPTY_PARAM", "amendment cannot be empty");
126
+ }
127
+ return {
128
+ cwd,
129
+ ...(contractId ? { contractId } : {}),
130
+ ...(contractAddressSource ? { contractAddressSource } : {}),
131
+ amendment: prose,
132
+ ...(sections.add ? { scopeDelta: { add } } : {}),
133
+ };
134
+ }
13
135
  function requireAmendment(value) {
14
136
  if (!value.trim())
15
137
  throw new FlowError("EMPTY_PARAM", "amendment cannot be empty");
@@ -24,13 +146,6 @@ function assertContractOperable(contractId, entries) {
24
146
  throw new FlowError("INTERNAL_STATE", `contract ${contractId} is terminal (${state.state})`);
25
147
  }
26
148
  }
27
- async function renderUpdatedContract(cwd, contractId) {
28
- const ledger = await readLedger(cwd, contractId);
29
- if (!ledger) {
30
- throw new FlowError("INTERNAL_STATE", `contract ${contractId} active ledger disappeared before render`);
31
- }
32
- await fs.writeFile(path.join(cwd, KEIYAKU_FILE), renderContractView({ id: contractId, entries: ledger.entries }), "utf8");
33
- }
34
149
  function normalizeScopeDelta(scopeDelta) {
35
150
  if (!scopeDelta)
36
151
  return undefined;
@@ -43,6 +158,7 @@ function normalizeScopeDelta(scopeDelta) {
43
158
  export async function amendContract(input) {
44
159
  const amendment = requireAmendment(input.amendment);
45
160
  const scopeDelta = normalizeScopeDelta(input.scopeDelta);
161
+ const renderCwd = input.renderCwd ?? input.cwd;
46
162
  const at = new Date(input.nowMs).toISOString();
47
163
  const ref = activeLedgerRef(input.contractId);
48
164
  let amendmentNumber = 0;
@@ -67,20 +183,21 @@ export async function amendContract(input) {
67
183
  if (amendmentNumber === 0) {
68
184
  throw new FlowError("INTERNAL_STATE", `contract ${input.contractId} active ledger disappeared during amend`);
69
185
  }
70
- await renderUpdatedContract(input.cwd, input.contractId);
186
+ await refreshContractView({ ledgerCwd: input.cwd, renderCwd, contractId: input.contractId });
71
187
  return { contractId: input.contractId, amendmentNumber, ledgerHead: result.head };
72
188
  }
73
189
  export async function amendKeiyaku(input) {
74
190
  await requireGitRepository(input.cwd, "amend");
75
- const address = await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
191
+ const address = input.address ?? await resolveContractAddress(input.cwd, input.contractId, input.contractAddressSource);
76
192
  return await withResolvedAddress(address, async () => {
77
193
  const contractId = address.binding.commissionId;
78
- await assertCleanWorkingTree(input.cwd, []);
194
+ const renderCwd = await resolveContractDeliveryCwd(input.cwd, contractId);
79
195
  const amendment = requireAmendment(input.amendment);
80
- const currentBranch = await getCurrentBranch(input.cwd);
196
+ const currentBranch = await getCurrentBranch(renderCwd);
81
197
  const baseBranch = (await resolveDefaultBranch(input.cwd))?.branch ?? currentBranch;
82
198
  const amended = await amendContract({
83
199
  cwd: input.cwd,
200
+ renderCwd,
84
201
  contractId,
85
202
  actor: "keiyaku",
86
203
  nowMs: Date.now(),