@wjarka/cezarion 0.14.8 → 0.14.9-dev.829

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 (207) hide show
  1. package/README.md +76 -2
  2. package/dist/application-update/bin-recovery.d.ts +5 -0
  3. package/dist/application-update/bin-recovery.js +96 -0
  4. package/dist/application-update/bin-recovery.js.map +1 -0
  5. package/dist/application-update/discovery.d.ts +22 -0
  6. package/dist/application-update/discovery.js +91 -0
  7. package/dist/application-update/discovery.js.map +1 -0
  8. package/dist/application-update/helper.d.ts +57 -0
  9. package/dist/application-update/helper.js +374 -0
  10. package/dist/application-update/helper.js.map +1 -0
  11. package/dist/application-update/launcher.d.ts +8 -0
  12. package/dist/application-update/launcher.js +53 -0
  13. package/dist/application-update/launcher.js.map +1 -0
  14. package/dist/application-update/lock.d.ts +4 -0
  15. package/dist/application-update/lock.js +70 -0
  16. package/dist/application-update/lock.js.map +1 -0
  17. package/dist/application-update/npm-process.d.ts +17 -0
  18. package/dist/application-update/npm-process.js +133 -0
  19. package/dist/application-update/npm-process.js.map +1 -0
  20. package/dist/application-update/service.d.ts +69 -0
  21. package/dist/application-update/service.js +456 -0
  22. package/dist/application-update/service.js.map +1 -0
  23. package/dist/artifacts/cli.d.ts +1 -0
  24. package/dist/artifacts/cli.js +32 -0
  25. package/dist/artifacts/cli.js.map +1 -0
  26. package/dist/artifacts/lifecycle.d.ts +4 -0
  27. package/dist/artifacts/lifecycle.js +42 -0
  28. package/dist/artifacts/lifecycle.js.map +1 -0
  29. package/dist/artifacts/resolve.d.ts +18 -0
  30. package/dist/artifacts/resolve.js +102 -0
  31. package/dist/artifacts/resolve.js.map +1 -0
  32. package/dist/artifacts/store.d.ts +8 -0
  33. package/dist/artifacts/store.js +323 -0
  34. package/dist/artifacts/store.js.map +1 -0
  35. package/dist/ci-wait/mcp.d.ts +3 -0
  36. package/dist/ci-wait/mcp.js +14 -3
  37. package/dist/ci-wait/mcp.js.map +1 -1
  38. package/dist/contract/application-update.d.ts +32 -0
  39. package/dist/contract/artifacts.d.ts +83 -0
  40. package/dist/contract/ask-schema.d.ts +32 -0
  41. package/dist/contract/ask.d.ts +2 -32
  42. package/dist/contract/conversations.d.ts +165 -0
  43. package/dist/contract/delegation.d.ts +39 -0
  44. package/dist/contract/events.d.ts +49 -0
  45. package/dist/contract/health.d.ts +12 -0
  46. package/dist/contract/index.d.ts +2 -0
  47. package/dist/contract/index.js +1374 -1247
  48. package/dist/contract/runs.d.ts +143 -0
  49. package/dist/contract/skills.d.ts +28 -0
  50. package/dist/core/agent-runner.d.ts +33 -4
  51. package/dist/core/agent-runner.js +5 -0
  52. package/dist/core/agent-runner.js.map +1 -1
  53. package/dist/core/claude-cli-runner.d.ts +6 -12
  54. package/dist/core/claude-cli-runner.js +89 -19
  55. package/dist/core/claude-cli-runner.js.map +1 -1
  56. package/dist/core/codex-app-server-runner.d.ts +2 -1
  57. package/dist/core/codex-app-server-runner.js +327 -92
  58. package/dist/core/codex-app-server-runner.js.map +1 -1
  59. package/dist/core/codex-app-server-transport.d.ts +13 -1
  60. package/dist/core/codex-app-server-transport.js +58 -17
  61. package/dist/core/codex-app-server-transport.js.map +1 -1
  62. package/dist/core/cursor-acp-runner.d.ts +4 -1
  63. package/dist/core/cursor-acp-runner.js +3 -0
  64. package/dist/core/cursor-acp-runner.js.map +1 -1
  65. package/dist/core/cursor-provider-error.js +3 -1
  66. package/dist/core/cursor-provider-error.js.map +1 -1
  67. package/dist/core/input-submissions.d.ts +14 -0
  68. package/dist/core/input-submissions.js +26 -0
  69. package/dist/core/input-submissions.js.map +1 -0
  70. package/dist/core/opencode-server-runner.d.ts +4 -1
  71. package/dist/core/opencode-server-runner.js +144 -21
  72. package/dist/core/opencode-server-runner.js.map +1 -1
  73. package/dist/core/pi-runner.d.ts +2 -1
  74. package/dist/core/pi-runner.js +72 -7
  75. package/dist/core/pi-runner.js.map +1 -1
  76. package/dist/delegation/cli.js +102 -6
  77. package/dist/delegation/cli.js.map +1 -1
  78. package/dist/delegation/conversations.js +13 -5
  79. package/dist/delegation/conversations.js.map +1 -1
  80. package/dist/delegation/input.d.ts +2 -1
  81. package/dist/delegation/input.js +6 -1
  82. package/dist/delegation/input.js.map +1 -1
  83. package/dist/delegation/provision.js +5 -3
  84. package/dist/delegation/provision.js.map +1 -1
  85. package/dist/delegation/questions.d.ts +19 -0
  86. package/dist/delegation/questions.js +45 -0
  87. package/dist/delegation/questions.js.map +1 -0
  88. package/dist/delegation/results.d.ts +1 -1
  89. package/dist/delegation/results.js +23 -13
  90. package/dist/delegation/results.js.map +1 -1
  91. package/dist/delegation/routes.d.ts +142 -15
  92. package/dist/delegation/routes.js +4 -1
  93. package/dist/delegation/routes.js.map +1 -1
  94. package/dist/delegation/service.d.ts +9 -5
  95. package/dist/delegation/service.js +106 -7
  96. package/dist/delegation/service.js.map +1 -1
  97. package/dist/delegation/transport.d.ts +138 -11
  98. package/dist/index.js +72 -26
  99. package/dist/index.js.map +1 -1
  100. package/dist/open-url.d.ts +2 -0
  101. package/dist/open-url.js +20 -0
  102. package/dist/open-url.js.map +1 -0
  103. package/dist/release/snapshot.d.ts +3 -3
  104. package/dist/release/snapshot.js +6 -6
  105. package/dist/release/snapshot.js.map +1 -1
  106. package/dist/runs/client-request.d.ts +11 -0
  107. package/dist/runs/client-request.js +26 -0
  108. package/dist/runs/client-request.js.map +1 -0
  109. package/dist/runs/delegation-state.d.ts +17 -0
  110. package/dist/runs/event-history.js +6 -0
  111. package/dist/runs/event-history.js.map +1 -1
  112. package/dist/runs/store.d.ts +78 -6
  113. package/dist/runs/store.js +163 -1
  114. package/dist/runs/store.js.map +1 -1
  115. package/dist/server/server.d.ts +3761 -1648
  116. package/dist/server/server.js +170 -4
  117. package/dist/server/server.js.map +1 -1
  118. package/dist/task-cli/cli.d.ts +38 -0
  119. package/dist/task-cli/cli.js +463 -0
  120. package/dist/task-cli/cli.js.map +1 -0
  121. package/dist/task-cli/discovery.d.ts +22 -0
  122. package/dist/task-cli/discovery.js +95 -0
  123. package/dist/task-cli/discovery.js.map +1 -0
  124. package/dist/task-cli/http.d.ts +43 -0
  125. package/dist/task-cli/http.js +101 -0
  126. package/dist/task-cli/http.js.map +1 -0
  127. package/dist/task-cli/projections.d.ts +40 -0
  128. package/dist/task-cli/projections.js +45 -0
  129. package/dist/task-cli/projections.js.map +1 -0
  130. package/dist/task-cli/watch.d.ts +78 -0
  131. package/dist/task-cli/watch.js +336 -0
  132. package/dist/task-cli/watch.js.map +1 -0
  133. package/dist/workflows/run.d.ts +93 -0
  134. package/dist/workflows/run.js +816 -81
  135. package/dist/workflows/run.js.map +1 -1
  136. package/dist/workspace/projects-cli.js +1 -1
  137. package/package.json +3 -3
  138. package/scripts/mock-claude.mjs +38 -2
  139. package/scripts/mock-codex-app-server.mjs +98 -1
  140. package/scripts/mock-cursor-acp.mjs +3 -0
  141. package/scripts/mock-opencode-serve.mjs +69 -2
  142. package/scripts/mock-pi-rpc.mjs +63 -2
  143. package/web/dist/assets/agent-accounts-JPfZPgKn.js +1 -0
  144. package/web/dist/assets/centered-state-HwCqQkH3.js +43 -0
  145. package/web/dist/assets/collapsible-CTymFdL7.js +1 -0
  146. package/web/dist/assets/commit-list-BZpYGQl2.js +1 -0
  147. package/web/dist/assets/compare-variants-9VkybaQ7.js +1 -0
  148. package/web/dist/assets/{diff-tRE59UNb2.js → diff-CZcD4D1r2.js} +2 -2
  149. package/web/dist/assets/diff-controls-CpcHG3Mm.js +1 -0
  150. package/web/dist/assets/{diff-stat-Cgnl_hYF.js → diff-stat-Cn7rzy1H.js} +1 -1
  151. package/web/dist/assets/{diff-view-DN1ort7Q.js → diff-view-C46nHiP2.js} +2 -2
  152. package/web/dist/assets/dropdown-menu-_YAzCaoQ.js +1 -0
  153. package/web/dist/assets/git-toolbar-BvKDz6uS.js +1 -0
  154. package/web/dist/assets/github-CgjJCGrD.js +1 -0
  155. package/web/dist/assets/{image-preview-C2JGcBlC.js → image-preview-B618kDBH.js} +1 -1
  156. package/web/dist/assets/index-BJtve0Pm.js +7 -0
  157. package/web/dist/assets/index-D0WAgmTQ.css +1 -0
  158. package/web/dist/assets/markdown-CFWZnvMh.js +2 -0
  159. package/web/dist/assets/pill-QfwkuR50.js +1 -0
  160. package/web/dist/assets/project-router-CkAA39N5.js +1 -0
  161. package/web/dist/assets/prompt-templates-Cfna_S0T.js +15 -0
  162. package/web/dist/assets/repo-git-DgzgJ0cP.js +1 -0
  163. package/web/dist/assets/{run-diff-CyG0Nciv.js → run-diff-YbCEQn5I.js} +2 -2
  164. package/web/dist/assets/run-header-DMgIbr1d.js +1 -0
  165. package/web/dist/assets/skills-BW79kUTd.js +1 -0
  166. package/web/dist/assets/{tab-link-BWNp3mLY.js → tab-link-MIHvczEm.js} +1 -1
  167. package/web/dist/assets/task-changes-C96ekt94.js +1 -0
  168. package/web/dist/assets/task-commits-DBfcMZyG.js +1 -0
  169. package/web/dist/assets/task-files-BKpH8dKQ.js +2 -0
  170. package/web/dist/assets/task-thread-Bw_nQuTx.css +1 -0
  171. package/web/dist/assets/task-thread-Dnq7Ek6W.js +9 -0
  172. package/web/dist/assets/textarea-D2vAVgYW.js +1 -0
  173. package/web/dist/assets/thread-loading-NW8Mthco.js +1 -0
  174. package/web/dist/assets/token-metrics-D188e6_0.js +1 -0
  175. package/web/dist/assets/utils-DKheTxGD.js +64 -0
  176. package/web/dist/assets/workflows-BFY1hgp3.js +11 -0
  177. package/web/dist/assets/{zoomable-image-Z3JDnm0b.js → zoomable-image-YW3Qdrr_.js} +1 -1
  178. package/web/dist/index.html +14 -14
  179. package/web/dist/assets/agent-accounts-BZm_2WAv.js +0 -1
  180. package/web/dist/assets/alert-dialog-B422RI-k.js +0 -43
  181. package/web/dist/assets/collapsible-Cy_Fmkrd.js +0 -1
  182. package/web/dist/assets/commit-list-Dcnk7TnI.js +0 -1
  183. package/web/dist/assets/compare-variants-Ciqwvd_f.js +0 -1
  184. package/web/dist/assets/diff-controls-BGrXGi7O.js +0 -1
  185. package/web/dist/assets/dropdown-menu-dwDGT8MM.js +0 -1
  186. package/web/dist/assets/git-toolbar-UiHnGvE0.js +0 -1
  187. package/web/dist/assets/github-D-MrkbJ7.js +0 -1
  188. package/web/dist/assets/ibm-plex-mono-regular-BF3vfe7_.ttf +0 -0
  189. package/web/dist/assets/index-BszfYvBq.js +0 -7
  190. package/web/dist/assets/index-CWh4pKP3.css +0 -1
  191. package/web/dist/assets/markdown-C_IcKVQd.js +0 -2
  192. package/web/dist/assets/pill-BsbO6G7y.js +0 -1
  193. package/web/dist/assets/project-router-DbLkll25.js +0 -1
  194. package/web/dist/assets/prompt-templates-BlTGJB5W.js +0 -15
  195. package/web/dist/assets/repo-git-Bm3s6Why.js +0 -1
  196. package/web/dist/assets/run-header-IFyIGCey.js +0 -1
  197. package/web/dist/assets/skills-C-C4DVFY.js +0 -1
  198. package/web/dist/assets/task-changes-D2PHtWUW.js +0 -1
  199. package/web/dist/assets/task-commits-N5u9U0_R.js +0 -1
  200. package/web/dist/assets/task-files-BS7c7CFm.js +0 -2
  201. package/web/dist/assets/task-thread-BQFdz1dJ.css +0 -1
  202. package/web/dist/assets/task-thread-DGM2dZV-.js +0 -9
  203. package/web/dist/assets/textarea-FjaU370i.js +0 -1
  204. package/web/dist/assets/thread-loading-77sJ8Yu_.js +0 -1
  205. package/web/dist/assets/token-metrics-Clxh03bE.js +0 -1
  206. package/web/dist/assets/utils-PYYcpCFZ.js +0 -64
  207. package/web/dist/assets/workflows-Czl0ZfXH.js +0 -11
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/task-cli/discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The loopback HTTP half of `cez task` (#504, spec 2026-09-24-cez-task-cli): plain `fetch`
3
+ * against the cockpit's own `/api/v1/p/:projectId` routes, with request deadlines.
4
+ *
5
+ * No credential is read, sent or printed: the cockpit's origin guard (#426) admits a loopback
6
+ * `Host` with no `Origin`, which is exactly what `fetch` sends.
7
+ */
8
+ export declare const REQUEST_TIMEOUT_MS = 45000;
9
+ /** Every failure carries its exit code and the JSON body the CLI prints. */
10
+ export declare class TaskCliError extends Error {
11
+ readonly exitCode: number;
12
+ readonly body: Record<string, unknown>;
13
+ constructor(exitCode: number, body: Record<string, unknown>);
14
+ }
15
+ export interface Cockpit {
16
+ /** `http://127.0.0.1:<port>` — no trailing slash. */
17
+ origin: string;
18
+ projectId: string;
19
+ /** `${origin}/api/v1/p/${projectId}` */
20
+ api: string;
21
+ }
22
+ export declare function threadUrl(cockpit: Cockpit, runId: string): string;
23
+ export interface HttpResult {
24
+ status: number;
25
+ data: unknown;
26
+ }
27
+ /** One request to an absolute URL. JSON bodies are parsed; anything else comes back as text. */
28
+ export declare function fetchJson(url: string, init?: {
29
+ method?: string;
30
+ body?: unknown;
31
+ timeoutMs?: number;
32
+ responseLimitBytes?: number;
33
+ }): Promise<HttpResult>;
34
+ /** A project-scoped route: `path` starts with `/`, relative to `/api/v1/p/:projectId`. */
35
+ export declare function request(cockpit: Cockpit, path: string, init?: {
36
+ method?: string;
37
+ body?: unknown;
38
+ timeoutMs?: number;
39
+ }): Promise<HttpResult>;
40
+ /** The cockpit said no: pass its `{ error }` through verbatim, exit 2. */
41
+ export declare function refuse(result: HttpResult): never;
42
+ /** A response the contract does not describe is a cockpit problem, not a caller problem. */
43
+ export declare function invalidResponse(what: string): never;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * The loopback HTTP half of `cez task` (#504, spec 2026-09-24-cez-task-cli): plain `fetch`
3
+ * against the cockpit's own `/api/v1/p/:projectId` routes, with request deadlines.
4
+ *
5
+ * No credential is read, sent or printed: the cockpit's origin guard (#426) admits a loopback
6
+ * `Host` with no `Origin`, which is exactly what `fetch` sends.
7
+ */
8
+ const RESPONSE_BYTES = 3_145_728;
9
+ export const REQUEST_TIMEOUT_MS = 45_000;
10
+ /** Every failure carries its exit code and the JSON body the CLI prints. */
11
+ export class TaskCliError extends Error {
12
+ exitCode;
13
+ body;
14
+ constructor(exitCode, body) {
15
+ super(typeof body.error === 'string' ? body.error : 'cez task failed');
16
+ this.exitCode = exitCode;
17
+ this.body = body;
18
+ }
19
+ }
20
+ export function threadUrl(cockpit, runId) {
21
+ return `${cockpit.origin}/p/${encodeURIComponent(cockpit.projectId)}/tasks/${encodeURIComponent(runId)}`;
22
+ }
23
+ /** Enforce a byte limit where the route has a bounded response, including error bodies. */
24
+ async function boundedText(response, limit) {
25
+ if (Number(response.headers.get('content-length')) > limit) {
26
+ await response.body?.cancel();
27
+ throw new Error('Response too large');
28
+ }
29
+ const reader = response.body?.getReader();
30
+ if (!reader)
31
+ return '';
32
+ let size = 0;
33
+ const chunks = [];
34
+ try {
35
+ for (;;) {
36
+ const next = await reader.read();
37
+ if (next.done)
38
+ break;
39
+ size += next.value.length;
40
+ if (size > limit) {
41
+ await reader.cancel();
42
+ throw new Error('Response too large');
43
+ }
44
+ chunks.push(next.value);
45
+ }
46
+ }
47
+ finally {
48
+ reader.releaseLock();
49
+ }
50
+ return Buffer.concat(chunks, size).toString('utf8');
51
+ }
52
+ /** One request to an absolute URL. JSON bodies are parsed; anything else comes back as text. */
53
+ export async function fetchJson(url, init = {}) {
54
+ let response;
55
+ let text;
56
+ try {
57
+ response = await fetch(url, {
58
+ method: init.method ?? (init.body === undefined ? 'GET' : 'POST'),
59
+ redirect: 'error',
60
+ headers: init.body === undefined ? {} : { 'content-type': 'application/json' },
61
+ ...(init.body === undefined ? {} : { body: JSON.stringify(init.body) }),
62
+ signal: AbortSignal.timeout(init.timeoutMs ?? REQUEST_TIMEOUT_MS),
63
+ });
64
+ text = await boundedText(response, response.ok ? (init.responseLimitBytes ?? RESPONSE_BYTES) : RESPONSE_BYTES);
65
+ }
66
+ catch (error) {
67
+ throw new TaskCliError(2, { code: 'unavailable', error: `cockpit request failed: ${error instanceof Error ? error.message : String(error)}` });
68
+ }
69
+ const json = (response.headers.get('content-type') ?? '').includes('application/json');
70
+ if (!json)
71
+ return { status: response.status, data: text };
72
+ try {
73
+ return { status: response.status, data: JSON.parse(text) };
74
+ }
75
+ catch {
76
+ throw new TaskCliError(2, { code: 'unavailable', error: 'cockpit returned invalid JSON' });
77
+ }
78
+ }
79
+ /** A project-scoped route: `path` starts with `/`, relative to `/api/v1/p/:projectId`. */
80
+ export function request(cockpit, path, init = {}) {
81
+ // GET /runs is the existing unpaginated full history. Even 32 valid 100k-character tasks
82
+ // exceed the ordinary cap. Match the cockpit's full-list read rather than making list/wait
83
+ // fail as history grows; retain deadlines, error-body caps and every other route's cap.
84
+ const fullRunList = path === '/runs' && (init.method ?? (init.body === undefined ? 'GET' : 'POST')) === 'GET';
85
+ return fetchJson(`${cockpit.api}${path}`, {
86
+ ...init,
87
+ ...(fullRunList ? { responseLimitBytes: Infinity } : {}),
88
+ });
89
+ }
90
+ /** The cockpit said no: pass its `{ error }` through verbatim, exit 2. */
91
+ export function refuse(result) {
92
+ const error = result.data && typeof result.data === 'object' && typeof result.data.error === 'string'
93
+ ? result.data.error
94
+ : `cockpit answered HTTP ${result.status}`;
95
+ throw new TaskCliError(2, { code: 'refused', status: result.status, error });
96
+ }
97
+ /** A response the contract does not describe is a cockpit problem, not a caller problem. */
98
+ export function invalidResponse(what) {
99
+ throw new TaskCliError(2, { code: 'unavailable', error: `cockpit returned an unexpected ${what} response` });
100
+ }
101
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/task-cli/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { ApiRun, RunStatus } from '../contract/index.js';
2
+ /**
3
+ * The slim shapes `cez task` prints (#504). A bot polls these every few seconds, so each one
4
+ * carries what a caller branches on and nothing it would have to pay tokens to skip: no
5
+ * `steps[]`, no `workflowDef`, no reference candidates. `--full` is the escape hatch.
6
+ */
7
+ export declare const TERMINAL_STATUSES: readonly RunStatus[];
8
+ export declare const SUCCESS_STATUSES: readonly RunStatus[];
9
+ export declare function runTitle(run: Pick<ApiRun, 'title' | 'titleSummary'>): string;
10
+ export declare function projectStatus(run: ApiRun, url: string, question?: unknown): Partial<{
11
+ id: string;
12
+ title: string;
13
+ status: "cancelled" | "done" | "failed" | "queued" | "review" | "running" | "waiting";
14
+ activity: "monitoring" | undefined;
15
+ currentStepId: string | undefined;
16
+ hasPendingHumanAsk: boolean;
17
+ question: unknown;
18
+ branch: string | undefined;
19
+ diffStat: {
20
+ adds: number;
21
+ dels: number;
22
+ files: number;
23
+ repointed?: boolean | undefined;
24
+ } | undefined;
25
+ pullRequestUrl: string | undefined;
26
+ error: string | undefined;
27
+ tokensUsed: number;
28
+ startedAt: string | undefined;
29
+ finishedAt: string | undefined;
30
+ url: string;
31
+ }>;
32
+ /** The record carries no `updatedAt`; the newest lifecycle stamp is the honest stand-in. */
33
+ export declare function projectListRow(run: ApiRun): Partial<{
34
+ id: string;
35
+ title: string;
36
+ status: "cancelled" | "done" | "failed" | "queued" | "review" | "running" | "waiting";
37
+ activity: "monitoring" | undefined;
38
+ hasPendingHumanAsk: boolean;
39
+ updatedAt: string;
40
+ }>;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The slim shapes `cez task` prints (#504). A bot polls these every few seconds, so each one
3
+ * carries what a caller branches on and nothing it would have to pay tokens to skip: no
4
+ * `steps[]`, no `workflowDef`, no reference candidates. `--full` is the escape hatch.
5
+ */
6
+ export const TERMINAL_STATUSES = ['done', 'review', 'failed', 'cancelled'];
7
+ export const SUCCESS_STATUSES = ['done', 'review'];
8
+ /** Keys with an undefined value are left off, so the JSON stays as small as the run is. */
9
+ function defined(value) {
10
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
11
+ }
12
+ export function runTitle(run) {
13
+ return run.titleSummary ?? run.title;
14
+ }
15
+ export function projectStatus(run, url, question) {
16
+ return defined({
17
+ id: run.id,
18
+ title: runTitle(run),
19
+ status: run.status,
20
+ activity: run.activity,
21
+ currentStepId: run.currentStepId,
22
+ hasPendingHumanAsk: run.hasPendingHumanAsk ?? false,
23
+ question,
24
+ branch: run.branch,
25
+ diffStat: run.diffStat,
26
+ pullRequestUrl: run.pullRequestUrl,
27
+ error: run.error,
28
+ tokensUsed: run.tokensUsed,
29
+ startedAt: run.startedAt,
30
+ finishedAt: run.finishedAt,
31
+ url,
32
+ });
33
+ }
34
+ /** The record carries no `updatedAt`; the newest lifecycle stamp is the honest stand-in. */
35
+ export function projectListRow(run) {
36
+ return defined({
37
+ id: run.id,
38
+ title: runTitle(run),
39
+ status: run.status,
40
+ activity: run.activity,
41
+ hasPendingHumanAsk: run.hasPendingHumanAsk ?? false,
42
+ updatedAt: run.finishedAt ?? run.startedAt ?? run.createdAt,
43
+ });
44
+ }
45
+ //# sourceMappingURL=projections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projections.js","sourceRoot":"","sources":["../../src/task-cli/projections.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAyB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AACjG,MAAM,CAAC,MAAM,gBAAgB,GAAyB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAEzE,2FAA2F;AAC3F,SAAS,OAAO,CAAoC,KAAQ;IAC1D,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAe,CAAC;AAC5G,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAA2C;IAClE,OAAO,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,KAAK,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,GAAW,EAAE,QAAkB;IACxE,OAAO,OAAO,CAAC;QACb,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC;QACpB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,kBAAkB,EAAE,GAAG,CAAC,kBAAkB,IAAI,KAAK;QACnD,QAAQ;QACR,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,GAAG;KACJ,CAAC,CAAC;AACL,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,OAAO,OAAO,CAAC;QACb,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC;QACpB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,kBAAkB,EAAE,GAAG,CAAC,kBAAkB,IAAI,KAAK;QACnD,SAAS,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS;KAC5D,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,78 @@
1
+ import { type RunHistoryEvent, type RunStatus } from '../contract/index.js';
2
+ import { type Cockpit } from './http.ts';
3
+ /**
4
+ * Watching a task from a terminal (#504, spec 2026-09-24-cez-task-cli). Every loop here ends —
5
+ * on a terminal status or at the caller's deadline — because a bot's tool call that never
6
+ * returns is the failure this command family exists to prevent.
7
+ */
8
+ export declare const DEFAULT_POLL_MS = 1500;
9
+ export type WaitMode = 'any' | 'all';
10
+ export type WaitUntil = 'settled' | 'attention';
11
+ export interface WaitEntry {
12
+ id: string;
13
+ /** `unknown`: the deadline passed before any poll answered. */
14
+ status: RunStatus | 'missing' | 'unknown';
15
+ activity?: string;
16
+ hasPendingHumanAsk?: boolean;
17
+ }
18
+ export interface WaitResult {
19
+ exitCode: number;
20
+ runs: WaitEntry[];
21
+ timedOut: boolean;
22
+ }
23
+ /**
24
+ * Grace for the millisecond race between a budget-bounded poll's abort and the
25
+ * deadline check (#538): `AbortSignal.timeout(budget)` can fire while
26
+ * `Date.now()` still reads just under the deadline, turning a timeout into an
27
+ * `unavailable` error (exit 2 instead of 3). A poll whose own abort consumed
28
+ * (almost) all of its budget failed *at* the deadline, so it answers "timed
29
+ * out". The grace never applies to other errors: a genuine failure stays
30
+ * `unavailable` even when it lands inside the grace window.
31
+ */
32
+ export declare const TIMEOUT_GRACE_MS = 250;
33
+ export declare function pollHitDeadline(startedAt: number, budgetMs: number, now?: number): boolean;
34
+ /**
35
+ * True when the wrapped cockpit error came from the poll's own abort (its
36
+ * budget running out) rather than a fast failure such as a refused
37
+ * connection. `fetchJson` folds every fetch failure into `unavailable`, so the
38
+ * abort surfaces only through the original message it preserves (`TimeoutError:
39
+ * The operation was aborted due to timeout`).
40
+ */
41
+ export declare function abortedPoll(error: unknown): boolean;
42
+ /**
43
+ * Polls `GET /runs`: one call covers any number of runs and holds no socket open. Each poll is
44
+ * bounded by what is left of the deadline, so a slow cockpit answers "timed out" on time rather
45
+ * than holding the caller for a full request timeout.
46
+ */
47
+ export declare function waitForRuns(cockpit: Cockpit, ids: string[], options: {
48
+ mode: WaitMode;
49
+ until: WaitUntil;
50
+ timeoutMs: number;
51
+ pollMs?: number;
52
+ }): Promise<WaitResult>;
53
+ /** One printable line per event the log keeps, or undefined for the rest. */
54
+ export declare function logLine(event: RunHistoryEvent): Record<string, unknown> | undefined;
55
+ /**
56
+ * `log`, read from the run's SSE stream, which replays every event after `afterSeq` and then
57
+ * streams live. The route subscribes to live `run` updates BEFORE it replays, so a `run` frame
58
+ * says nothing about where the replay is; the only reliable boundary is a `seq`. So:
59
+ *
60
+ * - the replay ends at `asOfSeq`, the event file's high-water mark read from `GET /history` just
61
+ * before connecting — everything up to it is the tail, bounded to `maxChars`, and without
62
+ * `follow` the command ends there;
63
+ * - with `follow`, a terminal status ends the command only once the stream has caught up with a
64
+ * SECOND high-water read taken after that status arrived, so events the engine wrote just
65
+ * before finishing are not dropped when the status frame overtakes them (exit 0/1, final line
66
+ * is the status).
67
+ *
68
+ * One `deadline` bounds every request, the history reads included (exit 3). Deduped by `seq`.
69
+ * The stream rather than `GET /history` pages: a history page starts at its first transcript
70
+ * item, so it drops lifecycle events such as the opening `step-start`.
71
+ */
72
+ export declare function readLog(cockpit: Cockpit, id: string, options: {
73
+ afterSeq: number;
74
+ maxChars: number;
75
+ follow: boolean;
76
+ deadline: number;
77
+ print: (line: string) => void;
78
+ }): Promise<number>;
@@ -0,0 +1,336 @@
1
+ import { apiRunSchema, runHistoryPageSchema, runRecordSchema, } from '../contract/index.js';
2
+ import { invalidResponse, refuse, request, TaskCliError } from './http.js';
3
+ import { SUCCESS_STATUSES, TERMINAL_STATUSES } from './projections.js';
4
+ /**
5
+ * Watching a task from a terminal (#504, spec 2026-09-24-cez-task-cli). Every loop here ends —
6
+ * on a terminal status or at the caller's deadline — because a bot's tool call that never
7
+ * returns is the failure this command family exists to prevent.
8
+ */
9
+ export const DEFAULT_POLL_MS = 1_500;
10
+ const MAX_FIELD_CHARS = 2_000;
11
+ function entryFor(id, run) {
12
+ if (!run)
13
+ return { id, status: 'missing' };
14
+ return {
15
+ id,
16
+ status: run.status,
17
+ ...(run.activity === undefined ? {} : { activity: run.activity }),
18
+ hasPendingHumanAsk: run.hasPendingHumanAsk ?? false,
19
+ };
20
+ }
21
+ /** Settled = nothing more will happen without a human; `missing` counts, it will never change. */
22
+ function isSettled(entry, until) {
23
+ if (entry.status === 'unknown')
24
+ return false;
25
+ if (entry.status === 'missing' || TERMINAL_STATUSES.includes(entry.status))
26
+ return true;
27
+ return until === 'attention' && (entry.status === 'waiting' || entry.hasPendingHumanAsk === true);
28
+ }
29
+ /** A failure is only a terminal non-success; stopping for attention is not one. */
30
+ function isFailure(entry) {
31
+ return entry.status === 'missing' || entry.status === 'failed' || entry.status === 'cancelled';
32
+ }
33
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
34
+ /**
35
+ * Grace for the millisecond race between a budget-bounded poll's abort and the
36
+ * deadline check (#538): `AbortSignal.timeout(budget)` can fire while
37
+ * `Date.now()` still reads just under the deadline, turning a timeout into an
38
+ * `unavailable` error (exit 2 instead of 3). A poll whose own abort consumed
39
+ * (almost) all of its budget failed *at* the deadline, so it answers "timed
40
+ * out". The grace never applies to other errors: a genuine failure stays
41
+ * `unavailable` even when it lands inside the grace window.
42
+ */
43
+ export const TIMEOUT_GRACE_MS = 250;
44
+ export function pollHitDeadline(startedAt, budgetMs, now = Date.now()) {
45
+ return now - startedAt >= budgetMs - TIMEOUT_GRACE_MS;
46
+ }
47
+ /**
48
+ * True when the wrapped cockpit error came from the poll's own abort (its
49
+ * budget running out) rather than a fast failure such as a refused
50
+ * connection. `fetchJson` folds every fetch failure into `unavailable`, so the
51
+ * abort surfaces only through the original message it preserves (`TimeoutError:
52
+ * The operation was aborted due to timeout`).
53
+ */
54
+ export function abortedPoll(error) {
55
+ if (!(error instanceof TaskCliError))
56
+ return false;
57
+ const body = error.body;
58
+ return /abort|timeout/i.test(String(body.error ?? ''));
59
+ }
60
+ /**
61
+ * Polls `GET /runs`: one call covers any number of runs and holds no socket open. Each poll is
62
+ * bounded by what is left of the deadline, so a slow cockpit answers "timed out" on time rather
63
+ * than holding the caller for a full request timeout.
64
+ */
65
+ export async function waitForRuns(cockpit, ids, options) {
66
+ const deadline = Date.now() + options.timeoutMs;
67
+ let entries = ids.map((id) => ({ id, status: 'unknown' }));
68
+ for (;;) {
69
+ const budget = deadline - Date.now();
70
+ if (budget <= 0)
71
+ return { exitCode: 3, runs: entries, timedOut: true };
72
+ const pollStart = Date.now();
73
+ let result;
74
+ try {
75
+ result = await request(cockpit, '/runs', { timeoutMs: budget });
76
+ }
77
+ catch (error) {
78
+ if (Date.now() >= deadline)
79
+ return { exitCode: 3, runs: entries, timedOut: true };
80
+ if (abortedPoll(error) && pollHitDeadline(pollStart, budget))
81
+ return { exitCode: 3, runs: entries, timedOut: true };
82
+ throw error;
83
+ }
84
+ if (result.status !== 200)
85
+ refuse(result);
86
+ const runs = apiRunSchema.array().safeParse(result.data);
87
+ if (!runs.success)
88
+ invalidResponse('run list');
89
+ const byId = new Map(runs.data.map((run) => [run.id, run]));
90
+ entries = ids.map((id) => entryFor(id, byId.get(id)));
91
+ const settled = entries.filter((entry) => isSettled(entry, options.until));
92
+ const done = options.mode === 'any' ? settled.length > 0 : settled.length === entries.length;
93
+ if (done) {
94
+ const judged = options.mode === 'any' ? settled : entries;
95
+ return { exitCode: judged.some(isFailure) && (options.mode === 'all' || judged.every(isFailure)) ? 1 : 0, runs: entries, timedOut: false };
96
+ }
97
+ const remaining = deadline - Date.now();
98
+ if (remaining <= 0)
99
+ return { exitCode: 3, runs: entries, timedOut: true };
100
+ await sleep(Math.min(options.pollMs ?? DEFAULT_POLL_MS, remaining));
101
+ }
102
+ }
103
+ const LOG_TYPES = new Set(['text', 'tool-call', 'tool-result', 'step-start', 'error', 'user-message']);
104
+ function clip(value) {
105
+ if (value === undefined)
106
+ return undefined;
107
+ const text = typeof value === 'string' ? value : JSON.stringify(value);
108
+ return text.length > MAX_FIELD_CHARS ? `${text.slice(0, MAX_FIELD_CHARS - 1)}…` : text;
109
+ }
110
+ /** One printable line per event the log keeps, or undefined for the rest. */
111
+ export function logLine(event) {
112
+ if (!LOG_TYPES.has(event.type))
113
+ return undefined;
114
+ const fields = {
115
+ seq: event.seq,
116
+ ts: event.ts,
117
+ type: event.type,
118
+ ...(event.stepId === undefined ? {} : { stepId: event.stepId }),
119
+ };
120
+ switch (event.type) {
121
+ case 'text':
122
+ case 'user-message':
123
+ fields.text = clip(event.text);
124
+ break;
125
+ case 'tool-call':
126
+ fields.tool = event.tool;
127
+ fields.input = clip(event.input);
128
+ break;
129
+ case 'tool-result':
130
+ fields.result = clip(event.result);
131
+ break;
132
+ case 'step-start':
133
+ fields.name = event.name;
134
+ if (event.iteration !== undefined)
135
+ fields.iteration = event.iteration;
136
+ break;
137
+ case 'error':
138
+ fields.message = clip(event.message);
139
+ break;
140
+ }
141
+ return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
142
+ }
143
+ /** Keeps the newest lines within `maxChars` (counting the newline between lines). */
144
+ class LineTail {
145
+ maxChars;
146
+ lines = [];
147
+ size = 0;
148
+ constructor(maxChars) {
149
+ this.maxChars = maxChars;
150
+ }
151
+ push(line) {
152
+ this.lines.push(line);
153
+ this.size += line.length + (this.lines.length > 1 ? 1 : 0);
154
+ while (this.size > this.maxChars && this.lines.length) {
155
+ const dropped = this.lines.shift();
156
+ this.size -= dropped.length + (this.lines.length ? 1 : 0);
157
+ }
158
+ }
159
+ drain() {
160
+ const lines = this.lines;
161
+ this.lines = [];
162
+ this.size = 0;
163
+ return lines;
164
+ }
165
+ }
166
+ /** Minimal SSE framing: `event:` / `data:` fields, blank-line separated. */
167
+ async function* sseFrames(body) {
168
+ const decoder = new TextDecoder();
169
+ let buffer = '';
170
+ for await (const chunk of body) {
171
+ buffer += decoder.decode(chunk, { stream: true }).replace(/\r\n/g, '\n');
172
+ let boundary;
173
+ while ((boundary = buffer.indexOf('\n\n')) >= 0) {
174
+ const block = buffer.slice(0, boundary);
175
+ buffer = buffer.slice(boundary + 2);
176
+ let event = 'message';
177
+ const data = [];
178
+ for (const line of block.split('\n')) {
179
+ if (line.startsWith('event:'))
180
+ event = line.slice(6).trim();
181
+ else if (line.startsWith('data:'))
182
+ data.push(line.slice(5).replace(/^ /, ''));
183
+ }
184
+ yield { event, data: data.join('\n') };
185
+ }
186
+ }
187
+ }
188
+ /**
189
+ * `log`, read from the run's SSE stream, which replays every event after `afterSeq` and then
190
+ * streams live. The route subscribes to live `run` updates BEFORE it replays, so a `run` frame
191
+ * says nothing about where the replay is; the only reliable boundary is a `seq`. So:
192
+ *
193
+ * - the replay ends at `asOfSeq`, the event file's high-water mark read from `GET /history` just
194
+ * before connecting — everything up to it is the tail, bounded to `maxChars`, and without
195
+ * `follow` the command ends there;
196
+ * - with `follow`, a terminal status ends the command only once the stream has caught up with a
197
+ * SECOND high-water read taken after that status arrived, so events the engine wrote just
198
+ * before finishing are not dropped when the status frame overtakes them (exit 0/1, final line
199
+ * is the status).
200
+ *
201
+ * One `deadline` bounds every request, the history reads included (exit 3). Deduped by `seq`.
202
+ * The stream rather than `GET /history` pages: a history page starts at its first transcript
203
+ * item, so it drops lifecycle events such as the opening `step-start`.
204
+ */
205
+ export async function readLog(cockpit, id, options) {
206
+ const tail = new LineTail(options.maxChars);
207
+ const remaining = () => options.deadline - Date.now();
208
+ let lastSeq = options.afterSeq;
209
+ let seenSeq = options.afterSeq;
210
+ let status;
211
+ let drainSeq;
212
+ /** The event file's high-water mark, or undefined once the deadline has passed. */
213
+ const highWater = async () => {
214
+ const budget = remaining();
215
+ if (budget <= 0)
216
+ return undefined;
217
+ const startedAt = Date.now();
218
+ let history;
219
+ try {
220
+ history = await request(cockpit, `/runs/${encodeURIComponent(id)}/history`, { timeoutMs: budget });
221
+ }
222
+ catch (error) {
223
+ if (remaining() <= 0)
224
+ return undefined;
225
+ if (abortedPoll(error) && pollHitDeadline(startedAt, budget))
226
+ return undefined;
227
+ throw error;
228
+ }
229
+ if (history.status !== 200)
230
+ refuse(history);
231
+ const page = runHistoryPageSchema.safeParse(history.data);
232
+ return page.success ? page.data.asOfSeq : invalidResponse('history');
233
+ };
234
+ const boundarySeq = await highWater();
235
+ if (boundarySeq === undefined)
236
+ return timedOut();
237
+ let replaying = boundarySeq > options.afterSeq;
238
+ if (!replaying && !options.follow)
239
+ return 0;
240
+ const controller = new AbortController();
241
+ const timer = setTimeout(() => controller.abort(), Math.max(remaining(), 0));
242
+ try {
243
+ let response;
244
+ try {
245
+ response = await fetch(`${cockpit.api}/runs/${encodeURIComponent(id)}/events?afterSeq=${lastSeq}`, {
246
+ redirect: 'error', signal: controller.signal, headers: { accept: 'text/event-stream' },
247
+ });
248
+ }
249
+ catch (error) {
250
+ if (controller.signal.aborted)
251
+ return timedOut();
252
+ throw new TaskCliError(2, { code: 'unavailable', error: `cockpit request failed: ${error instanceof Error ? error.message : String(error)}` });
253
+ }
254
+ if (!response.ok || !response.body) {
255
+ const text = await response.text().catch(() => '');
256
+ let data = text;
257
+ try {
258
+ data = JSON.parse(text);
259
+ }
260
+ catch { /* not JSON */ }
261
+ refuse({ status: response.status, data });
262
+ }
263
+ try {
264
+ for await (const frame of sseFrames(response.body)) {
265
+ if (frame.event === 'run-event' || frame.event === 'ui-event') {
266
+ const event = JSON.parse(frame.data);
267
+ if (typeof event.seq !== 'number')
268
+ continue;
269
+ // Both streams share the file's seq space, so either one can reach a boundary;
270
+ // only v1 lines are printed, deduped by seq.
271
+ seenSeq = Math.max(seenSeq, event.seq);
272
+ if (frame.event === 'run-event' && event.seq > lastSeq) {
273
+ lastSeq = event.seq;
274
+ const line = logLine(event);
275
+ if (line) {
276
+ if (replaying)
277
+ tail.push(JSON.stringify(line));
278
+ else
279
+ options.print(JSON.stringify(line));
280
+ }
281
+ }
282
+ }
283
+ else if (frame.event === 'run') {
284
+ const run = runRecordSchema.safeParse(JSON.parse(frame.data));
285
+ if (run.success)
286
+ status = run.data.status;
287
+ }
288
+ let exit = step();
289
+ if (exit === 'drain') {
290
+ drainSeq = await highWater();
291
+ if (drainSeq === undefined)
292
+ return timedOut();
293
+ exit = step();
294
+ }
295
+ if (typeof exit === 'number')
296
+ return exit;
297
+ }
298
+ }
299
+ catch (error) {
300
+ if (!controller.signal.aborted)
301
+ throw error;
302
+ }
303
+ if (controller.signal.aborted)
304
+ return timedOut();
305
+ throw new TaskCliError(2, { code: 'unavailable', error: 'the cockpit closed the event stream' });
306
+ }
307
+ finally {
308
+ clearTimeout(timer);
309
+ controller.abort();
310
+ }
311
+ /** Ends the replay at its boundary; then a terminal status ends the command once drained. */
312
+ function step() {
313
+ if (replaying && seenSeq >= boundarySeq) {
314
+ replaying = false;
315
+ for (const line of tail.drain())
316
+ options.print(line);
317
+ if (!options.follow)
318
+ return 0;
319
+ }
320
+ if (replaying || status === undefined || !TERMINAL_STATUSES.includes(status))
321
+ return undefined;
322
+ if (drainSeq === undefined)
323
+ return 'drain';
324
+ if (seenSeq < drainSeq)
325
+ return undefined;
326
+ options.print(JSON.stringify({ id, status, timedOut: false }));
327
+ return SUCCESS_STATUSES.includes(status) ? 0 : 1;
328
+ }
329
+ function timedOut() {
330
+ for (const line of tail.drain())
331
+ options.print(line);
332
+ options.print(JSON.stringify({ id, ...(status === undefined ? {} : { status }), timedOut: true }));
333
+ return 3;
334
+ }
335
+ }
336
+ //# sourceMappingURL=watch.js.map