@calliopelabs/cli 3.5.4 → 3.7.0

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 (311) hide show
  1. package/README.md +8 -0
  2. package/dist/acp/controls.d.ts +10 -0
  3. package/dist/acp/controls.d.ts.map +1 -0
  4. package/dist/acp/controls.js +81 -0
  5. package/dist/acp/controls.js.map +1 -0
  6. package/dist/acp.d.ts +21 -7
  7. package/dist/acp.d.ts.map +1 -1
  8. package/dist/acp.js +139 -23
  9. package/dist/acp.js.map +1 -1
  10. package/dist/bin.d.ts.map +1 -1
  11. package/dist/bin.js +28 -8
  12. package/dist/bin.js.map +1 -1
  13. package/dist/build-identity.d.ts +27 -0
  14. package/dist/build-identity.d.ts.map +1 -0
  15. package/dist/build-identity.js +32 -0
  16. package/dist/build-identity.js.map +1 -0
  17. package/dist/build-metadata.json +1 -0
  18. package/dist/execution/authority.d.ts +4 -1
  19. package/dist/execution/authority.d.ts.map +1 -1
  20. package/dist/execution/authority.js +23 -7
  21. package/dist/execution/authority.js.map +1 -1
  22. package/dist/execution/child-grants.d.ts +2 -0
  23. package/dist/execution/child-grants.d.ts.map +1 -1
  24. package/dist/execution/child-grants.js +19 -4
  25. package/dist/execution/child-grants.js.map +1 -1
  26. package/dist/execution/delegated.d.ts +14 -0
  27. package/dist/execution/delegated.d.ts.map +1 -0
  28. package/dist/execution/delegated.js +66 -0
  29. package/dist/execution/delegated.js.map +1 -0
  30. package/dist/execution/files.js +6 -6
  31. package/dist/execution/files.js.map +1 -1
  32. package/dist/execution/guard.d.ts +5 -0
  33. package/dist/execution/guard.d.ts.map +1 -1
  34. package/dist/execution/guard.js +31 -16
  35. package/dist/execution/guard.js.map +1 -1
  36. package/dist/execution/index.d.ts +4 -0
  37. package/dist/execution/index.d.ts.map +1 -1
  38. package/dist/execution/index.js +4 -0
  39. package/dist/execution/index.js.map +1 -1
  40. package/dist/execution/ledger-cli.d.ts +2 -1
  41. package/dist/execution/ledger-cli.d.ts.map +1 -1
  42. package/dist/execution/ledger-cli.js +51 -14
  43. package/dist/execution/ledger-cli.js.map +1 -1
  44. package/dist/execution/ledger-protocol.d.ts +6 -2
  45. package/dist/execution/ledger-protocol.d.ts.map +1 -1
  46. package/dist/execution/ledger-protocol.js +110 -24
  47. package/dist/execution/ledger-protocol.js.map +1 -1
  48. package/dist/execution/ledger-server.d.ts +13 -0
  49. package/dist/execution/ledger-server.d.ts.map +1 -1
  50. package/dist/execution/ledger-server.js +57 -13
  51. package/dist/execution/ledger-server.js.map +1 -1
  52. package/dist/execution/ledger-tunnel.d.ts +50 -0
  53. package/dist/execution/ledger-tunnel.d.ts.map +1 -0
  54. package/dist/execution/ledger-tunnel.js +307 -0
  55. package/dist/execution/ledger-tunnel.js.map +1 -0
  56. package/dist/execution/ledger.d.ts +4 -0
  57. package/dist/execution/ledger.d.ts.map +1 -1
  58. package/dist/execution/ledger.js +30 -4
  59. package/dist/execution/ledger.js.map +1 -1
  60. package/dist/execution/parent-accounting.d.ts +41 -0
  61. package/dist/execution/parent-accounting.d.ts.map +1 -0
  62. package/dist/execution/parent-accounting.js +52 -0
  63. package/dist/execution/parent-accounting.js.map +1 -0
  64. package/dist/execution/parent-budget.d.ts +25 -0
  65. package/dist/execution/parent-budget.d.ts.map +1 -0
  66. package/dist/execution/parent-budget.js +102 -0
  67. package/dist/execution/parent-budget.js.map +1 -0
  68. package/dist/execution/project-spend.d.ts.map +1 -1
  69. package/dist/execution/project-spend.js +35 -5
  70. package/dist/execution/project-spend.js.map +1 -1
  71. package/dist/execution/remote-checkpoint.d.ts +15 -0
  72. package/dist/execution/remote-checkpoint.d.ts.map +1 -0
  73. package/dist/execution/remote-checkpoint.js +165 -0
  74. package/dist/execution/remote-checkpoint.js.map +1 -0
  75. package/dist/execution/remote-ledger.d.ts +12 -2
  76. package/dist/execution/remote-ledger.d.ts.map +1 -1
  77. package/dist/execution/remote-ledger.js +53 -21
  78. package/dist/execution/remote-ledger.js.map +1 -1
  79. package/dist/execution/types.d.ts +23 -5
  80. package/dist/execution/types.d.ts.map +1 -1
  81. package/dist/execution/types.js.map +1 -1
  82. package/dist/goals/supervised.js +2 -2
  83. package/dist/goals/supervised.js.map +1 -1
  84. package/dist/headless.d.ts +2 -6
  85. package/dist/headless.d.ts.map +1 -1
  86. package/dist/headless.js +17 -21
  87. package/dist/headless.js.map +1 -1
  88. package/dist/improvement/actions.d.ts +3 -4
  89. package/dist/improvement/actions.d.ts.map +1 -1
  90. package/dist/improvement/actions.js +10 -7
  91. package/dist/improvement/actions.js.map +1 -1
  92. package/dist/improvement/capabilities.d.ts +54 -0
  93. package/dist/improvement/capabilities.d.ts.map +1 -0
  94. package/dist/improvement/capabilities.js +44 -0
  95. package/dist/improvement/capabilities.js.map +1 -0
  96. package/dist/improvement/cli.d.ts +1 -1
  97. package/dist/improvement/cli.d.ts.map +1 -1
  98. package/dist/improvement/cli.js +29 -3
  99. package/dist/improvement/cli.js.map +1 -1
  100. package/dist/improvement/host-capabilities.d.ts +119 -0
  101. package/dist/improvement/host-capabilities.d.ts.map +1 -0
  102. package/dist/improvement/host-capabilities.js +18 -0
  103. package/dist/improvement/host-capabilities.js.map +1 -0
  104. package/dist/improvement/index.d.ts +6 -0
  105. package/dist/improvement/index.d.ts.map +1 -1
  106. package/dist/improvement/index.js +6 -0
  107. package/dist/improvement/index.js.map +1 -1
  108. package/dist/improvement/proposal-hash.d.ts +3 -0
  109. package/dist/improvement/proposal-hash.d.ts.map +1 -0
  110. package/dist/improvement/proposal-hash.js +5 -0
  111. package/dist/improvement/proposal-hash.js.map +1 -0
  112. package/dist/improvement/review-contract.d.ts +32 -0
  113. package/dist/improvement/review-contract.d.ts.map +1 -0
  114. package/dist/improvement/review-contract.js +62 -0
  115. package/dist/improvement/review-contract.js.map +1 -0
  116. package/dist/improvement/review-requests.d.ts +72 -0
  117. package/dist/improvement/review-requests.d.ts.map +1 -0
  118. package/dist/improvement/review-requests.js +63 -0
  119. package/dist/improvement/review-requests.js.map +1 -0
  120. package/dist/improvement/source-changes.d.ts +78 -0
  121. package/dist/improvement/source-changes.d.ts.map +1 -0
  122. package/dist/improvement/source-changes.js +91 -0
  123. package/dist/improvement/source-changes.js.map +1 -0
  124. package/dist/improvement/task-outcomes.d.ts +82 -0
  125. package/dist/improvement/task-outcomes.d.ts.map +1 -0
  126. package/dist/improvement/task-outcomes.js +72 -0
  127. package/dist/improvement/task-outcomes.js.map +1 -0
  128. package/dist/index.d.ts +10 -0
  129. package/dist/index.d.ts.map +1 -1
  130. package/dist/index.js +6 -0
  131. package/dist/index.js.map +1 -1
  132. package/dist/isolation/contracts.d.ts +13 -0
  133. package/dist/isolation/contracts.d.ts.map +1 -1
  134. package/dist/isolation/contracts.js +9 -1
  135. package/dist/isolation/contracts.js.map +1 -1
  136. package/dist/isolation/coordinator.d.ts.map +1 -1
  137. package/dist/isolation/coordinator.js +4 -0
  138. package/dist/isolation/coordinator.js.map +1 -1
  139. package/dist/isolation/process.d.ts.map +1 -1
  140. package/dist/isolation/process.js +3 -1
  141. package/dist/isolation/process.js.map +1 -1
  142. package/dist/isolation/worktree.d.ts +9 -1
  143. package/dist/isolation/worktree.d.ts.map +1 -1
  144. package/dist/isolation/worktree.js +8 -3
  145. package/dist/isolation/worktree.js.map +1 -1
  146. package/dist/memory.d.ts +5 -3
  147. package/dist/memory.d.ts.map +1 -1
  148. package/dist/memory.js +8 -6
  149. package/dist/memory.js.map +1 -1
  150. package/dist/migration/assessment-contract.d.ts +60 -0
  151. package/dist/migration/assessment-contract.d.ts.map +1 -0
  152. package/dist/migration/assessment-contract.js +132 -0
  153. package/dist/migration/assessment-contract.js.map +1 -0
  154. package/dist/migration/assessment-schema.d.ts +19 -0
  155. package/dist/migration/assessment-schema.d.ts.map +1 -0
  156. package/dist/migration/assessment-schema.js +43 -0
  157. package/dist/migration/assessment-schema.js.map +1 -0
  158. package/dist/migration/assessment.d.ts +159 -0
  159. package/dist/migration/assessment.d.ts.map +1 -0
  160. package/dist/migration/assessment.js +219 -0
  161. package/dist/migration/assessment.js.map +1 -0
  162. package/dist/migration/cli.d.ts +6 -0
  163. package/dist/migration/cli.d.ts.map +1 -0
  164. package/dist/migration/cli.js +101 -0
  165. package/dist/migration/cli.js.map +1 -0
  166. package/dist/migration/evidence.d.ts +27 -0
  167. package/dist/migration/evidence.d.ts.map +1 -0
  168. package/dist/migration/evidence.js +124 -0
  169. package/dist/migration/evidence.js.map +1 -0
  170. package/dist/migration/example.d.ts +33 -0
  171. package/dist/migration/example.d.ts.map +1 -0
  172. package/dist/migration/example.js +28 -0
  173. package/dist/migration/example.js.map +1 -0
  174. package/dist/migration/index.d.ts +21 -0
  175. package/dist/migration/index.d.ts.map +1 -0
  176. package/dist/migration/index.js +15 -0
  177. package/dist/migration/index.js.map +1 -0
  178. package/dist/migration/inventory.d.ts +70 -0
  179. package/dist/migration/inventory.d.ts.map +1 -0
  180. package/dist/migration/inventory.js +233 -0
  181. package/dist/migration/inventory.js.map +1 -0
  182. package/dist/migration/schema.d.ts +61 -0
  183. package/dist/migration/schema.d.ts.map +1 -0
  184. package/dist/migration/schema.js +76 -0
  185. package/dist/migration/schema.js.map +1 -0
  186. package/dist/migration/verification.d.ts +146 -0
  187. package/dist/migration/verification.d.ts.map +1 -0
  188. package/dist/migration/verification.js +260 -0
  189. package/dist/migration/verification.js.map +1 -0
  190. package/dist/orchestration/actions.d.ts +2 -0
  191. package/dist/orchestration/actions.d.ts.map +1 -1
  192. package/dist/orchestration/actions.js.map +1 -1
  193. package/dist/orchestration/artifact-host.d.ts +46 -0
  194. package/dist/orchestration/artifact-host.d.ts.map +1 -0
  195. package/dist/orchestration/artifact-host.js +64 -0
  196. package/dist/orchestration/artifact-host.js.map +1 -0
  197. package/dist/orchestration/coordinator-actions.d.ts +1 -1
  198. package/dist/orchestration/coordinator-actions.d.ts.map +1 -1
  199. package/dist/orchestration/coordinator-actions.js +5 -3
  200. package/dist/orchestration/coordinator-actions.js.map +1 -1
  201. package/dist/orchestration/coordinator-types.d.ts +23 -2
  202. package/dist/orchestration/coordinator-types.d.ts.map +1 -1
  203. package/dist/orchestration/coordinator.d.ts +5 -0
  204. package/dist/orchestration/coordinator.d.ts.map +1 -1
  205. package/dist/orchestration/coordinator.js +30 -13
  206. package/dist/orchestration/coordinator.js.map +1 -1
  207. package/dist/orchestration/execution-cli.d.ts +1 -1
  208. package/dist/orchestration/execution-cli.d.ts.map +1 -1
  209. package/dist/orchestration/execution-cli.js +29 -6
  210. package/dist/orchestration/execution-cli.js.map +1 -1
  211. package/dist/orchestration/execution-journal.d.ts.map +1 -1
  212. package/dist/orchestration/execution-journal.js +79 -6
  213. package/dist/orchestration/execution-journal.js.map +1 -1
  214. package/dist/orchestration/execution-store.d.ts +5 -1
  215. package/dist/orchestration/execution-store.d.ts.map +1 -1
  216. package/dist/orchestration/execution-store.js +66 -5
  217. package/dist/orchestration/execution-store.js.map +1 -1
  218. package/dist/orchestration/execution.d.ts.map +1 -1
  219. package/dist/orchestration/execution.js +37 -10
  220. package/dist/orchestration/execution.js.map +1 -1
  221. package/dist/orchestration/index.d.ts +6 -0
  222. package/dist/orchestration/index.d.ts.map +1 -1
  223. package/dist/orchestration/index.js +6 -0
  224. package/dist/orchestration/index.js.map +1 -1
  225. package/dist/orchestration/interruption.d.ts +10 -0
  226. package/dist/orchestration/interruption.d.ts.map +1 -0
  227. package/dist/orchestration/interruption.js +38 -0
  228. package/dist/orchestration/interruption.js.map +1 -0
  229. package/dist/orchestration/question-contract.d.ts +19 -0
  230. package/dist/orchestration/question-contract.d.ts.map +1 -0
  231. package/dist/orchestration/question-contract.js +21 -0
  232. package/dist/orchestration/question-contract.js.map +1 -0
  233. package/dist/orchestration/questions.d.ts +9 -0
  234. package/dist/orchestration/questions.d.ts.map +1 -0
  235. package/dist/orchestration/questions.js +75 -0
  236. package/dist/orchestration/questions.js.map +1 -0
  237. package/dist/orchestration/reviewed-ledger.d.ts +23 -0
  238. package/dist/orchestration/reviewed-ledger.d.ts.map +1 -0
  239. package/dist/orchestration/reviewed-ledger.js +57 -0
  240. package/dist/orchestration/reviewed-ledger.js.map +1 -0
  241. package/dist/orchestration/routing.d.ts +3 -0
  242. package/dist/orchestration/routing.d.ts.map +1 -1
  243. package/dist/orchestration/routing.js +5 -1
  244. package/dist/orchestration/routing.js.map +1 -1
  245. package/dist/orchestration/source-candidates.d.ts +64 -0
  246. package/dist/orchestration/source-candidates.d.ts.map +1 -0
  247. package/dist/orchestration/source-candidates.js +95 -0
  248. package/dist/orchestration/source-candidates.js.map +1 -0
  249. package/dist/orchestration/validation.js +1 -1
  250. package/dist/orchestration/validation.js.map +1 -1
  251. package/dist/runlog.js +2 -2
  252. package/dist/runlog.js.map +1 -1
  253. package/dist/runtime/permissions.d.ts.map +1 -1
  254. package/dist/runtime/permissions.js +3 -2
  255. package/dist/runtime/permissions.js.map +1 -1
  256. package/dist/runtime/turn.d.ts +2 -0
  257. package/dist/runtime/turn.d.ts.map +1 -1
  258. package/dist/runtime/turn.js +21 -7
  259. package/dist/runtime/turn.js.map +1 -1
  260. package/dist/session-management/actions.js +1 -1
  261. package/dist/session-management/actions.js.map +1 -1
  262. package/dist/sessions/execution-binding.d.ts +8 -0
  263. package/dist/sessions/execution-binding.d.ts.map +1 -0
  264. package/dist/sessions/execution-binding.js +9 -0
  265. package/dist/sessions/execution-binding.js.map +1 -0
  266. package/dist/sessions/index.d.ts +1 -0
  267. package/dist/sessions/index.d.ts.map +1 -1
  268. package/dist/sessions/index.js +1 -0
  269. package/dist/sessions/index.js.map +1 -1
  270. package/dist/source-changes/writer.d.ts +77 -0
  271. package/dist/source-changes/writer.d.ts.map +1 -0
  272. package/dist/source-changes/writer.js +189 -0
  273. package/dist/source-changes/writer.js.map +1 -0
  274. package/dist/spawning/actions.d.ts +10 -1
  275. package/dist/spawning/actions.d.ts.map +1 -1
  276. package/dist/spawning/actions.js +26 -7
  277. package/dist/spawning/actions.js.map +1 -1
  278. package/dist/spawning/cli.js +1 -1
  279. package/dist/spawning/cli.js.map +1 -1
  280. package/dist/spawning/execution.d.ts.map +1 -1
  281. package/dist/spawning/execution.js +2 -0
  282. package/dist/spawning/execution.js.map +1 -1
  283. package/dist/spawning/types.d.ts +19 -6
  284. package/dist/spawning/types.d.ts.map +1 -1
  285. package/dist/spawning/validation.d.ts +16 -0
  286. package/dist/spawning/validation.d.ts.map +1 -1
  287. package/dist/spawning/validation.js +44 -8
  288. package/dist/spawning/validation.js.map +1 -1
  289. package/dist/storage.d.ts +5 -0
  290. package/dist/storage.d.ts.map +1 -1
  291. package/dist/storage.js +10 -1
  292. package/dist/storage.js.map +1 -1
  293. package/dist/supervision/contracts.js +4 -1
  294. package/dist/supervision/contracts.js.map +1 -1
  295. package/dist/tools.d.ts +1 -0
  296. package/dist/tools.d.ts.map +1 -1
  297. package/dist/tools.js +5 -0
  298. package/dist/tools.js.map +1 -1
  299. package/docs/README.md +66 -0
  300. package/docs/acp.md +228 -0
  301. package/docs/delegation-ledger.md +331 -0
  302. package/docs/improvement-cycles.md +304 -0
  303. package/docs/migration-assessment.md +113 -0
  304. package/docs/migration-example-plan.json +88 -0
  305. package/docs/migration-example-report.json +511 -0
  306. package/docs/migration-inventory.md +63 -0
  307. package/docs/migration-playbook.md +152 -0
  308. package/docs/migration-verification.md +177 -0
  309. package/docs/releases/3.6.0.md +43 -0
  310. package/docs/releases/3.7.0.md +53 -0
  311. package/package.json +11 -3
package/README.md CHANGED
@@ -152,3 +152,11 @@ Issues and PRs welcome — [CONTRIBUTING.md](CONTRIBUTING.md). The codebase is T
152
152
  ## License
153
153
 
154
154
  MIT © Calliope Labs Inc
155
+
156
+ ## Code migration kit
157
+
158
+ The packaged [migration playbook](docs/migration-playbook.md) provides versioned
159
+ inventory, baseline, assessment and verification helpers for the initial
160
+ JavaScript-to-TypeScript CLI adapter. Discover them offline with
161
+ `calliope migration capabilities --json`; native reviewed execution remains the
162
+ authority for worker commands, budgets and file scope.
@@ -0,0 +1,10 @@
1
+ import type { ModelPreference } from '../preferences/types.js';
2
+ export interface AcpControls {
3
+ version: 1;
4
+ revision: string | null;
5
+ mode: 'work' | 'plan';
6
+ selection?: ModelPreference;
7
+ }
8
+ export declare function readAcpControls(id: string, creating?: boolean): AcpControls;
9
+ export declare function saveAcpControls(id: string, value: AcpControls, creating?: boolean): AcpControls;
10
+ //# sourceMappingURL=controls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controls.d.ts","sourceRoot":"","sources":["../../src/acp/controls.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B;AAQD,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,WAAW,CAazE;AACD,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,UAAQ,GAAG,WAAW,CAwB7F"}
@@ -0,0 +1,81 @@
1
+ /** Session controls are preferences, never a grant of execution authority. */
2
+ import * as fs from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { getSessionById, getSessionDirById } from '../storage.js';
6
+ import { assertSessionDirectory, readPrivateSessionFile } from '../sessions/index.js';
7
+ import { validatePreference } from '../preferences/validate.js';
8
+ const invalid = () => new Error('ACP session controls are missing, damaged or changed; reload the session or restore its saved controls.');
9
+ function location(id) {
10
+ const dir = getSessionDirById(id);
11
+ if (!dir)
12
+ throw invalid();
13
+ assertSessionDirectory(dir);
14
+ return join(dir, 'acp-controls.json');
15
+ }
16
+ export function readAcpControls(id, creating = false) {
17
+ const raw = readPrivateSessionFile(location(id), 16384);
18
+ if (raw === null) {
19
+ if (!creating && getSessionById(id)?.acpControlsVersion === 1)
20
+ throw invalid();
21
+ return { version: 1, revision: null, mode: 'work' };
22
+ }
23
+ let value;
24
+ try {
25
+ value = JSON.parse(raw);
26
+ }
27
+ catch {
28
+ throw invalid();
29
+ }
30
+ if (!value || Array.isArray(value) || value.version !== 1 || typeof value.revision !== 'string' ||
31
+ !/^[a-f0-9-]{36}$/.test(value.revision) || !['work', 'plan'].includes(value.mode) ||
32
+ Object.keys(value).some(key => !['version', 'revision', 'mode', 'selection'].includes(key)))
33
+ throw invalid();
34
+ if (value.selection !== undefined)
35
+ value.selection = validatePreference(value.selection);
36
+ return value;
37
+ }
38
+ export function saveAcpControls(id, value, creating = false) {
39
+ const file = location(id), lock = file + '.lock', temporary = file + '.' + randomUUID() + '.tmp';
40
+ let fd;
41
+ try {
42
+ fd = fs.openSync(lock, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY, 0o600);
43
+ }
44
+ catch {
45
+ throw new Error('ACP session controls have another writer; reload before retrying.');
46
+ }
47
+ try {
48
+ if (readAcpControls(id, creating).revision !== value.revision)
49
+ throw invalid();
50
+ const next = { version: 1, revision: randomUUID(), mode: value.mode,
51
+ ...(value.selection ? { selection: validatePreference(value.selection) } : {}) };
52
+ if (!['work', 'plan'].includes(next.mode))
53
+ throw invalid();
54
+ const output = fs.openSync(temporary, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY, 0o600);
55
+ try {
56
+ fs.writeFileSync(output, JSON.stringify(next) + '\n');
57
+ fs.fsyncSync(output);
58
+ }
59
+ finally {
60
+ fs.closeSync(output);
61
+ }
62
+ fs.renameSync(temporary, file);
63
+ // Match native session commits: NTFS does not support flushing a read-only directory handle.
64
+ if (process.platform !== 'win32') {
65
+ const directory = fs.openSync(dirname(file), fs.constants.O_RDONLY);
66
+ try {
67
+ fs.fsyncSync(directory);
68
+ }
69
+ finally {
70
+ fs.closeSync(directory);
71
+ }
72
+ }
73
+ return next;
74
+ }
75
+ finally {
76
+ fs.closeSync(fd);
77
+ fs.rmSync(temporary, { force: true });
78
+ fs.unlinkSync(lock);
79
+ }
80
+ }
81
+ //# sourceMappingURL=controls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controls.js","sourceRoot":"","sources":["../../src/acp/controls.ts"],"names":[],"mappings":"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"}
package/dist/acp.d.ts CHANGED
@@ -9,7 +9,8 @@
9
9
  * agent logic.
10
10
  *
11
11
  * Baseline agent surface implemented: `initialize`, `authenticate`, `session/new`,
12
- * `session/load`, `session/prompt`; `session/cancel`; streaming `session/update`
12
+ * `session/load`, `session/prompt`, `session/set_mode`, `session/set_config_option`;
13
+ * `session/cancel`; streaming `session/update`
13
14
  * notifications; and the `session/request_permission` flow. When the client advertises `fs`
14
15
  * capabilities, file tools are routed through the client's `fs/read_text_file` and
15
16
  * `fs/write_text_file` so edits land on the editor's (possibly unsaved) buffers.
@@ -19,18 +20,27 @@
19
20
  * lazy-loaded from bin.ts only for the `acp` subcommand, so it adds nothing to the
20
21
  * default cold-start path.
21
22
  */
22
- import { type AgentContext, type Stream, type InitializeRequest, type InitializeResponse, type NewSessionRequest, type NewSessionResponse, type LoadSessionRequest, type LoadSessionResponse, type AuthenticateResponse, type PromptRequest, type PromptResponse, type CancelNotification } from '@agentclientprotocol/sdk';
23
+ import { type AgentContext, type Stream, type InitializeRequest, type InitializeResponse, type NewSessionRequest, type NewSessionResponse, type LoadSessionRequest, type LoadSessionResponse, type AuthenticateResponse, type PromptRequest, type PromptResponse, type CancelNotification, type SetSessionModeRequest, type SetSessionConfigOptionRequest, type SetSessionConfigOptionResponse } from '@agentclientprotocol/sdk';
24
+ import { type AgentExecution } from './execution/guard.js';
25
+ import { type DelegatedExecutionOptions } from './execution/delegated.js';
23
26
  declare class CalliopeAgent {
24
27
  private readonly conn;
28
+ private readonly execution?;
25
29
  private readonly approvals;
26
30
  private clientCapabilities;
27
31
  private readonly sessions;
28
32
  /** Tail of the outgoing write chain; awaited to flush notifications. */
29
33
  private lastWrite;
30
- constructor(conn: AgentContext);
34
+ constructor(conn: AgentContext, execution?: AgentExecution | undefined);
35
+ private controlsState;
36
+ private configOptions;
37
+ private discoverModels;
38
+ private idleSession;
39
+ setMode(params: SetSessionModeRequest, signal?: AbortSignal): Promise<{}>;
40
+ setConfigOption(params: SetSessionConfigOptionRequest, signal?: AbortSignal): Promise<SetSessionConfigOptionResponse>;
31
41
  initialize(params: InitializeRequest): Promise<InitializeResponse>;
32
42
  authenticate(): Promise<AuthenticateResponse>;
33
- newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
43
+ newSession(params: NewSessionRequest, signal?: AbortSignal): Promise<NewSessionResponse>;
34
44
  /**
35
45
  * Restore a saved session and replay its conversation as session/update
36
46
  * notifications before responding. Same store and rules as terminal /resume:
@@ -38,7 +48,7 @@ declare class CalliopeAgent {
38
48
  * without a recorded result are closed as unknown outcomes. No provider
39
49
  * request is made and no tool runs.
40
50
  */
41
- loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse>;
51
+ loadSession(params: LoadSessionRequest, signal?: AbortSignal): Promise<LoadSessionResponse>;
42
52
  prompt(params: PromptRequest): Promise<PromptResponse>;
43
53
  cancel(params: CancelNotification): Promise<void>;
44
54
  private runTurn;
@@ -73,11 +83,15 @@ declare class CalliopeAgent {
73
83
  * which drive the SDK's client side against it over an in-memory stream pair.
74
84
  * The SDK calls onConnect before dispatch, so the returned agent is ready.
75
85
  */
76
- export declare function serveAcp(stream: Stream): CalliopeAgent;
86
+ export declare function serveAcp(stream: Stream, options?: {
87
+ execution?: AgentExecution;
88
+ }): CalliopeAgent;
77
89
  /**
78
90
  * Run Calliope as an ACP agent over stdio (stdout/stdin carry JSON-RPC). Started
79
91
  * from bin.ts when argv[0] === 'acp'. Resolves when the client closes the stream.
80
92
  */
81
- export declare function runAcpAgent(): Promise<number>;
93
+ export declare function runAcpAgent(options?: {
94
+ ledger?: DelegatedExecutionOptions;
95
+ }): Promise<number>;
82
96
  export {};
83
97
  //# sourceMappingURL=acp.d.ts.map
package/dist/acp.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"acp.d.ts","sourceRoot":"","sources":["../src/acp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,OAAO,EAGL,KAAK,YAAY,EAIjB,KAAK,MAAM,EAKX,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACxB,MAAM,0BAA0B,CAAC;AAwKlC,cAAM,aAAa;IAOL,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuB;IACjD,OAAO,CAAC,kBAAkB,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,wEAAwE;IACxE,OAAO,CAAC,SAAS,CAAoC;IAErD,YAA6B,IAAI,EAAE,YAAY,EAAI;IAI7C,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoBvE;IAIK,YAAY,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAElD;IAIK,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAiCvE;IAID;;;;;;OAMG;IACG,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA6D1E;IAIK,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CA0B3D;IAIK,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOtD;YAIa,OAAO;IAsDrB,yEAAyE;IACzE,OAAO,CAAC,gBAAgB;IAkBxB;;;;OAIG;YACW,iBAAiB;IAkC/B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAsBxB;;;;OAIG;IACH,OAAO,CAAC,IAAI;IAQZ,gDAAgD;IAChD,OAAO,CAAC,KAAK;IAIb,iEAAiE;IAC3D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAM9B;CACF;AAMD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAatD;AAED;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAkBnD"}
1
+ {"version":3,"file":"acp.d.ts","sourceRoot":"","sources":["../src/acp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAQH,OAAO,EAGL,KAAK,YAAY,EAIjB,KAAK,MAAM,EAKX,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,EAEpC,MAAM,0BAA0B,CAAC;AAkBlC,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAuE,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AA0J/I,cAAM,aAAa;IAOL,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAgB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAN5E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuB;IACjD,OAAO,CAAC,kBAAkB,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,wEAAwE;IACxE,OAAO,CAAC,SAAS,CAAoC;IAErD,YAA6B,IAAI,EAAE,YAAY,EAAmB,SAAS,CAAC,EAAE,cAAc,YAAA,EAAI;IAEhG,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,aAAa;YAOP,cAAc;IAa5B,OAAO,CAAC,WAAW;IAQb,OAAO,CAAC,MAAM,EAAC,qBAAqB,EAAC,MAAM,CAAC,EAAC,WAAW,eAS7D;IACK,eAAe,CAAC,MAAM,EAAC,6BAA6B,EAAC,MAAM,CAAC,EAAC,WAAW,GAAE,OAAO,CAAC,8BAA8B,CAAC,CAkBtH;IAIK,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAqBvE;IAIK,YAAY,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAElD;IAIK,UAAU,CAAC,MAAM,EAAE,iBAAiB,EAAE,MAAM,CAAC,EAAC,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAyC5F;IAID;;;;;;OAMG;IACG,WAAW,CAAC,MAAM,EAAE,kBAAkB,EAAE,MAAM,CAAC,EAAC,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAmE/F;IAIK,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAuB3D;IAIK,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOtD;YAIa,OAAO;IA4DrB,yEAAyE;IACzE,OAAO,CAAC,gBAAgB;IAkBxB;;;;OAIG;YACW,iBAAiB;IAkC/B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAsBxB;;;;OAIG;IACH,OAAO,CAAC,IAAI;IAQZ,gDAAgD;IAChD,OAAO,CAAC,KAAK;IAIb,iEAAiE;IAC3D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAM9B;CACF;AAMD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,cAAc,CAAA;CAAO,GAAG,aAAa,CAepG;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,yBAAyB,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBvG"}
package/dist/acp.js CHANGED
@@ -9,7 +9,8 @@
9
9
  * agent logic.
10
10
  *
11
11
  * Baseline agent surface implemented: `initialize`, `authenticate`, `session/new`,
12
- * `session/load`, `session/prompt`; `session/cancel`; streaming `session/update`
12
+ * `session/load`, `session/prompt`, `session/set_mode`, `session/set_config_option`;
13
+ * `session/cancel`; streaming `session/update`
13
14
  * notifications; and the `session/request_permission` flow. When the client advertises `fs`
14
15
  * capabilities, file tools are routed through the client's `fs/read_text_file` and
15
16
  * `fs/write_text_file` so edits land on the editor's (possibly unsaved) buffers.
@@ -20,6 +21,7 @@
20
21
  * default cold-start path.
21
22
  */
22
23
  import { boundAcpInput, ACP_INPUT_LIMIT_METHOD } from './acp/input.js';
24
+ import { readAcpControls, saveAcpControls } from './acp/controls.js';
23
25
  import { ApprovalStore } from './approvals/index.js';
24
26
  import { realpathSync } from 'node:fs';
25
27
  import * as path from 'node:path';
@@ -31,6 +33,7 @@ import { completePendingTools, runTurn } from './runtime/index.js';
31
33
  import { createSession, getSessionById, getSessionDirById, readSessionConversation, saveSessionConversation } from './storage.js';
32
34
  import { SessionRecoveryError, readToolOutputs } from './sessions/index.js';
33
35
  import { resolvePreferences } from './preferences/index.js';
36
+ import { getAvailableModels } from './model-detection.js';
34
37
  import { cancellable, isCancellation } from './cancellation.js';
35
38
  import { TOOLS } from './tools.js';
36
39
  import { DEFAULT_MODELS } from './types.js';
@@ -39,6 +42,8 @@ import * as memory from './memory.js';
39
42
  import { resolveIterationLimit } from './iteration-limit.js';
40
43
  import { formatBudgetHalt } from './budget.js';
41
44
  import { RunLog } from './runlog.js';
45
+ import { ExecutionGuard } from './execution/guard.js';
46
+ import { connectDelegatedExecution, executionBinding, assertExecutionBinding } from './execution/delegated.js';
42
47
  // ============================================================================
43
48
  // Diagnostics — stderr only (stdout carries the protocol).
44
49
  // ============================================================================
@@ -141,10 +146,10 @@ function promptToText(blocks) {
141
146
  return parts.join('\n');
142
147
  }
143
148
  /** Provider/model preferences for a session's project; session/new and session/load resolve them alike. */
144
- function sessionPreferences(cwd) {
149
+ function sessionPreferences(cwd, controls) {
145
150
  let preference;
146
151
  try {
147
- preference = resolvePreferences(cwd);
152
+ preference = resolvePreferences(cwd, { session: controls?.selection });
148
153
  }
149
154
  catch (error) {
150
155
  throw RequestError.invalidParams({ error: error instanceof Error ? error.message : String(error) });
@@ -167,13 +172,94 @@ function sessionPreferences(cwd) {
167
172
  // ============================================================================
168
173
  class CalliopeAgent {
169
174
  conn;
175
+ execution;
170
176
  approvals = new ApprovalStore();
171
177
  clientCapabilities = {};
172
178
  sessions = new Map();
173
179
  /** Tail of the outgoing write chain; awaited to flush notifications. */
174
180
  lastWrite = Promise.resolve();
175
- constructor(conn) {
181
+ constructor(conn, execution) {
176
182
  this.conn = conn;
183
+ this.execution = execution;
184
+ }
185
+ controlsState(session) {
186
+ return { modes: { currentModeId: session.controls.mode, availableModes: [
187
+ { id: 'work', name: 'Work', description: 'Use normal permissions and confirmations.' },
188
+ { id: 'plan', name: 'Plan', description: 'Analyze and plan without executing workspace mutations.' },
189
+ ] }, configOptions: this.configOptions(session) };
190
+ }
191
+ configOptions(session) {
192
+ const current = session.controls.selection?.model ? 'model:' + session.controls.selection.model : 'default';
193
+ const options = [{ value: 'default', name: this.execution ? 'Parent-managed model' : `Configured model${session.model ? ': ' + session.model : ''}` }];
194
+ if (!this.execution)
195
+ options.push(...session.modelOptions);
196
+ if (!options.some(option => option.value === current))
197
+ options.push({ value: current, name: session.model });
198
+ return [{ id: 'model', name: 'Model', category: 'model', type: 'select', currentValue: current, options }];
199
+ }
200
+ async discoverModels(session, signal) {
201
+ session.modelOptions = [];
202
+ if (this.execution)
203
+ return;
204
+ const bounded = signal ? AbortSignal.any([signal, AbortSignal.timeout(5000)]) : AbortSignal.timeout(5000);
205
+ try {
206
+ const models = await getAvailableModels(session.resolvedProvider, { quiet: true, signal: bounded });
207
+ session.modelOptions = models.filter(model => model.evidence?.source === 'live' && model.capabilities?.chat !== false && model.id.length <= 512 && !/[\x00-\x1f\x7f]/.test(model.id))
208
+ .slice(0, 256).map(model => ({ value: 'model:' + model.id, name: model.name || model.id }));
209
+ }
210
+ catch {
211
+ if (signal?.aborted)
212
+ throw signal.reason;
213
+ // Unavailable discovery never invents selectable model IDs.
214
+ }
215
+ }
216
+ idleSession(id) {
217
+ const session = this.sessions.get(id);
218
+ if (!session)
219
+ throw RequestError.invalidParams({ error: `unknown session: ${id}` });
220
+ if (session.controller)
221
+ throw RequestError.invalidParams({ error: 'A prompt is already running in this session' });
222
+ if (readAcpControls(id).revision !== session.controls.revision)
223
+ throw RequestError.invalidParams({ error: 'Session controls changed; load the current session before continuing.' });
224
+ if (this.execution)
225
+ new ExecutionGuard(this.execution, session.cwd).assertActive();
226
+ return session;
227
+ }
228
+ async setMode(params, signal) {
229
+ signal?.throwIfAborted();
230
+ let session = this.idleSession(params.sessionId);
231
+ if (params.modeId !== 'work' && params.modeId !== 'plan')
232
+ throw RequestError.invalidParams({ error: 'Unsupported session mode' });
233
+ if (this.execution) {
234
+ await new ExecutionGuard(this.execution, session.cwd).refreshAuthority(signal);
235
+ session = this.idleSession(params.sessionId);
236
+ }
237
+ signal?.throwIfAborted();
238
+ session.controls = saveAcpControls(session.id, { ...session.controls, mode: params.modeId });
239
+ await this.emit(session.id, { sessionUpdate: 'current_mode_update', currentModeId: session.controls.mode });
240
+ return {};
241
+ }
242
+ async setConfigOption(params, signal) {
243
+ signal?.throwIfAborted();
244
+ const session = this.idleSession(params.sessionId);
245
+ if (params.configId !== 'model' || typeof params.value !== 'string')
246
+ throw RequestError.invalidParams({ error: 'Unsupported session configuration' });
247
+ if (this.execution)
248
+ throw RequestError.invalidParams({ error: 'The parent controls the delegated model selection.' });
249
+ const revision = session.controls.revision, provider = session.resolvedProvider;
250
+ await this.discoverModels(session, signal);
251
+ if (this.idleSession(params.sessionId) !== session || session.controls.revision !== revision || session.resolvedProvider !== provider)
252
+ throw RequestError.invalidParams({ error: 'Session changed during model discovery; retry with its current controls.' });
253
+ const value = params.value;
254
+ if (value !== 'default' && !session.modelOptions.some(option => option.value === value))
255
+ throw RequestError.invalidParams({ error: 'Select a currently discovered model.' });
256
+ const next = { ...session.controls, selection: value === 'default' ? undefined : { provider: session.resolvedProvider, model: value.slice(6) } };
257
+ const preferences = sessionPreferences(session.cwd, next);
258
+ const controls = saveAcpControls(session.id, next);
259
+ Object.assign(session, preferences, { controls });
260
+ const configOptions = this.configOptions(session);
261
+ await this.emit(session.id, { sessionUpdate: 'config_option_update', configOptions });
262
+ return { configOptions };
177
263
  }
178
264
  // ---- ACP: initialize --------------------------------------------------
179
265
  async initialize(params) {
@@ -186,7 +272,8 @@ class CalliopeAgent {
186
272
  return {
187
273
  protocolVersion: negotiated,
188
274
  agentCapabilities: {
189
- // MCP-over-ACP and modes are not implemented yet.
275
+ _meta: { calliopeDelegation: { version: 1, workspaceBinding: 1, sessionBinding: 1 }, calliopeSessionControls: { version: 1 } },
276
+ // MCP-over-ACP remains unsupported.
190
277
  loadSession: true,
191
278
  promptCapabilities: { image: false, audio: false, embeddedContext: false },
192
279
  },
@@ -200,14 +287,25 @@ class CalliopeAgent {
200
287
  return {};
201
288
  }
202
289
  // ---- ACP: session/new -------------------------------------------------
203
- async newSession(params) {
290
+ async newSession(params, signal) {
204
291
  const cwd = params.cwd || process.cwd();
292
+ let guard;
293
+ try {
294
+ if (this.execution) {
295
+ guard = new ExecutionGuard(this.execution, cwd);
296
+ guard.assertActive();
297
+ }
298
+ }
299
+ catch (error) {
300
+ throw RequestError.invalidParams({ error: errMessage(error) });
301
+ }
205
302
  const { preference, provider, resolvedProvider, model } = sessionPreferences(cwd);
206
303
  const costModel = model || DEFAULT_MODELS[resolvedProvider];
207
- const sessionId = createSession(cwd, { activate: false, prefix: 'acp' }).id;
304
+ const sessionId = createSession(cwd, { activate: false, prefix: 'acp', acpControlsVersion: 1, ...(this.execution ? { executionBinding: executionBinding(this.execution) } : {}) }).id;
305
+ const controls = saveAcpControls(sessionId, { version: 1, revision: null, mode: 'work' }, true);
208
306
  const runlog = RunLog.open(sessionId);
209
307
  const systemPrompt = getSystemPromptForProvider(resolvedProvider);
210
- const memoryContext = memory.buildMemoryContext(cwd);
308
+ const memoryContext = guard ? memory.buildMemoryContext(cwd, { allowPath: file => !guard.check({ id: 'context', name: 'read_file', arguments: { path: file } }) }) : memory.buildMemoryContext(cwd);
211
309
  const fullPrompt = memoryContext.trim()
212
310
  ? systemPrompt + '\n\n--- Project Context ---\n' + memoryContext
213
311
  : systemPrompt;
@@ -226,9 +324,12 @@ class CalliopeAgent {
226
324
  messages,
227
325
  runlog,
228
326
  cancelled: false,
327
+ controls, modelOptions: [],
229
328
  });
329
+ const session = this.sessions.get(sessionId);
330
+ await this.discoverModels(session, signal);
230
331
  debug(`session/new: ${sessionId} cwd=${cwd} provider=${resolvedProvider} model=${costModel}`);
231
- return { sessionId };
332
+ return { sessionId, ...this.controlsState(session) };
232
333
  }
233
334
  // ---- ACP: session/load ------------------------------------------------
234
335
  /**
@@ -238,7 +339,7 @@ class CalliopeAgent {
238
339
  * without a recorded result are closed as unknown outcomes. No provider
239
340
  * request is made and no tool runs.
240
341
  */
241
- async loadSession(params) {
342
+ async loadSession(params, signal) {
242
343
  const { sessionId, cwd } = params;
243
344
  if (this.sessions.get(sessionId)?.controller)
244
345
  throw RequestError.invalidParams({ error: 'A prompt is already running in this session' });
@@ -247,6 +348,9 @@ class CalliopeAgent {
247
348
  throw RequestError.invalidParams({ error: `unknown session: ${sessionId}` });
248
349
  let state;
249
350
  try {
351
+ assertExecutionBinding(saved.executionBinding, this.execution, !!this.execution);
352
+ if (this.execution)
353
+ new ExecutionGuard(this.execution, cwd).assertActive();
250
354
  if (!path.isAbsolute(cwd) || realpathSync(cwd) !== realpathSync(saved.projectPath)) {
251
355
  throw new Error(`Session belongs to ${saved.projectPath}; load it with that working directory.`);
252
356
  }
@@ -257,9 +361,13 @@ class CalliopeAgent {
257
361
  catch (error) {
258
362
  throw RequestError.invalidParams({ error: errMessage(error) });
259
363
  }
364
+ const controls = readAcpControls(sessionId);
365
+ if (this.execution && controls.selection)
366
+ throw RequestError.invalidParams({ error: 'A delegated session cannot restore an independently selected model.' });
260
367
  const session = {
261
- id: sessionId, cwd, ...sessionPreferences(cwd), revision: state.revision,
368
+ id: sessionId, cwd, ...sessionPreferences(cwd, controls), revision: state.revision,
262
369
  messages: state.messages, runlog: RunLog.open(sessionId), cancelled: false,
370
+ controls, modelOptions: [],
263
371
  };
264
372
  // An idle copy already open here is replaced by the saved state; a prompt sent
265
373
  // before the load responds is refused as an unknown session.
@@ -301,18 +409,16 @@ class CalliopeAgent {
301
409
  await this.emit(sessionId, { sessionUpdate: 'agent_thought_chunk', content: { type: 'text', text: `[${notices.join(' ')}]\n` } });
302
410
  await this.flush();
303
411
  this.sessions.set(sessionId, session);
412
+ await this.discoverModels(session, signal);
304
413
  session.runlog.policyEvent({ tool: 'session', source: 'session-recovery', decision: 'allow', durationMs: 0,
305
414
  reason: `session/load revision=${state.revision} messages=${session.messages.length}` });
306
415
  await session.runlog.flush();
307
416
  debug(`session/load: ${sessionId} cwd=${cwd} messages=${session.messages.length} status=${state.status}`);
308
- return {};
417
+ return this.controlsState(session);
309
418
  }
310
419
  // ---- ACP: session/prompt ---------------------------------------------
311
420
  async prompt(params) {
312
- const session = this.sessions.get(params.sessionId);
313
- if (!session) {
314
- throw RequestError.invalidParams({ error: `unknown session: ${params.sessionId}` });
315
- }
421
+ const session = this.idleSession(params.sessionId);
316
422
  if (session.controller)
317
423
  throw RequestError.invalidParams({ error: 'A prompt is already running in this session' });
318
424
  session.controller = new AbortController();
@@ -353,6 +459,11 @@ class CalliopeAgent {
353
459
  // ---- The agent loop (ACP-flavoured mirror of the headless loop) --------
354
460
  async runTurn(session, prompt) {
355
461
  const messages = { current: session.messages };
462
+ const controlRevision = session.controls.revision;
463
+ const assertControls = () => {
464
+ if (readAcpControls(session.id).revision !== controlRevision)
465
+ throw new Error('Session controls changed during execution; reload before continuing.');
466
+ };
356
467
  let streamedChars = 0;
357
468
  try {
358
469
  for (const warning of session.preference.warnings)
@@ -360,6 +471,7 @@ class CalliopeAgent {
360
471
  const result = await runTurn({
361
472
  captureToolOutput: true,
362
473
  client: 'acp',
474
+ execution: this.execution,
363
475
  onSafetyBranch: async () => {
364
476
  const { branchSession } = await import('./session-management/index.js');
365
477
  const branch = await branchSession(session.id, { kind: 'safety', signal: session.controller?.signal, runlog: session.runlog });
@@ -371,11 +483,12 @@ class CalliopeAgent {
371
483
  session.runlog.sessionCheckpoint({ revision: saved.revision, status, messageCount: saved.messages.length, checksum: saved.checksum });
372
484
  },
373
485
  sessionId: session.id, cwd: session.cwd, provider: session.provider,
486
+ get mode() { assertControls(); return session.controls.mode; },
374
487
  model: session.model || undefined, prompt, messages, signal: session.controller?.signal,
375
488
  preferenceSources: session.preference.sources,
376
489
  runlog: session.runlog, maxIterations: resolveIterationLimit(config.get('maxIterations')),
377
490
  confirmation: 'mutating', approvals: this.approvals, tools: () => TOOLS, toolOptions: { fs: this.clientFsDelegate(session.id) },
378
- prepare: async (request) => { streamedChars = 0; return request; },
491
+ prepare: async (request) => { assertControls(); streamedChars = 0; return request; },
379
492
  approve: (call, decision) => this.requestPermission(session, call, decision.request?.reusable ?? false),
380
493
  onToken: token => {
381
494
  if (!token || session.cancelled)
@@ -528,15 +641,17 @@ class CalliopeAgent {
528
641
  * which drive the SDK's client side against it over an in-memory stream pair.
529
642
  * The SDK calls onConnect before dispatch, so the returned agent is ready.
530
643
  */
531
- export function serveAcp(stream) {
644
+ export function serveAcp(stream, options = {}) {
532
645
  let agent;
533
646
  createAgent()
534
- .onConnect(connection => { agent = new CalliopeAgent(connection.client); })
647
+ .onConnect(connection => { agent = new CalliopeAgent(connection.client, options.execution); })
535
648
  .onRequest(ACP_INPUT_LIMIT_METHOD, () => undefined, () => { throw RequestError.invalidParams({ error: 'ACP input complexity limit exceeded.' }); })
536
649
  .onRequest(methods.agent.initialize, ({ params }) => agent.initialize(params))
537
650
  .onRequest(methods.agent.authenticate, () => agent.authenticate())
538
- .onRequest(methods.agent.session.new, ({ params }) => agent.newSession(params))
539
- .onRequest(methods.agent.session.load, ({ params }) => agent.loadSession(params))
651
+ .onRequest(methods.agent.session.new, ({ params, signal }) => agent.newSession(params, signal))
652
+ .onRequest(methods.agent.session.load, ({ params, signal }) => agent.loadSession(params, signal))
653
+ .onRequest(methods.agent.session.setMode, ({ params, signal }) => agent.setMode(params, signal))
654
+ .onRequest(methods.agent.session.setConfigOption, ({ params, signal }) => agent.setConfigOption(params, signal))
540
655
  .onRequest(methods.agent.session.prompt, ({ params }) => agent.prompt(params))
541
656
  .onNotification(methods.agent.session.cancel, ({ params }) => agent.cancel(params))
542
657
  .connect({ ...stream, readable: boundAcpInput(stream.readable) });
@@ -546,11 +661,12 @@ export function serveAcp(stream) {
546
661
  * Run Calliope as an ACP agent over stdio (stdout/stdin carry JSON-RPC). Started
547
662
  * from bin.ts when argv[0] === 'acp'. Resolves when the client closes the stream.
548
663
  */
549
- export async function runAcpAgent() {
664
+ export async function runAcpAgent(options = {}) {
665
+ const execution = options.ledger ? await connectDelegatedExecution(process.cwd(), options.ledger) : undefined;
550
666
  const input = Readable.toWeb(process.stdin);
551
667
  const output = Writable.toWeb(process.stdout);
552
668
  const stream = ndJsonStream(output, input);
553
- const agent = serveAcp(stream);
669
+ const agent = serveAcp(stream, { execution });
554
670
  log(`Calliope ACP agent ready on stdio (protocol v${PROTOCOL_VERSION}). Logs go to stderr.`);
555
671
  // Stay alive until the client disconnects (stdin closes).
556
672
  await new Promise((resolve) => {