toolroll 0.9.65 → 0.9.68

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 (264) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +20 -2
  3. package/dist/acp-client.js +1 -3
  4. package/dist/agent-board.d.ts +0 -1
  5. package/dist/agent-board.js +0 -1
  6. package/dist/agent-fence.d.ts +69 -2
  7. package/dist/agent-fence.js +294 -11
  8. package/dist/agentconfig.js +4 -1
  9. package/dist/batch-checks.js +3 -2
  10. package/dist/browser/THIRD_PARTY_NOTICES.txt +46 -0
  11. package/dist/browser/terminal.js +134 -23
  12. package/dist/browser/workspace.css +2 -2
  13. package/dist/browser/workspace.js +31 -29
  14. package/dist/builder.d.ts +4 -0
  15. package/dist/builder.js +56 -37
  16. package/dist/canonical-json.d.ts +14 -0
  17. package/dist/canonical-json.js +36 -0
  18. package/dist/chat-actions.js +1 -2
  19. package/dist/chat-admission.d.ts +19 -0
  20. package/dist/chat-admission.js +22 -0
  21. package/dist/chat-channel.js +1 -1
  22. package/dist/chat-delivery.js +0 -1
  23. package/dist/chat-follow.d.ts +17 -0
  24. package/dist/chat-follow.js +32 -0
  25. package/dist/chat-messages.js +0 -1
  26. package/dist/chat-settings-page.d.ts +23 -0
  27. package/dist/chat-settings-page.js +41 -0
  28. package/dist/claim.js +3 -6
  29. package/dist/claude-print.d.ts +12 -0
  30. package/dist/claude-print.js +34 -0
  31. package/dist/cli-http.d.ts +0 -7
  32. package/dist/cli-http.js +0 -21
  33. package/dist/cli.d.ts +3 -10
  34. package/dist/cli.js +15 -24
  35. package/dist/coding-provider.js +2 -1
  36. package/dist/coding-workspace.js +1 -1
  37. package/dist/{team-cli.d.ts → connect-cli.d.ts} +5 -9
  38. package/dist/connect-cli.js +331 -0
  39. package/dist/containment.d.ts +0 -2
  40. package/dist/containment.js +2 -6
  41. package/dist/contracts/chat-actions.d.ts +0 -1
  42. package/dist/contracts/chat-content.d.ts +0 -4
  43. package/dist/contracts/chat-content.js +0 -2
  44. package/dist/contracts/chat-tables.d.ts +0 -1
  45. package/dist/contracts/cli.d.ts +0 -62
  46. package/dist/contracts/cli.js +0 -4
  47. package/dist/contracts/coding-workspace.js +1 -1
  48. package/dist/contracts/console-api.d.ts +1 -0
  49. package/dist/contracts/console-api.js +1 -0
  50. package/dist/contracts/contract.js +1 -1
  51. package/dist/contracts/flow-card.d.ts +0 -1
  52. package/dist/contracts/flow-propose.d.ts +0 -1
  53. package/dist/contracts/flow-send.js +1 -1
  54. package/dist/contracts/gateway-tools.d.ts +0 -1
  55. package/dist/contracts/integration-metadata.js +1 -1
  56. package/dist/contracts/lead-tools.d.ts +0 -1
  57. package/dist/contracts/memory-pass.d.ts +0 -1
  58. package/dist/contracts/memory-pass.js +1 -1
  59. package/dist/contracts/plan.js +1 -1
  60. package/dist/contracts/project-knowledge.js +1 -1
  61. package/dist/contracts/project-skills.d.ts +0 -1
  62. package/dist/contracts/proof.js +1 -1
  63. package/dist/contracts/recipes.js +1 -1
  64. package/dist/contracts/scope.js +1 -1
  65. package/dist/contracts/scout-report.js +1 -1
  66. package/dist/contracts/teams-callback.d.ts +0 -1
  67. package/dist/converse.d.ts +0 -6
  68. package/dist/converse.js +0 -19
  69. package/dist/daemon.js +3 -2
  70. package/dist/decision.d.ts +1 -3
  71. package/dist/deprecations.d.ts +12 -4
  72. package/dist/deprecations.js +27 -4
  73. package/dist/desktop-update-recovery.js +1 -1
  74. package/dist/desktop-update.js +3 -2
  75. package/dist/discord-chat.js +4 -15
  76. package/dist/discord-settings.d.ts +2 -6
  77. package/dist/discord-settings.js +10 -38
  78. package/dist/discord.js +12 -23
  79. package/dist/evidence.d.ts +0 -1
  80. package/dist/evidence.js +0 -3
  81. package/dist/exec.d.ts +0 -9
  82. package/dist/exec.js +3 -81
  83. package/dist/flow-actions.js +0 -3
  84. package/dist/flow-draft.d.ts +1 -3
  85. package/dist/flow-draft.js +15 -28
  86. package/dist/flow-scripts.d.ts +1 -8
  87. package/dist/flow-scripts.js +0 -13
  88. package/dist/flow-send.js +1 -1
  89. package/dist/flow-share.d.ts +0 -2
  90. package/dist/flow-share.js +1 -6
  91. package/dist/flow-steps.js +1 -1
  92. package/dist/flow-triggers.d.ts +0 -1
  93. package/dist/flow-triggers.js +1 -2
  94. package/dist/flow-unattended.js +1 -12
  95. package/dist/flows.js +1 -1
  96. package/dist/health-cli.d.ts +2 -2
  97. package/dist/health-cli.js +1 -1
  98. package/dist/invoke.d.ts +18 -3
  99. package/dist/invoke.js +142 -17
  100. package/dist/is-record.d.ts +2 -0
  101. package/dist/is-record.js +2 -0
  102. package/dist/lead-doors.d.ts +1 -5
  103. package/dist/lead-doors.js +0 -4
  104. package/dist/lead-tools.js +3 -6
  105. package/dist/live-detail.d.ts +0 -1
  106. package/dist/live-detail.js +0 -6
  107. package/dist/live-terminal.d.ts +24 -13
  108. package/dist/live-terminal.js +81 -43
  109. package/dist/live.d.ts +0 -1
  110. package/dist/live.js +1 -2
  111. package/dist/mcp-core.js +0 -2
  112. package/dist/memory-pass.js +1 -1
  113. package/dist/model-catalog.js +1 -1
  114. package/dist/modes.js +1 -12
  115. package/dist/operate.d.ts +2 -0
  116. package/dist/operate.js +436 -745
  117. package/dist/password-bearer.d.ts +0 -2
  118. package/dist/password-bearer.js +0 -4
  119. package/dist/peek-cli.js +2 -1
  120. package/dist/peek.d.ts +2 -26
  121. package/dist/peek.js +2 -168
  122. package/dist/phase-routing.d.ts +0 -4
  123. package/dist/phase-routing.js +2 -21
  124. package/dist/planner-source.d.ts +2 -1
  125. package/dist/planner-source.js +2 -12
  126. package/dist/planner.js +2 -14
  127. package/dist/process-tree.d.ts +0 -2
  128. package/dist/process-tree.js +0 -10
  129. package/dist/project-delete-ui.d.ts +6 -0
  130. package/dist/project-delete-ui.js +20 -1
  131. package/dist/project-delete.d.ts +0 -2
  132. package/dist/project-delete.js +1 -3
  133. package/dist/project-learning.d.ts +0 -14
  134. package/dist/project-skills.d.ts +0 -1
  135. package/dist/project-skills.js +0 -14
  136. package/dist/project-tools.js +2 -10
  137. package/dist/provider.d.ts +0 -17
  138. package/dist/provider.js +0 -26
  139. package/dist/pulls.d.ts +0 -2
  140. package/dist/pulls.js +0 -9
  141. package/dist/release-adapters.d.ts +9 -0
  142. package/dist/release-adapters.js +81 -4
  143. package/dist/release.d.ts +7 -1
  144. package/dist/release.js +20 -7
  145. package/dist/remote-exec.d.ts +3 -2
  146. package/dist/remote-exec.js +3 -2
  147. package/dist/repos.d.ts +0 -5
  148. package/dist/repos.js +2 -10
  149. package/dist/repository-context.d.ts +0 -2
  150. package/dist/repository-context.js +0 -4
  151. package/dist/restart-certification.d.ts +0 -2
  152. package/dist/restart-certification.js +1 -5
  153. package/dist/result-follow-ups.d.ts +1 -4
  154. package/dist/result-follow-ups.js +4 -8
  155. package/dist/result-review.d.ts +0 -2
  156. package/dist/result-review.js +0 -2
  157. package/dist/retention.d.ts +0 -1
  158. package/dist/retention.js +0 -1
  159. package/dist/review-context.d.ts +0 -1
  160. package/dist/review-context.js +1 -12
  161. package/dist/reviewer.d.ts +0 -16
  162. package/dist/reviewer.js +0 -18
  163. package/dist/run-closeout.js +2 -1
  164. package/dist/scope.d.ts +13 -0
  165. package/dist/scope.js +11 -18
  166. package/dist/scout-browser.js +2 -1
  167. package/dist/serve.js +3 -2
  168. package/dist/server/chrome.d.ts +10 -1
  169. package/dist/server/chrome.js +45 -4
  170. package/dist/server/console.css +60 -137
  171. package/dist/server/flows.js +2 -2
  172. package/dist/server/http.d.ts +2 -0
  173. package/dist/server/http.js +5 -0
  174. package/dist/server/pages.js +2 -2
  175. package/dist/server/people-tokens.js +3 -4
  176. package/dist/server/remote.js +1 -6
  177. package/dist/server/render-chat.d.ts +9 -2
  178. package/dist/server/render-chat.js +53 -7
  179. package/dist/server/render-pages.d.ts +28 -3
  180. package/dist/server/render-pages.js +78 -9
  181. package/dist/server/render-results.d.ts +0 -6
  182. package/dist/server/render-results.js +3 -9
  183. package/dist/server/render-tasks.js +6 -6
  184. package/dist/server/route-table.d.ts +0 -4
  185. package/dist/server/route-table.js +1 -6
  186. package/dist/server/settings.js +43 -22
  187. package/dist/server/task-views.js +1 -1
  188. package/dist/server/tasks.js +10 -7
  189. package/dist/setup-guide.d.ts +2 -2
  190. package/dist/skills.d.ts +0 -6
  191. package/dist/skills.js +0 -8
  192. package/dist/slack-chat.d.ts +1 -7
  193. package/dist/slack-chat.js +5 -18
  194. package/dist/slack-settings.d.ts +2 -6
  195. package/dist/slack-settings.js +10 -38
  196. package/dist/slack.js +12 -23
  197. package/dist/sleep.d.ts +2 -0
  198. package/dist/sleep.js +2 -0
  199. package/dist/store.d.ts +11 -74
  200. package/dist/store.js +36 -179
  201. package/dist/structured-output.d.ts +0 -1
  202. package/dist/structured-output.js +0 -1
  203. package/dist/subagent-work.d.ts +0 -2
  204. package/dist/subagent-work.js +0 -2
  205. package/dist/subagents.d.ts +1 -3
  206. package/dist/subagents.js +20 -33
  207. package/dist/subscription-chat.d.ts +1 -3
  208. package/dist/subscription-chat.js +2 -3
  209. package/dist/task-sizing.d.ts +1 -4
  210. package/dist/task-sizing.js +3 -10
  211. package/dist/teams-chat.js +4 -11
  212. package/dist/teams.js +12 -19
  213. package/dist/teardown-git.d.ts +22 -0
  214. package/dist/teardown-git.js +136 -2
  215. package/dist/telegram-lead.js +2 -2
  216. package/dist/telegram.js +1 -1
  217. package/dist/token-notices.d.ts +0 -11
  218. package/dist/token-notices.js +1 -5
  219. package/dist/tool-broker.d.ts +12 -0
  220. package/dist/tool-broker.js +66 -2
  221. package/dist/tool-secret-store.d.ts +0 -3
  222. package/dist/tool-secret-store.js +2 -11
  223. package/dist/toolroll-update.d.ts +0 -2
  224. package/dist/toolroll-update.js +2 -2
  225. package/dist/unfenced-test-support.d.ts +2 -0
  226. package/dist/unfenced-test-support.js +11 -0
  227. package/dist/verification-evidence.d.ts +0 -1
  228. package/dist/verification-evidence.js +0 -1
  229. package/dist/webhooks.d.ts +0 -1
  230. package/dist/webhooks.js +0 -1
  231. package/dist/workspace-ui.d.ts +0 -8
  232. package/dist/workspace-ui.js +1 -6
  233. package/dist/writes-mode.d.ts +9 -0
  234. package/dist/writes-mode.js +8 -0
  235. package/package.json +5 -1
  236. package/dist/contracts/native-census.d.ts +0 -42
  237. package/dist/contracts/native-census.js +0 -36
  238. package/dist/process-recovery-anchor.d.ts +0 -75
  239. package/dist/process-recovery-anchor.js +0 -188
  240. package/dist/process-recovery-coalition.d.ts +0 -25
  241. package/dist/process-recovery-coalition.js +0 -105
  242. package/dist/process-recovery-eligibility.d.ts +0 -179
  243. package/dist/process-recovery-eligibility.js +0 -174
  244. package/dist/process-recovery-native.d.ts +0 -161
  245. package/dist/process-recovery-native.js +0 -595
  246. package/dist/process-recovery-provenance.d.ts +0 -163
  247. package/dist/process-recovery-provenance.js +0 -292
  248. package/dist/process-recovery-services.d.ts +0 -56
  249. package/dist/process-recovery-services.js +0 -191
  250. package/dist/process-recovery-settlement.d.ts +0 -32
  251. package/dist/process-recovery-settlement.js +0 -133
  252. package/dist/process-recovery.d.ts +0 -41
  253. package/dist/process-recovery.js +0 -91
  254. package/dist/session-brief.d.ts +0 -8
  255. package/dist/session-brief.js +0 -24
  256. package/dist/session-cli.d.ts +0 -42
  257. package/dist/session-cli.js +0 -271
  258. package/dist/session-contract.d.ts +0 -183
  259. package/dist/session-contract.js +0 -122
  260. package/dist/session-remote.d.ts +0 -19
  261. package/dist/session-remote.js +0 -86
  262. package/dist/session-service.d.ts +0 -22
  263. package/dist/session-service.js +0 -157
  264. package/dist/team-cli.js +0 -692
@@ -1,86 +0,0 @@
1
- import { realpathSync } from 'node:fs';
2
- import { envelopeJson } from './envelope.js';
3
- import { REMOTE_MESSAGES, reproveRemote } from './operate-remote.js';
4
- import { SESSION_RESPONSE_BYTES, isSessionResponse } from './session-contract.js';
5
- import { parseSessionArguments, renderSessionResponse, sessionExitCode, sessionRequestOf } from './session-cli.js';
6
- import { SessionService } from './session-service.js';
7
- export function createSessionRunner(options) {
8
- const { store } = options;
9
- const clock = options.clock ?? (() => new Date());
10
- const authorized = (actor, repo) => {
11
- const account = store.accountOf(actor.name);
12
- return account !== null && account.revokedAt === null && account.generation === actor.generation && store.isInstanceOperator(actor.name)
13
- && (repo === undefined || (options.projectAllowed(repo) && options.projects().includes(repo)));
14
- };
15
- const service = options.workspace === null || store.isDemo() ? null : new SessionService({ workspace: options.workspace, authorized,
16
- project: (actor, input) => {
17
- const path = realpathSync(input);
18
- if (!authorized(actor, path) || !options.projects().includes(path))
19
- throw Error('Choose a project already admitted to this installation.');
20
- return path;
21
- },
22
- });
23
- return async (principal, argv, files, write) => {
24
- const json = argv.includes('--json');
25
- const parsed = parseSessionArguments(argv.slice(1), 'server');
26
- const operation = 'problem' in parsed ? null : parsed.spec.operation;
27
- const command = `session ${operation ?? '(unknown)'}`;
28
- let tokenName = null;
29
- const record = (outcome, repo, why) => {
30
- store.recordAction({ at: clock().toISOString(), actor: principal.account, repo, taskId: null, runId: null, action: `remote command: ${command}`, outcome, source: 'api',
31
- detail: `token ${tokenName ?? principal.tokenId}${why === null ? '' : ` · ${why}`}` });
32
- };
33
- const answer = (response) => {
34
- write(json ? envelopeJson({ ...response, command }) : renderSessionResponse(response));
35
- return sessionExitCode(response);
36
- };
37
- const refuse = (spec, reason, message) => {
38
- record('refused', null, reason);
39
- if (spec === null) {
40
- write(json ? envelopeJson({ ok: false, command, reason, message }) : message);
41
- return 2;
42
- }
43
- return answer({ version: 1, operation: spec.operation, ok: false, status: 'rejected', delivery: 'not-sent', retry: spec.mutation ? 'never' : 'safe-read', reason, message, nextActions: [] });
44
- };
45
- const proved = reproveRemote(store, principal, clock());
46
- if (!proved.ok)
47
- return refuse('problem' in parsed ? null : parsed.spec, 'unauthenticated', REMOTE_MESSAGES.stale);
48
- tokenName = proved.tokenName;
49
- if ('problem' in parsed)
50
- return refuse(null, 'usage', parsed.problem);
51
- const { spec } = parsed;
52
- if (spec.mutation && proved.scope !== 'act')
53
- return refuse(spec, 'read-only', REMOTE_MESSAGES.read);
54
- // A session reaches every project this installation admits: a project-limited token never stands for that.
55
- if (principal.projects !== null || !store.isInstanceOperator(principal.account))
56
- return refuse(spec, 'all-projects', 'Native sessions need instance operator access to every project. Use a token without a project limit.');
57
- const file = parsed.flags['file'];
58
- if (file !== undefined && (typeof file !== 'string' || !Object.hasOwn(files, file)))
59
- return refuse(spec, 'usage', REMOTE_MESSAGES.files);
60
- const validated = sessionRequestOf(parsed, typeof file === 'string' ? files[file] : undefined);
61
- if (!validated.ok)
62
- return refuse(spec, 'invalid-request', validated.message);
63
- if (service === null)
64
- return refuse(spec, 'session-unavailable', 'The session owner is not ready.');
65
- const repo = 'project' in validated.request && spec.operation === 'start' ? validated.request.project : null;
66
- record('requested', repo, null);
67
- const uncertain = (reason, message) => {
68
- const sessionId = 'sessionId' in validated.request ? validated.request.sessionId : undefined;
69
- return { version: 1, operation: spec.operation, ok: false, status: 'uncertain', delivery: 'unknown', retry: 'inspect-first', reason, message,
70
- nextActions: [{ operation: sessionId ? 'show' : 'list', label: sessionId ? 'Inspect session' : 'Inspect saved sessions', ...(sessionId ? { sessionId } : {}) }] };
71
- };
72
- let reply;
73
- try {
74
- reply = await service.execute({ name: principal.account, generation: principal.generation }, spec.operation, validated.request);
75
- if (!isSessionResponse(reply, spec.operation))
76
- throw new Error('Invalid owner response');
77
- if (Buffer.byteLength(JSON.stringify(reply)) > SESSION_RESPONSE_BYTES)
78
- reply = uncertain('response-too-large', 'The owner response is too large to deliver. Inspect saved activity before continuing; this request was not retried.');
79
- }
80
- catch {
81
- reply = uncertain('unconfirmed-response', 'The owner response could not be confirmed. Inspect saved activity before continuing; this request was not retried.');
82
- }
83
- record(reply.ok ? 'done' : reply.status === 'uncertain' ? 'failed' : 'refused', repo, reply.ok ? null : reply.reason ?? null);
84
- return answer(reply);
85
- };
86
- }
@@ -1,22 +0,0 @@
1
- import { CodingWorkspace, type CodingActor } from './coding-workspace.js';
2
- import { type SessionOperation, type SessionResponse } from './session-contract.js';
3
- type Options = {
4
- workspace: CodingWorkspace;
5
- /** Recheck the live account generation and installation-wide operator role. */
6
- authorized: (actor: CodingActor, repo?: string) => boolean;
7
- /** Resolve only an admitted project. Never accept an arbitrary filesystem path. */
8
- project: (actor: CodingActor, project: string) => string;
9
- };
10
- /** A narrow projection and operation boundary shared by authenticated adapters.
11
- * This object never opens a second catalog or starts a competing provider. */
12
- export declare class SessionService {
13
- private readonly options;
14
- constructor(options: Options);
15
- private authorize;
16
- private view;
17
- private next;
18
- private inspect;
19
- private result;
20
- execute<O extends SessionOperation>(actor: CodingActor, operation: O, value: unknown): Promise<SessionResponse>;
21
- }
22
- export {};
@@ -1,157 +0,0 @@
1
- import { realpathSync } from 'node:fs';
2
- import { CodingActionError, CodingWorkspace } from './coding-workspace.js';
3
- import { sessionDescriptor, validateSessionRequest } from './session-contract.js';
4
- import { savedSessionBrief } from './session-brief.js';
5
- const mutationMessages = { start: 'Session started.', send: 'Message delivered.', stop: 'Stop requested.', resume: 'Session reconnected.', recover: 'Saved activity reconciled. Inspect the current state before continuing.' };
6
- const expectedOf = (input) => ({ revision: input.expectedRevision, nativeThreadId: input.expectedThreadId, turnId: input.expectedTurnId });
7
- /** A narrow projection and operation boundary shared by authenticated adapters.
8
- * This object never opens a second catalog or starts a competing provider. */
9
- export class SessionService {
10
- options;
11
- constructor(options) {
12
- this.options = options;
13
- }
14
- authorize(actor, repo) {
15
- if (!this.options.authorized(actor, repo))
16
- throw new CodingActionError('This operation requires current operator access to the project.', 'rejected');
17
- }
18
- view(actor, id) {
19
- const session = this.options.workspace.get(id, actor);
20
- this.authorize(actor, session.repo);
21
- return { id: session.id, repo: session.repo, title: session.title, status: session.status,
22
- nativeThreadId: session.nativeThreadId, turnId: session.turnId,
23
- revision: this.options.workspace.revision(id, actor), branch: session.branch, base: session.base,
24
- error: session.error, deliveryReviewRequired: session.deliveryReviewRequired === true, updatedAt: session.updatedAt };
25
- }
26
- next(session) {
27
- if (!session)
28
- return [{ operation: 'list', label: 'Inspect sessions' }];
29
- const sessionId = session.id;
30
- const read = [{ operation: 'show', label: 'Inspect session', sessionId }, { operation: 'changes', label: 'Review changes', sessionId }];
31
- if (session.deliveryReviewRequired || session.status === 'needs-input')
32
- return [{ operation: 'open-ui', label: session.deliveryReviewRequired ? 'Review uncertain delivery' : 'Answer in the workspace', sessionId }, ...read];
33
- if (session.status === 'uncertain')
34
- return [{ operation: 'recover', label: 'Reconcile saved activity', sessionId }, ...read];
35
- if (session.status === 'interrupted')
36
- return [{ operation: 'resume', label: 'Reconnect session', sessionId }, ...read];
37
- if (session.status === 'ready')
38
- return [{ operation: 'send', label: 'Continue session', sessionId }, ...read];
39
- if (session.status === 'working')
40
- return [...read, { operation: 'send', label: 'Send a follow-up', sessionId }, { operation: 'stop', label: 'Stop this turn', sessionId }];
41
- return read;
42
- }
43
- inspect(session) {
44
- if (!session)
45
- return [{ operation: 'list', label: 'Inspect saved sessions' }];
46
- const sessionId = session.id;
47
- const next = [{ operation: 'show', label: 'Inspect saved session', sessionId }, { operation: 'changes', label: 'Review changes', sessionId }];
48
- if (session.deliveryReviewRequired || session.status === 'needs-input')
49
- next.push({ operation: 'open-ui', label: session.deliveryReviewRequired ? 'Review uncertain delivery' : 'Answer in the workspace', sessionId });
50
- else if (session.status === 'uncertain')
51
- next.push({ operation: 'recover', label: 'Reconcile saved activity', sessionId });
52
- return next;
53
- }
54
- result(actor, id, activity = false) {
55
- const snapshot = this.options.workspace.snapshotBounded(id, actor), session = this.view(actor, id);
56
- // Keep recent context and explicitly label omitted history. Native history
57
- // remains authoritative; this bounded transport is not a memory rewrite.
58
- let remaining = 1_000_000;
59
- const items = [];
60
- for (const item of snapshot.items.slice(-100).reverse()) {
61
- const bytes = Buffer.byteLength(JSON.stringify(item));
62
- if (bytes > remaining)
63
- break;
64
- items.unshift(item);
65
- remaining -= bytes;
66
- }
67
- const requests = snapshot.requests.slice(0, 50).map(({ id, kind, title, detail }) => ({ id, kind, title: title.slice(0, 1000), detail: detail.slice(0, 8000) }));
68
- const next = this.next(session)[0]?.label ?? 'Inspect session';
69
- const truncated = snapshot.truncated || items.length !== snapshot.items.length || requests.length !== snapshot.requests.length || snapshot.requests.some(request => request.title.length > 1000 || request.detail.length > 8000);
70
- return { session, ...(activity ? { items, requests } : {}), truncated, brief: savedSessionBrief(session, items, next, truncated) };
71
- }
72
- async execute(actor, operation, value) {
73
- const rejected = (message, reason) => ({ version: 1, operation, ok: false, status: 'rejected', delivery: 'not-sent', retry: 'never', message, reason, nextActions: [] });
74
- const validation = validateSessionRequest(operation, value);
75
- if (!validation.ok)
76
- return rejected(validation.message, 'invalid-request');
77
- const input = validation.request;
78
- const workspace = this.options.workspace;
79
- let deliveryMayHaveOccurred = false;
80
- try {
81
- this.authorize(actor);
82
- if (operation === 'list') {
83
- const request = input;
84
- const repo = request.project ? this.options.project(actor, request.project) : undefined;
85
- const page = workspace.listBounded(actor, { limit: request.limit ?? 50, ...(repo === undefined ? {} : { repo }), authorized: project => this.options.authorized(actor, project) });
86
- const sessions = page.sessions.map(s => this.view(actor, s.id));
87
- return { version: 1, operation, ok: true, status: 'succeeded', delivery: 'confirmed', retry: 'safe-read', message: `${sessions.length} sessions${page.truncated ? ' shown; more may be available' : ''}.`, nextActions: [], result: { sessions, truncated: page.truncated } };
88
- }
89
- if (operation === 'show' || operation === 'changes') {
90
- const { sessionId } = input;
91
- this.view(actor, sessionId);
92
- const changes = operation === 'changes' ? await workspace.changes(sessionId, actor) : undefined;
93
- const result = this.result(actor, sessionId, operation === 'show' && input.view === 'activity');
94
- if (changes)
95
- result.changes = changes;
96
- return { version: 1, operation, ok: true, status: 'succeeded', delivery: 'confirmed', retry: 'safe-read', message: 'Saved session loaded.', nextActions: this.next(result.session), result };
97
- }
98
- const request = input;
99
- let repo;
100
- if (operation === 'start') {
101
- repo = realpathSync(this.options.project(actor, request.project));
102
- this.authorize(actor, repo);
103
- }
104
- else
105
- this.view(actor, request.sessionId);
106
- // Stable field order makes a JSON field-order difference the same request.
107
- const fingerprint = JSON.stringify([operation, Object.keys(request).sort().map(k => [k, request[k]]), repo ?? null]);
108
- const receipt = await workspace.command(actor, request.key, fingerprint, () => this.authorize(actor, repo), async () => {
109
- const sessionId = operation === 'start' ? null : request.sessionId;
110
- try {
111
- this.authorize(actor, repo);
112
- if (sessionId) {
113
- this.view(actor, sessionId);
114
- workspace.assertExpected(sessionId, actor, expectedOf(request));
115
- }
116
- }
117
- catch (error) {
118
- throw error instanceof CodingActionError ? error : new CodingActionError(error instanceof Error ? error.message : 'This session is unavailable.', 'rejected', sessionId ?? undefined);
119
- }
120
- deliveryMayHaveOccurred = true;
121
- if (operation === 'start') {
122
- const start = request;
123
- return (await workspace.start(actor, { repo: repo, title: start.title, prompt: start.prompt, model: start.model ?? null, requestId: start.key })).id;
124
- }
125
- const expected = expectedOf(request);
126
- if (operation === 'send')
127
- await workspace.send(sessionId, actor, request.prompt, request.key, expected);
128
- else if (operation === 'stop')
129
- await workspace.stop(sessionId, actor, expected);
130
- else if (operation === 'resume')
131
- await workspace.resume(sessionId, actor, expected);
132
- else if (operation === 'recover')
133
- await workspace.recover(sessionId, actor, expected);
134
- return sessionId;
135
- });
136
- // A replay reads a prior receipt without performing again. Losing access
137
- // or failing to project that result cannot prove the original was unsent.
138
- deliveryMayHaveOccurred ||= receipt.status !== 'rejected';
139
- const id = receipt.sessionId ?? (operation === 'start' ? undefined : request.sessionId);
140
- const result = { ...(id ? this.result(actor, id) : {}), receipt: { key: request.key, status: receipt.status } };
141
- const status = receipt.status === 'accepted' ? 'succeeded' : receipt.status === 'pending' && result.session ? 'pending' : receipt.status === 'rejected' ? 'rejected' : 'uncertain';
142
- const nextActions = status === 'pending' || status === 'uncertain' ? this.inspect(result.session) : this.next(result.session);
143
- if (result.brief && (status === 'pending' || status === 'uncertain'))
144
- result.brief = { ...result.brief, nextAction: nextActions[0]?.label ?? 'Inspect saved session' };
145
- return { version: 1, operation, ok: status === 'succeeded' || status === 'pending', status,
146
- delivery: status === 'rejected' ? 'not-sent' : status === 'uncertain' ? 'unknown' : 'confirmed', retry: 'inspect-first',
147
- message: receipt.message ?? (status === 'succeeded' ? mutationMessages[operation] : 'Inspect saved activity before continuing. This request will not be sent again.'),
148
- nextActions, result };
149
- }
150
- catch (error) {
151
- const message = error instanceof Error ? error.message : 'The session operation could not finish.';
152
- if (deliveryMayHaveOccurred)
153
- return { version: 1, operation, ok: false, status: 'uncertain', delivery: 'unknown', retry: 'inspect-first', message, reason: 'unconfirmed-response', nextActions: [{ operation: 'list', label: 'Inspect saved sessions' }] };
154
- return rejected(message, sessionDescriptor(operation)?.mutation ? 'not-admitted' : 'unavailable');
155
- }
156
- }
157
- }