@open-mercato/cezar 0.9.2-pr774.1069 → 0.9.2-pr776.1115

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 (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -20
  3. package/dist/contract/index.js +64 -18
  4. package/dist/contract/projects.d.ts +0 -4
  5. package/dist/contract/runs.d.ts +86 -0
  6. package/dist/contract/workspace.d.ts +18 -0
  7. package/dist/git-diff-base.d.ts +51 -26
  8. package/dist/git-diff-base.js +163 -37
  9. package/dist/git-diff-base.js.map +1 -1
  10. package/dist/git-worktree.d.ts +15 -6
  11. package/dist/git-worktree.js +10 -3
  12. package/dist/git-worktree.js.map +1 -1
  13. package/dist/index.js +30 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/runs/run-index.d.ts +19 -0
  16. package/dist/runs/run-index.js +43 -0
  17. package/dist/runs/run-index.js.map +1 -0
  18. package/dist/runs/store.d.ts +34 -7
  19. package/dist/runs/store.js +71 -38
  20. package/dist/runs/store.js.map +1 -1
  21. package/dist/server/git-changes.d.ts +7 -5
  22. package/dist/server/git-changes.js +15 -10
  23. package/dist/server/git-changes.js.map +1 -1
  24. package/dist/server/server.d.ts +470 -93
  25. package/dist/server/server.js +142 -71
  26. package/dist/server/server.js.map +1 -1
  27. package/dist/workflows/run.js +5 -3
  28. package/dist/workflows/run.js.map +1 -1
  29. package/dist/workspace/config.d.ts +4 -8
  30. package/dist/workspace/config.js +5 -9
  31. package/dist/workspace/config.js.map +1 -1
  32. package/dist/workspace/projects-cli.js +4 -6
  33. package/dist/workspace/projects-cli.js.map +1 -1
  34. package/dist/workspace/projects.d.ts +13 -34
  35. package/dist/workspace/projects.js +5 -46
  36. package/dist/workspace/projects.js.map +1 -1
  37. package/dist/workspace/ui-state.d.ts +3 -2
  38. package/dist/workspace/ui-state.js +3 -2
  39. package/dist/workspace/ui-state.js.map +1 -1
  40. package/package.json +3 -3
  41. package/web/dist/assets/{alert-dialog-6dQMgOXw.js → alert-dialog-T_OZt_0N.js} +1 -1
  42. package/web/dist/assets/arrow-down-naX1Y8_P.js +1 -0
  43. package/web/dist/assets/arrow-left-nsQsVWFx.js +1 -0
  44. package/web/dist/assets/centered-state-C5LGBIPx.js +43 -0
  45. package/web/dist/assets/chevron-right-DUb439P9.js +1 -0
  46. package/web/dist/assets/circle-check-BQqTupyN.js +1 -0
  47. package/web/dist/assets/{circle-x-DF_Vyxoz.js → circle-x-yLvASIQc.js} +1 -1
  48. package/web/dist/assets/{collapsible-BJslo1eE.js → collapsible-BuHaoF0A.js} +1 -1
  49. package/web/dist/assets/{commit-list-DPoEJ-5l.js → commit-list-C5mrhnvj.js} +1 -1
  50. package/web/dist/assets/{compare-variants-Ddz-oUCA.js → compare-variants-D80s1myj.js} +1 -1
  51. package/web/dist/assets/{diff-BdWNf9S7.js → diff-M29ekGyG.js} +2 -2
  52. package/web/dist/assets/{diff-stat-CXB01WSy.js → diff-stat-DipSnVpe.js} +1 -1
  53. package/web/dist/assets/{diff-view-Bsy3QLyj.js → diff-view-4crv56CQ.js} +1 -1
  54. package/web/dist/assets/{dropdown-menu-B4Epti3u.js → dropdown-menu-Dsui_Qpe.js} +1 -1
  55. package/web/dist/assets/{editable-title-CbQ3uoGg.js → editable-title-Cxks5ulD.js} +1 -1
  56. package/web/dist/assets/{ellipsis-vertical-BVCmLX8u.js → ellipsis-vertical-OwPh90bg.js} +1 -1
  57. package/web/dist/assets/{file-Bml0aHqJ.js → file-B-PciHAX.js} +1 -1
  58. package/web/dist/assets/{folder-CBh3ERAX.js → folder-1X-vj51c.js} +1 -1
  59. package/web/dist/assets/{git-pull-request-3Jrr-Hnz.js → git-pull-request-C8VI-rYA.js} +1 -1
  60. package/web/dist/assets/{git-toolbar-Gy4_cPLz.js → git-toolbar-CbmlBiAo.js} +1 -1
  61. package/web/dist/assets/github-D18m86T_.js +1 -0
  62. package/web/dist/assets/{image-preview-DS7DvehB.js → image-preview-DPoKyqaH.js} +1 -1
  63. package/web/dist/assets/index-BJ49lw5s.js +6 -0
  64. package/web/dist/assets/index-D6LN2Ox9.css +2 -0
  65. package/web/dist/assets/{markdown-B1EknoxI.js → markdown-CVuxhT1g.js} +1 -1
  66. package/web/dist/assets/{new-task-form-1cTj9thS.js → new-task-form-DHKMN3F1.js} +1 -1
  67. package/web/dist/assets/{pill-BHn0lyG-.js → pill-G3pmePZz.js} +1 -1
  68. package/web/dist/assets/{project-router-BkbADB0Z.js → project-router-CarrBPqh.js} +1 -1
  69. package/web/dist/assets/{prompt-templates-D83ZM7XO.js → prompt-templates-C7vW9wdz.js} +1 -1
  70. package/web/dist/assets/{refresh-cw-DX8Tj6b9.js → refresh-cw-DcyKyvRa.js} +1 -1
  71. package/web/dist/assets/{repo-git-CKR1UKRl.js → repo-git-DCb_5ELG.js} +1 -1
  72. package/web/dist/assets/{run-diff-BKlEMNhf.js → run-diff-M00fVhC8.js} +2 -2
  73. package/web/dist/assets/run-header-BHRkYIc5.js +1 -0
  74. package/web/dist/assets/{search-x-B1NrVJwd.js → search-x-C-_qFGPr.js} +1 -1
  75. package/web/dist/assets/{skill-empty-hint-CLB1FsYI.js → skill-empty-hint-DG6Vml0Y.js} +1 -1
  76. package/web/dist/assets/{skills-OLjkJUPE.js → skills-ELIfGgJ-.js} +1 -1
  77. package/web/dist/assets/{sparkles-iifrwy2d.js → sparkles-Dnc2zVUS.js} +1 -1
  78. package/web/dist/assets/{square-terminal-B7pjoOV-.js → square-terminal-DvU55QgE.js} +1 -1
  79. package/web/dist/assets/{tab-link-CDAMnCm8.js → tab-link-BJI3KkN9.js} +1 -1
  80. package/web/dist/assets/task-changes-C2nK7q4X.js +1 -0
  81. package/web/dist/assets/{task-commits-BCgNsn2-.js → task-commits-B7qMOydZ.js} +1 -1
  82. package/web/dist/assets/{task-files-DbzZ7e5E.js → task-files-CgFwOxDJ.js} +2 -2
  83. package/web/dist/assets/{task-thread-D6I003ru.js → task-thread-p_ifHwP_.js} +7 -7
  84. package/web/dist/assets/{textarea-BDfkreJ8.js → textarea-mX1N3ZdA.js} +1 -1
  85. package/web/dist/assets/thread-loading-FU_kkMiP.js +1 -0
  86. package/web/dist/assets/{trash-2-Bm7yPRlf.js → trash-2-wp262e5g.js} +1 -1
  87. package/web/dist/assets/{triangle-alert-BoNsVbBe.js → triangle-alert-BznBoKx7.js} +1 -1
  88. package/web/dist/assets/{upload-DNOSu710.js → upload-Cio3C2hO.js} +1 -1
  89. package/web/dist/assets/{use-desktop-vb6126vH.js → use-desktop-ffK0BPND.js} +1 -1
  90. package/web/dist/assets/{use-submit-shortcut-DyHLUMIa.js → use-submit-shortcut-Cvel1EHv.js} +1 -1
  91. package/web/dist/assets/{utils-CT1niAPs.js → utils-CkZreLOk.js} +1 -1
  92. package/web/dist/assets/{workflows-BJqCaffV.js → workflows-BQGCpThz.js} +3 -3
  93. package/web/dist/assets/{zoomable-image-C7pDSJpR.js → zoomable-image-Dzk9EIcj.js} +1 -1
  94. package/web/dist/index.html +23 -23
  95. package/dist/workspace/boot.d.ts +0 -27
  96. package/dist/workspace/boot.js +0 -41
  97. package/dist/workspace/boot.js.map +0 -1
  98. package/web/dist/assets/arrow-down-9F3rZKJT.js +0 -1
  99. package/web/dist/assets/arrow-left-BOTr2zuB.js +0 -1
  100. package/web/dist/assets/centered-state-QSAdDoHK.js +0 -43
  101. package/web/dist/assets/chevron-right-Bga7k0g0.js +0 -1
  102. package/web/dist/assets/circle-check-CMvUZyJZ.js +0 -1
  103. package/web/dist/assets/github-BcC0R3jc.js +0 -1
  104. package/web/dist/assets/index-B84F80Q8.css +0 -2
  105. package/web/dist/assets/index-BQhkx-lw.js +0 -6
  106. package/web/dist/assets/run-header-BC6ZZQtf.js +0 -1
  107. package/web/dist/assets/task-changes-Dc7u63rJ.js +0 -1
  108. package/web/dist/assets/thread-loading-D6DMmo5_.js +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Patryk Lewczuk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -19,7 +19,7 @@ your phone, working your backlog while you're away.
19
19
 
20
20
  [A look inside](#a-look-inside) · [What cezar does best](#what-cezar-does-best) · [What it solves](#what-it-solves) · [Who it's for](#who-its-for) · [Quick start](#quick-start) · [How it works](#how-it-works) · [Core concepts](#core-concepts) · [Cockpit tour](#cockpit-tour) · [Agent backends](#coding-agent-backends) · [Remote access](#remote-access-host-cezar-on-a-server)
21
21
 
22
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](#license)
22
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
23
23
  ![Node 20+](https://img.shields.io/badge/Node-20%2B-339933)
24
24
  ![TypeScript 7.x](https://img.shields.io/badge/TypeScript-7.x-3178c6)
25
25
  ![Zero config](https://img.shields.io/badge/config-zero-success)
@@ -307,21 +307,12 @@ a GitHub page.
307
307
  ## Multiple projects, one cockpit
308
308
 
309
309
  One `cezar serve` hosts **every repo you work in**, not just the one you started
310
- it in. Projects live in a per-user registry at `~/.cezar/config.json` — the
311
- workspace file that also holds the global knobs
310
+ it in. Each repo cezar boots in registers itself in a per-user registry at
311
+ `~/.cezar/config.json` — the workspace file that also holds the global knobs
312
312
  (the parallel cap, the memory ceiling, the browse root, and the checkout root). Nothing is added to
313
313
  the repo: per-project state stays exactly where it was, in that repo's
314
314
  `.ai/cezar/`.
315
315
 
316
- **Your first run registers the repo you start it in** — that is the whole setup.
317
- After that the registry is yours to curate: starting cezar somewhere else serves
318
- that folder as usual (its own tasks, its own `.ai/cezar/`) but does not add it to
319
- the list behind your back. It shows up at the top of the sidebar marked **not
320
- saved**, and **Settings → Projects** lists it as *not registered* with an
321
- **Add project** button — so the folder you launched in is always one click from
322
- being kept, and never kept without the click. `cezar projects add` does the same
323
- from a terminal.
324
-
325
316
  Every view is project-scoped:
326
317
 
327
318
  ```
@@ -345,8 +336,8 @@ and task list — and the new-task composer names the project it will run in.
345
336
 
346
337
  Removing a project (**Settings → Projects**) drops the registry entry only — the
347
338
  repo and its `.ai/cezar/` are never touched, so re-adding it later finds all its
348
- tasks intact. The project cezar is currently serving can't be removed from the
349
- cockpit — stop the server and use `cezar projects remove <id>` instead.
339
+ tasks intact. The project cezar is currently serving can't be removed: it
340
+ re-registers itself at the next start.
350
341
 
351
342
  **From the terminal** — the same registry, no cockpit required (handy over ssh):
352
343
 
@@ -359,11 +350,12 @@ cezar projects remove api # drop the registry entry; the repo is untouch
359
350
  These read and write `~/.cezar/config.json` directly, so they work with the
360
351
  server stopped, and `CEZ_HOME` selects which workspace they operate on.
361
352
 
362
- Settings split along the same line: **Agents**, **Worktrees**, **Bookmarklets**,
363
- **Prompt templates** and **MCP** describe one repo and live under
364
- `/p/<projectId>/settings`; **Appearance**, **Notifications**, **Resources**,
365
- **Projects** and **Keyboard** are yours or the machine's and live at
366
- `/settings/global`.
353
+ Settings split along the same line: **General** (the project's folder, its
354
+ registry facts, its parallel-task ceiling, and Remove), **Agents**,
355
+ **Worktrees**, **Bookmarklets**, **Prompt templates** and **MCP** describe one
356
+ repo and live under `/p/<projectId>/settings`; **Appearance**,
357
+ **Notifications**, **Resources**, **Projects** and **Keyboard** are yours or the
358
+ machine's and live at `/settings/global`.
367
359
 
368
360
  **Old page URLs keep working.** Every unprefixed page path — `/`, `/tasks/<id>`,
369
361
  `/settings` — still answers, bound to the project cezar was started in; the
@@ -721,4 +713,4 @@ Start here; graduate to cezar when a team needs shared visibility.
721
713
 
722
714
  ## License
723
715
 
724
- **MIT** © Patryk Lewczuk
716
+ **MIT** © Patryk Lewczuk — full text in [LICENSE](LICENSE).
@@ -204,11 +204,12 @@ var diffStatSchema = z4.object({
204
204
  adds: z4.number(),
205
205
  dels: z4.number(),
206
206
  files: z4.number(),
207
- /** Additive since #751, and present ONLY when true: the numbers cover uncommitted work
208
- * alone, because the worktree's HEAD had been repointed off the task's own branch (every
209
- * review/QA run does this) and the merge-base anchor would otherwise have reported the
210
- * checked-out branch's entire diff as this task's. Absent on every normal run and on every
211
- * record written before #751 — a consumer that ignores it sees exactly the old shape. */
207
+ /** Additive since #751, and present ONLY when true: the numbers were measured against a
208
+ * branch the agent checked out into the task's worktree, as the run found it, because the
209
+ * worktree's HEAD had been repointed off the task's own branch (every review/QA run does
210
+ * this) and the merge-base anchor would otherwise have reported that branch's entire diff
211
+ * as this task's. Absent on every normal run and on every record written before #751 — a
212
+ * consumer that ignores it sees exactly the old shape. */
212
213
  repointed: z4.boolean().optional()
213
214
  });
214
215
  var queuedMessageSchema = z4.object({
@@ -333,7 +334,8 @@ var runRecordSchema = z4.object({
333
334
  /** Read receipt (#unread-done-items): ISO time the cockpit last opened this run's
334
335
  * thread. A finished (`done`/`failed`) run reads as *unread* until seen since it
335
336
  * finished — see `isUnread()` in the cockpit's `lib/read-state.ts`. Absent on old
336
- * runs and on any run not yet opened, both of which count as unread. */
337
+ * runs, on any run not yet opened, and on one deliberately put back to unread via
338
+ * `POST /runs/:id/unread` (#775) — all three count as unread. */
337
339
  seenAt: z4.string().optional(),
338
340
  currentStepId: z4.string().optional(),
339
341
  error: z4.string().optional(),
@@ -355,6 +357,40 @@ var runRecordSchema = z4.object({
355
357
  var apiRunSchema = runRecordSchema.extend({
356
358
  usage: processUsageSchema.optional()
357
359
  });
360
+ var runIndexEntrySchema = z4.object({
361
+ /** The registered project this run belongs to. Joins against `GET /projects`. */
362
+ projectId: z4.string(),
363
+ id: z4.string(),
364
+ title: z4.string(),
365
+ titleSummary: z4.string().optional(),
366
+ titleOrigin: z4.enum(["user", "auto", "marker"]).optional(),
367
+ status: runStatusSchema,
368
+ activity: runActivitySchema.optional(),
369
+ createdAt: z4.string(),
370
+ finishedAt: z4.string().optional(),
371
+ /** With `status`/`finishedAt`/`archived`, the four inputs `isUnread` reads — what lets the
372
+ * palette lead with "finished while you weren't looking" across every project, not just the
373
+ * one you happen to be standing in. */
374
+ seenAt: z4.string().optional(),
375
+ /** Always present, like `RunRecord.archived`: absent would read as "not archived", and the
376
+ * unread rule treats archiving as a stronger "done with this" than reading. */
377
+ archived: z4.boolean(),
378
+ /** A run parked by a provider usage limit is `failed` on the record with a resume booked
379
+ * (spec 2026-08-03-auto-resume-after-usage-limit). Both `deriveAttention` and `isUnread` read
380
+ * it, so without it here a cross-project row would show a red "failed" dot and land in
381
+ * Recently finished for work that is simply waiting for its appointment. */
382
+ autoResumeAt: z4.string().optional()
383
+ });
384
+ var runsIndexResponseSchema = z4.object({
385
+ /** Newest first, across every registered project. Archived runs are included — `GET /runs`
386
+ * carries them for the project you are standing in, and a finder that dropped them elsewhere
387
+ * would make a task vanish the moment you left its project. */
388
+ runs: z4.array(runIndexEntrySchema),
389
+ /** The per-project cap that produced this list. */
390
+ perProjectLimit: z4.number(),
391
+ /** Ids of the projects that had more runs than the cap allowed. */
392
+ truncated: z4.array(z4.string())
393
+ });
358
394
  var createRunResponseSchema = z4.union([
359
395
  runRecordSchema,
360
396
  z4.object({ runs: z4.array(runRecordSchema) })
@@ -766,13 +802,7 @@ var projectListEntrySchema = z7.object({
766
802
  forge: z7.literal("github").optional(),
767
803
  /** Per-project cap on concurrently running tasks (spec 2026-07-22). Omitted = inherit the
768
804
  * workspace `resources.maxParallel`; a number pins this project. */
769
- maxParallel: z7.number().optional(),
770
- /** Set ONLY on the synthetic entry for the folder this server was started in when that folder
771
- * is not in the registry — the ordinary state since boot registration became seed-once. The
772
- * server serves it like any project (it owns the boot context), so the cockpit must be able to
773
- * reach it; but it is not persisted, so the rows that edit the registry (Remove, Max parallel)
774
- * do not apply and Settings offers "Add project" instead. Never present on a registry entry. */
775
- unregistered: z7.literal(true).optional()
805
+ maxParallel: z7.number().optional()
776
806
  });
777
807
  var projectsResponseSchema = z7.object({
778
808
  projects: z7.array(projectListEntrySchema),
@@ -934,9 +964,11 @@ var workspaceLastLocationSchema = z8.strictObject({
934
964
  hash: z8.string().max(2048).startsWith("#").optional()
935
965
  });
936
966
  var workspaceUiStateSchema = z8.looseObject({
937
- /** The sidebar's per-project collapse map (step 3.3) — `true` collapses a group, `false` pins
938
- * it open, absent means the default (the active project expands, the rest collapse). Loose for
939
- * the same round-trip reason as its parent. */
967
+ /** LEGACY — the sidebar's per-project collapse map (step 3.3). Still accepted and still
968
+ * round-tripped so an older cockpit sharing this home keeps working, but the current cockpit
969
+ * neither reads nor writes it: which groups are shut describes the WINDOW, not the workspace,
970
+ * so it lives in that browser's localStorage (`packages/web/src/lib/sidebar-collapse.ts`).
971
+ * One shared answer meant a phone collapsing a group collapsed it on the desktop too. */
940
972
  sidebar: z8.looseObject({ collapsed: z8.record(z8.string(), z8.boolean()).optional() }).optional(),
941
973
  /** Dismissed runtime-auth incident IDs, keyed by provider. An ID is only dismissed until the
942
974
  * provider reports a different incident, so this stays workspace-global with the browser
@@ -952,8 +984,10 @@ var workspaceUiStateSchema = z8.looseObject({
952
984
  /** Settings → Notifications, GLOBAL since step 3.5 — one answer for the whole workspace, since
953
985
  * the delivering browser is one browser whichever project you are looking at. */
954
986
  notifications: z8.looseObject({ enabled: z8.boolean().optional() }).optional(),
955
- /** Last settled project-scoped page. Used only when entering at the exact bare root;
956
- * explicit deep links always win. */
987
+ /** LEGACY, exactly like `sidebar` above — the last settled project-scoped page, restored when
988
+ * entering at the exact bare root. The shape is unchanged and still accepted, but the current
989
+ * cockpit keeps it in localStorage (`packages/web/src/lib/last-location.ts`): stored here, the
990
+ * last client to navigate decided where every OTHER client's next launch landed. */
957
991
  lastLocation: workspaceLastLocationSchema.optional(),
958
992
  /** The user's curated selection of default (vendor) skills. Tri-state: ABSENT means "not
959
993
  * curated", so every default skill shows; a PRESENT array (even `[]`) means only those names
@@ -1081,6 +1115,14 @@ var openTargetSchema = z8.object({
1081
1115
  var openTargetsResponseSchema = z8.object({
1082
1116
  targets: z8.array(openTargetSchema)
1083
1117
  });
1118
+ var openProjectInSchema = z8.object({
1119
+ // A short bound (#429): matched against a downstream allowlist, so an app id is never long.
1120
+ target: z8.string().trim().min(1, "target required").max(200)
1121
+ });
1122
+ var openProjectInResponseSchema = z8.object({
1123
+ opened: z8.literal(true),
1124
+ path: z8.string()
1125
+ });
1084
1126
 
1085
1127
  // ../contract/src/skills.ts
1086
1128
  import { z as z9 } from "zod";
@@ -1538,6 +1580,8 @@ export {
1538
1580
  openAgentAccountFileInputSchema,
1539
1581
  openAgentAccountFileResponseSchema,
1540
1582
  openInCliResponseSchema,
1583
+ openProjectInResponseSchema,
1584
+ openProjectInSchema,
1541
1585
  openTargetSchema,
1542
1586
  openTargetsResponseSchema,
1543
1587
  parsedWorkflowSchema,
@@ -1568,12 +1612,14 @@ export {
1568
1612
  runCommitSchema,
1569
1613
  runCommitsResponseSchema,
1570
1614
  runEventSchema,
1615
+ runIndexEntrySchema,
1571
1616
  runRecordSchema,
1572
1617
  runStatusSchema,
1573
1618
  runnerModelCatalogResponseSchema,
1574
1619
  runnerModelOptionSchema,
1575
1620
  runnerModelsSchema,
1576
1621
  runnerSchema,
1622
+ runsIndexResponseSchema,
1577
1623
  saveWorkflowInputSchema,
1578
1624
  saveWorkflowResponseSchema,
1579
1625
  selectAgentProfileInputSchema,
@@ -36,7 +36,6 @@ export declare const projectListEntrySchema: z.ZodObject<{
36
36
  branch: z.ZodOptional<z.ZodString>;
37
37
  forge: z.ZodOptional<z.ZodLiteral<"github">>;
38
38
  maxParallel: z.ZodOptional<z.ZodNumber>;
39
- unregistered: z.ZodOptional<z.ZodLiteral<true>>;
40
39
  }, z.core.$strip>;
41
40
  export type ProjectListEntry = z.infer<typeof projectListEntrySchema>;
42
41
  /** `GET /api/v1/projects` — the workspace registry. Workspace-level: never 404s, never scoped.
@@ -61,7 +60,6 @@ export declare const projectsResponseSchema: z.ZodObject<{
61
60
  branch: z.ZodOptional<z.ZodString>;
62
61
  forge: z.ZodOptional<z.ZodLiteral<"github">>;
63
62
  maxParallel: z.ZodOptional<z.ZodNumber>;
64
- unregistered: z.ZodOptional<z.ZodLiteral<true>>;
65
63
  }, z.core.$strip>>;
66
64
  bootProject: z.ZodString;
67
65
  projectsDir: z.ZodString;
@@ -91,7 +89,6 @@ export declare const registerProjectResponseSchema: z.ZodObject<{
91
89
  branch: z.ZodOptional<z.ZodString>;
92
90
  forge: z.ZodOptional<z.ZodLiteral<"github">>;
93
91
  maxParallel: z.ZodOptional<z.ZodNumber>;
94
- unregistered: z.ZodOptional<z.ZodLiteral<true>>;
95
92
  }, z.core.$strip>;
96
93
  error: z.ZodOptional<z.ZodString>;
97
94
  }, z.core.$strip>;
@@ -129,7 +126,6 @@ export declare const updateProjectResponseSchema: z.ZodObject<{
129
126
  branch: z.ZodOptional<z.ZodString>;
130
127
  forge: z.ZodOptional<z.ZodLiteral<"github">>;
131
128
  maxParallel: z.ZodOptional<z.ZodNumber>;
132
- unregistered: z.ZodOptional<z.ZodLiteral<true>>;
133
129
  }, z.core.$strip>;
134
130
  }, z.core.$strip>;
135
131
  export type UpdateProjectResponse = z.infer<typeof updateProjectResponseSchema>;
@@ -445,6 +445,92 @@ export declare const apiRunSchema: z.ZodObject<{
445
445
  }, z.core.$strip>>;
446
446
  }, z.core.$strip>;
447
447
  export type ApiRun = z.infer<typeof apiRunSchema>;
448
+ /**
449
+ * One run in the WORKSPACE-level index (`GET /api/v1/workspace/runs-index`) — the ⌘K palette's
450
+ * "find a task in any project" list.
451
+ *
452
+ * Deliberately a separate, slim shape rather than `ApiRun`. The index answers for every
453
+ * registered project at once, and `runRecordSchema` carries `steps[]` and `workflowDef` — a fat
454
+ * record whose cost is fine per project and absurd multiplied by the registry. These are exactly
455
+ * the fields a palette row renders: `runTitle`'s three (`title`, `titleSummary`, `titleOrigin`),
456
+ * `deriveAttention`'s `AttentionInput`, `isUnread`'s `ReadStateInput`, and the timestamps
457
+ * `shortAge` reads. Adding a field here is cheap; adding the whole record is what this exists to
458
+ * avoid — but note that widening either of those two `Pick`s means widening this too, or the
459
+ * palette's cross-project rows silently answer differently from every other surface.
460
+ *
461
+ * `projectId` is the join key, NOT the project name: the registry is already on the client and is
462
+ * authoritative for display names, and duplicating one here would let a renamed project show two
463
+ * different labels in one palette.
464
+ */
465
+ export declare const runIndexEntrySchema: z.ZodObject<{
466
+ projectId: z.ZodString;
467
+ id: z.ZodString;
468
+ title: z.ZodString;
469
+ titleSummary: z.ZodOptional<z.ZodString>;
470
+ titleOrigin: z.ZodOptional<z.ZodEnum<{
471
+ auto: "auto";
472
+ marker: "marker";
473
+ user: "user";
474
+ }>>;
475
+ status: z.ZodEnum<{
476
+ cancelled: "cancelled";
477
+ done: "done";
478
+ failed: "failed";
479
+ queued: "queued";
480
+ review: "review";
481
+ running: "running";
482
+ waiting: "waiting";
483
+ }>;
484
+ activity: z.ZodOptional<z.ZodEnum<{
485
+ monitoring: "monitoring";
486
+ }>>;
487
+ createdAt: z.ZodString;
488
+ finishedAt: z.ZodOptional<z.ZodString>;
489
+ seenAt: z.ZodOptional<z.ZodString>;
490
+ archived: z.ZodBoolean;
491
+ autoResumeAt: z.ZodOptional<z.ZodString>;
492
+ }, z.core.$strip>;
493
+ export type RunIndexEntry = z.infer<typeof runIndexEntrySchema>;
494
+ /**
495
+ * `GET /workspace/runs-index`.
496
+ *
497
+ * `truncated` is not decoration: the index caps each project's contribution, and a capped list
498
+ * that says nothing reads as "your task is not here" when the honest answer is "not in the
499
+ * newest N". Naming the projects that hit the cap is what lets a consumer say so.
500
+ */
501
+ export declare const runsIndexResponseSchema: z.ZodObject<{
502
+ runs: z.ZodArray<z.ZodObject<{
503
+ projectId: z.ZodString;
504
+ id: z.ZodString;
505
+ title: z.ZodString;
506
+ titleSummary: z.ZodOptional<z.ZodString>;
507
+ titleOrigin: z.ZodOptional<z.ZodEnum<{
508
+ auto: "auto";
509
+ marker: "marker";
510
+ user: "user";
511
+ }>>;
512
+ status: z.ZodEnum<{
513
+ cancelled: "cancelled";
514
+ done: "done";
515
+ failed: "failed";
516
+ queued: "queued";
517
+ review: "review";
518
+ running: "running";
519
+ waiting: "waiting";
520
+ }>;
521
+ activity: z.ZodOptional<z.ZodEnum<{
522
+ monitoring: "monitoring";
523
+ }>>;
524
+ createdAt: z.ZodString;
525
+ finishedAt: z.ZodOptional<z.ZodString>;
526
+ seenAt: z.ZodOptional<z.ZodString>;
527
+ archived: z.ZodBoolean;
528
+ autoResumeAt: z.ZodOptional<z.ZodString>;
529
+ }, z.core.$strip>>;
530
+ perProjectLimit: z.ZodNumber;
531
+ truncated: z.ZodArray<z.ZodString>;
532
+ }, z.core.$strip>;
533
+ export type RunsIndexResponse = z.infer<typeof runsIndexResponseSchema>;
448
534
  /**
449
535
  * `POST /runs` (201) — one record for ×1, a group for ×2/×3.
450
536
  *
@@ -470,3 +470,21 @@ export declare const openTargetsResponseSchema: z.ZodObject<{
470
470
  }, z.core.$strip>>;
471
471
  }, z.core.$strip>;
472
472
  export type OpenTargetsResponse = z.infer<typeof openTargetsResponseSchema>;
473
+ /**
474
+ * `POST /api/v1/open-in` — open THIS PROJECT'S root in a detected app (Settings → the project
475
+ * folder row). The path is never sent: it is the scoped project's own registered root, resolved
476
+ * server-side, so the route has no traversal surface at all. `target` is an
477
+ * `/api/v1/open-targets` id; unlike the run route there is no `default`/`cli:` handling, because
478
+ * a repo root is a directory and an agent CLI belongs in a task worktree.
479
+ */
480
+ export declare const openProjectInSchema: z.ZodObject<{
481
+ target: z.ZodString;
482
+ }, z.core.$strip>;
483
+ export type OpenProjectInRequest = z.infer<typeof openProjectInSchema>;
484
+ /** The 200 for the above — `opened` is a literal because every failure is a 409 with `{ error }`,
485
+ * so a `false` would be unreachable and would only invite a client to branch on it. */
486
+ export declare const openProjectInResponseSchema: z.ZodObject<{
487
+ opened: z.ZodLiteral<true>;
488
+ path: z.ZodString;
489
+ }, z.core.$strip>;
490
+ export type OpenProjectInResponse = z.infer<typeof openProjectInResponseSchema>;
@@ -6,22 +6,47 @@
6
6
  * to the task's own commits even after the base branch moves on, and even
7
7
  * after the task merges the base back in.
8
8
  *
9
- * The exception is a **repointed HEAD**. cezar hands the agent a worktree on
10
- * the task's own branch, but nothing stops the agent from checking out some
11
- * other branch in it — every `review/pr-NNN`, QA, and "continue someone
12
- * else's branch" run does exactly that. Anchoring at the merge-base then
13
- * silently redefines "this task's diff" as *the whole reviewed branch*, which
14
- * is how a task that committed nothing came to report `+22505 −2628`. When
15
- * HEAD is not on the task's branch the honest anchor is `HEAD` itself: the
16
- * uncommitted work in the tree is the only part this task can be said to have
17
- * produced.
18
- *
19
- * #591 established that rule for the Changes tab; #751 found the sidebar and
20
- * Tasks-table numbers still on the old anchor. It lives here so a third
21
- * surface cannot get it wrong again — callers hand in their own `git` runner
22
- * (`git-worktree.ts` and `server/git-changes.ts` each own a private one, and
23
- * `git-changes` needs its scratch-`GIT_INDEX_FILE` variant), so this module
24
- * stays a pure decision with no process-spawning of its own.
9
+ * Two things break that answer, and both are fixed here:
10
+ *
11
+ * **A stale local base ref.** `RunRecord.baseBranch` is a NAME (`main`),
12
+ * resolved to a ref once, when the worktree was created. Nothing ever
13
+ * fast-forwards the user's local `main` — cezar's agents only ever `git fetch`,
14
+ * which moves `origin/main` — so on a repo the user does not pull, the local
15
+ * ref drifts arbitrarily far behind. The merge-base then collapses onto that
16
+ * stale tip and every upstream commit the task forked from or merged in counts
17
+ * as the task's own work: an eight-line fix measured `+59514 −12160` against a
18
+ * `main` that was 98 commits and one monorepo restructure behind origin. So the
19
+ * base is re-resolved to the freshest ref it names at every call
20
+ * (`freshestBaseRef`) — the read-time twin of `resolveBaseRef`, which does the
21
+ * same thing at worktree-creation time and cannot know what happens later.
22
+ *
23
+ * **A repointed HEAD.** cezar hands the agent a worktree on the task's own
24
+ * branch, but nothing stops the agent from checking out another branch in it —
25
+ * every `review/pr-NNN` and QA run does exactly that, and so does every skill
26
+ * that opens its work on a named `feat/…` branch. The merge-base then silently
27
+ * redefines "this task's diff" as *the whole checked-out branch*, which is how
28
+ * a task that committed nothing came to report `+22505 −2628`. #591 and #751
29
+ * answered that with `HEAD` — uncommitted work only — which is right for a
30
+ * pure review and badly wrong for the far more common case of an agent that
31
+ * committed real work on a branch it created itself: those runs reported
32
+ * `+0 −0` for changes that were genuinely theirs.
33
+ *
34
+ * The honest anchor for a repointed HEAD is the branch as it stood **when this
35
+ * run first saw it** — its `<branch>@{<run start>}` reflog state. Diffing
36
+ * against that attributes exactly what the run did to the tree and nothing
37
+ * that was already there: a review run gets its `+0`, and a run that branched
38
+ * off the base and committed gets its real numbers. Where that baseline is
39
+ * itself stale — the run merged the base branch in afterwards, dragging the
40
+ * whole upstream delta behind it — the ordinary merge-base is the tighter
41
+ * answer, so the two candidates compete and the one that attributes FEWER
42
+ * CHANGED LINES to the task wins. The reported number is then never more than
43
+ * the least either measure can defend.
44
+ *
45
+ * It lives here so a third surface cannot get it wrong again — callers hand in
46
+ * their own `git` runner (`git-worktree.ts` and `server/git-changes.ts` each
47
+ * own a private one, and `git-changes` needs its scratch-`GIT_INDEX_FILE`
48
+ * variant), so this module stays a pure decision with no process-spawning of
49
+ * its own.
25
50
  */
26
51
  /** What a caller's `git` runner must answer with. Both existing runners are wider than this. */
27
52
  export interface GitRunResult {
@@ -38,23 +63,23 @@ export interface RepointedHead {
38
63
  export interface TaskDiffBase {
39
64
  /** The ref to diff against. */
40
65
  base: string;
41
- /** Present only when HEAD left the task's branch — the reason `base` is `HEAD`. */
66
+ /** Present only when HEAD left the task's branch — the reason `base` is what it is. */
42
67
  repointedHead?: RepointedHead;
43
68
  }
44
69
  /**
45
- * Resolve the diff anchor for a task worktree. Never throws: a failing git
46
- * call degrades to the base branch name, which is what the un-guarded callers
47
- * did before this helper existed.
70
+ * Resolve the diff anchor for a task worktree. Never throws: a failing git call
71
+ * degrades to the base branch name, which is what the un-guarded callers did
72
+ * before this helper existed.
48
73
  *
49
74
  * `taskBranch` is optional because not every caller has one (the main working
50
- * tree has no task branch at all). Without it there is nothing to compare
51
- * HEAD against, so the merge-base anchor is used unchanged — that is the
52
- * pre-#751 behavior, preserved exactly.
75
+ * tree has no task branch at all). Without it there is nothing to compare HEAD
76
+ * against, so the merge-base anchor is used unchanged.
53
77
  *
54
- * A detached HEAD (`rev-parse --abbrev-ref HEAD` → `HEAD`) counts as
55
- * repointed: it is by definition not the task's branch, so the same
56
- * uncommitted-only anchor is the honest one.
78
+ * `runStartedAt` is what makes the repointed-HEAD answer a measurement instead
79
+ * of a guess; without it the anchor stays `HEAD` (uncommitted work only), the
80
+ * conservative #751 answer that can never claim someone else's commits.
57
81
  */
58
82
  export declare function resolveTaskDiffBase(runGit: GitRunner, baseBranch: string, opts?: {
59
83
  taskBranch?: string;
84
+ runStartedAt?: string;
60
85
  }): Promise<TaskDiffBase>;