@ashlr/hub 3.8.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/dist/build-identity.json +2 -2
  3. package/dist/cli/resource-profile.js +95 -21
  4. package/dist/cli/resource-profile.js.map +1 -1
  5. package/dist/core/resources/native-profile.d.ts +56 -0
  6. package/dist/core/resources/native-profile.js +368 -6
  7. package/dist/core/resources/native-profile.js.map +1 -1
  8. package/dist/core/run/model-catalog.js +8 -1
  9. package/dist/core/run/model-catalog.js.map +1 -1
  10. package/dist/core/universe/builtins/preparation/manifest.json +1 -1
  11. package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +9 -2
  12. package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +8 -1
  13. package/dist/core/verse/adapters/claude.d.ts +49 -2
  14. package/dist/core/verse/adapters/claude.js +317 -56
  15. package/dist/core/verse/adapters/claude.js.map +1 -1
  16. package/dist/core/verse/adapters/codex.d.ts +81 -5
  17. package/dist/core/verse/adapters/codex.js +409 -47
  18. package/dist/core/verse/adapters/codex.js.map +1 -1
  19. package/dist/core/verse/adapters/grok.d.ts +13 -1
  20. package/dist/core/verse/adapters/grok.js +28 -2
  21. package/dist/core/verse/adapters/grok.js.map +1 -1
  22. package/dist/core/verse/adapters/index.d.ts +32 -0
  23. package/dist/core/verse/adapters/index.js +2 -0
  24. package/dist/core/verse/adapters/index.js.map +1 -1
  25. package/dist/core/verse/codex-rollout.d.ts +284 -0
  26. package/dist/core/verse/codex-rollout.js +786 -0
  27. package/dist/core/verse/codex-rollout.js.map +1 -0
  28. package/dist/core/verse/context-fit.d.ts +63 -0
  29. package/dist/core/verse/context-fit.js +355 -0
  30. package/dist/core/verse/context-fit.js.map +1 -0
  31. package/dist/core/verse/context-math.d.ts +215 -0
  32. package/dist/core/verse/context-math.js +365 -0
  33. package/dist/core/verse/context-math.js.map +1 -0
  34. package/dist/core/verse/local-models.d.ts +137 -1
  35. package/dist/core/verse/local-models.js +209 -5
  36. package/dist/core/verse/local-models.js.map +1 -1
  37. package/dist/core/verse/model-windows.d.ts +217 -0
  38. package/dist/core/verse/model-windows.js +616 -0
  39. package/dist/core/verse/model-windows.js.map +1 -0
  40. package/dist/core/verse/preferences.d.ts +123 -0
  41. package/dist/core/verse/preferences.js +403 -0
  42. package/dist/core/verse/preferences.js.map +1 -0
  43. package/dist/core/verse/project-memory.d.ts +112 -0
  44. package/dist/core/verse/project-memory.js +295 -0
  45. package/dist/core/verse/project-memory.js.map +1 -0
  46. package/dist/core/verse/seats.d.ts +106 -4
  47. package/dist/core/verse/seats.js +291 -89
  48. package/dist/core/verse/seats.js.map +1 -1
  49. package/dist/core/verse/session-engine.d.ts +76 -2
  50. package/dist/core/verse/session-engine.js +684 -63
  51. package/dist/core/verse/session-engine.js.map +1 -1
  52. package/dist/core/verse/session-handoff.d.ts +94 -0
  53. package/dist/core/verse/session-handoff.js +551 -0
  54. package/dist/core/verse/session-handoff.js.map +1 -0
  55. package/dist/core/verse/session-search.d.ts +63 -0
  56. package/dist/core/verse/session-search.js +213 -0
  57. package/dist/core/verse/session-search.js.map +1 -0
  58. package/dist/core/verse/session-store.d.ts +16 -2
  59. package/dist/core/verse/session-store.js +80 -6
  60. package/dist/core/verse/session-store.js.map +1 -1
  61. package/dist/core/verse/types.d.ts +333 -1
  62. package/dist/core/verse/types.js +54 -2
  63. package/dist/core/verse/types.js.map +1 -1
  64. package/dist/core/verse/verse-api.d.ts +40 -5
  65. package/dist/core/verse/verse-api.js +724 -25
  66. package/dist/core/verse/verse-api.js.map +1 -1
  67. package/dist/core/web/api.d.ts +14 -4
  68. package/dist/core/web/api.js +26 -11
  69. package/dist/core/web/api.js.map +1 -1
  70. package/dist/core/web/public/next/assets/{App-BsbJFA2j.js → App-XURmiAAp.js} +5 -5
  71. package/dist/core/web/public/next/assets/{App-BsbJFA2j.js.map → App-XURmiAAp.js.map} +1 -1
  72. package/dist/core/web/public/next/assets/{ApprovalsSection-2CCSoBTJ.js → ApprovalsSection-BriUzoZc.js} +2 -2
  73. package/dist/core/web/public/next/assets/{ApprovalsSection-2CCSoBTJ.js.map → ApprovalsSection-BriUzoZc.js.map} +1 -1
  74. package/dist/core/web/public/next/assets/AutonomySection-C4GBsQVc.js +2 -0
  75. package/dist/core/web/public/next/assets/{AutonomySection-yeNNUPur.js.map → AutonomySection-C4GBsQVc.js.map} +1 -1
  76. package/dist/core/web/public/next/assets/ChatSection-BpOBOUvg.css +1 -0
  77. package/dist/core/web/public/next/assets/ChatSection-hlgPBRco.js +84 -0
  78. package/dist/core/web/public/next/assets/ChatSection-hlgPBRco.js.map +1 -0
  79. package/dist/core/web/public/next/assets/ConfirmDialog-BdQYI-Ng.js +2 -0
  80. package/dist/core/web/public/next/assets/ConfirmDialog-BdQYI-Ng.js.map +1 -0
  81. package/dist/core/web/public/next/assets/{Dialog-nK15L1M0.js → Dialog-BxdIfmFH.js} +2 -2
  82. package/dist/core/web/public/next/assets/{Dialog-nK15L1M0.js.map → Dialog-BxdIfmFH.js.map} +1 -1
  83. package/dist/core/web/public/next/assets/{DiffViewer-D369eZe8.js → DiffViewer-DmeRWuc4.js} +2 -2
  84. package/dist/core/web/public/next/assets/{DiffViewer-D369eZe8.js.map → DiffViewer-DmeRWuc4.js.map} +1 -1
  85. package/dist/core/web/public/next/assets/{McpSection-DVgiSYG1.js → McpSection-q34T7Tho.js} +2 -2
  86. package/dist/core/web/public/next/assets/{McpSection-DVgiSYG1.js.map → McpSection-q34T7Tho.js.map} +1 -1
  87. package/dist/core/web/public/next/assets/{MutationTokenDialog-B9I4PAcp.js → MutationTokenDialog-CFAkwOEt.js} +2 -2
  88. package/dist/core/web/public/next/assets/{MutationTokenDialog-B9I4PAcp.js.map → MutationTokenDialog-CFAkwOEt.js.map} +1 -1
  89. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-z6tJj3s9.js → ResourcePoolConsoleApp-DHc8GFOY.js} +4 -4
  90. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-z6tJj3s9.js.map → ResourcePoolConsoleApp-DHc8GFOY.js.map} +1 -1
  91. package/dist/core/web/public/next/assets/{RouteErrorBoundary-OSajL45F.js → RouteErrorBoundary-ZL9F4qUn.js} +2 -2
  92. package/dist/core/web/public/next/assets/{RouteErrorBoundary-OSajL45F.js.map → RouteErrorBoundary-ZL9F4qUn.js.map} +1 -1
  93. package/dist/core/web/public/next/assets/{Meter-CLcr-4wS.css → Segmented-8cf0Fypn.css} +1 -1
  94. package/dist/core/web/public/next/assets/{Meter-Cmo1pLbk.js → Segmented-DbRjVye3.js} +2 -2
  95. package/dist/core/web/public/next/assets/Segmented-DbRjVye3.js.map +1 -0
  96. package/dist/core/web/public/next/assets/SettingsSection-nJGGGYqv.js +2 -0
  97. package/dist/core/web/public/next/assets/{SettingsSection-B3YH2Npi.js.map → SettingsSection-nJGGGYqv.js.map} +1 -1
  98. package/dist/core/web/public/next/assets/UniverseConsoleApp-6GJydgo4.js +2 -0
  99. package/dist/core/web/public/next/assets/{UniverseConsoleApp-wEKIzl1M.js.map → UniverseConsoleApp-6GJydgo4.js.map} +1 -1
  100. package/dist/core/web/public/next/assets/{UniverseView-CQVpJqqI.js → UniverseView-DLDtNYL8.js} +2 -2
  101. package/dist/core/web/public/next/assets/{UniverseView-CQVpJqqI.js.map → UniverseView-DLDtNYL8.js.map} +1 -1
  102. package/dist/core/web/public/next/assets/UsageSection-Cf9d9iFH.js +2 -0
  103. package/dist/core/web/public/next/assets/UsageSection-Cf9d9iFH.js.map +1 -0
  104. package/dist/core/web/public/next/assets/VerseConsoleApp-DsdKQoTH.css +1 -0
  105. package/dist/core/web/public/next/assets/VerseConsoleApp-y0uOHixd.js +3 -0
  106. package/dist/core/web/public/next/assets/VerseConsoleApp-y0uOHixd.js.map +1 -0
  107. package/dist/core/web/public/next/assets/{charts-BopViIha.js → charts-CVooqJWN.js} +2 -2
  108. package/dist/core/web/public/next/assets/{charts-BopViIha.js.map → charts-CVooqJWN.js.map} +1 -1
  109. package/dist/core/web/public/next/assets/context-model-Bqv8DVuH.js +2 -0
  110. package/dist/core/web/public/next/assets/context-model-Bqv8DVuH.js.map +1 -0
  111. package/dist/core/web/public/next/assets/global-eaY9GyX3.js +2 -0
  112. package/dist/core/web/public/next/assets/global-eaY9GyX3.js.map +1 -0
  113. package/dist/core/web/public/next/assets/{icons-L06pQH6j.js → icons-vex8rclC.js} +2 -2
  114. package/dist/core/web/public/next/assets/{icons-L06pQH6j.js.map → icons-vex8rclC.js.map} +1 -1
  115. package/dist/core/web/public/next/assets/{index-CXgVQHek.js → index-D2Eik-zt.js} +3 -3
  116. package/dist/core/web/public/next/assets/{index-CXgVQHek.js.map → index-D2Eik-zt.js.map} +1 -1
  117. package/dist/core/web/public/next/assets/{queries-D3sSlan1.js → queries-bAYU0VNy.js} +2 -2
  118. package/dist/core/web/public/next/assets/{queries-D3sSlan1.js.map → queries-bAYU0VNy.js.map} +1 -1
  119. package/dist/core/web/public/next/assets/use-guarded-action-C7QrP7Yp.js +2 -0
  120. package/dist/core/web/public/next/assets/{use-guarded-action-BxfJ9E-n.js.map → use-guarded-action-C7QrP7Yp.js.map} +1 -1
  121. package/dist/core/web/public/next/assets/verse-model-TfDk9cky.js +2 -0
  122. package/dist/core/web/public/next/assets/verse-model-TfDk9cky.js.map +1 -0
  123. package/dist/core/web/public/next/assets/{workspace-model-v0a1gl-C.js → workspace-model-CHol5RVp.js} +2 -2
  124. package/dist/core/web/public/next/assets/{workspace-model-v0a1gl-C.js.map → workspace-model-CHol5RVp.js.map} +1 -1
  125. package/dist/core/web/public/next/index.html +1 -1
  126. package/dist/release-dependency-inventory.json +1 -1
  127. package/docs/README.md +1 -0
  128. package/package.json +1 -1
  129. package/dist/core/web/public/next/assets/AutonomySection-yeNNUPur.js +0 -2
  130. package/dist/core/web/public/next/assets/ChatSection-BfnLe5dT.css +0 -1
  131. package/dist/core/web/public/next/assets/ChatSection-DwDDnCZQ.js +0 -80
  132. package/dist/core/web/public/next/assets/ChatSection-DwDDnCZQ.js.map +0 -1
  133. package/dist/core/web/public/next/assets/ConfirmDialog-C363ty_c.js +0 -2
  134. package/dist/core/web/public/next/assets/ConfirmDialog-C363ty_c.js.map +0 -1
  135. package/dist/core/web/public/next/assets/Meter-Cmo1pLbk.js.map +0 -1
  136. package/dist/core/web/public/next/assets/SettingsSection-B3YH2Npi.js +0 -2
  137. package/dist/core/web/public/next/assets/UniverseConsoleApp-wEKIzl1M.js +0 -2
  138. package/dist/core/web/public/next/assets/UsageSection-dToBThXe.js +0 -2
  139. package/dist/core/web/public/next/assets/UsageSection-dToBThXe.js.map +0 -1
  140. package/dist/core/web/public/next/assets/VerseConsoleApp-CMkCizvL.css +0 -1
  141. package/dist/core/web/public/next/assets/VerseConsoleApp-DHU32JCQ.js +0 -3
  142. package/dist/core/web/public/next/assets/VerseConsoleApp-DHU32JCQ.js.map +0 -1
  143. package/dist/core/web/public/next/assets/global-DMWakEhP.js +0 -2
  144. package/dist/core/web/public/next/assets/global-DMWakEhP.js.map +0 -1
  145. package/dist/core/web/public/next/assets/use-guarded-action-BxfJ9E-n.js +0 -2
  146. package/dist/core/web/public/next/assets/verse-model-CvtK6vP6.js +0 -2
  147. package/dist/core/web/public/next/assets/verse-model-CvtK6vP6.js.map +0 -1
@@ -43,11 +43,90 @@ export interface VerseSeat {
43
43
  * at all — `health.state` says what it does know.
44
44
  */
45
45
  capacity?: VerseSeatCapacity;
46
+ /**
47
+ * V3.9 ADDITIVE. The version of the CLI binary this seat is PINNED to (its
48
+ * native-profile launcher execs one exact file). Absent when unknown and on
49
+ * local seats. A pinned binary never updates itself, so a model that needs a
50
+ * newer CLI is listed with `unavailableReason` rather than silently failing.
51
+ */
52
+ cliVersion?: string;
53
+ /**
54
+ * V3.9 ADDITIVE. Plain-language facts about this seat's context setup the UI
55
+ * must show rather than imply (binary skew, catalog not yet fetched, …).
56
+ * Absent when there is nothing to say, so ordinary seats keep their shape.
57
+ */
58
+ notes?: string[];
59
+ }
60
+ /**
61
+ * Where a context-window figure came from, so nothing on screen implies more
62
+ * certainty than it has (docs/VERSE-CONTEXT.md §1).
63
+ *
64
+ * - `runtime` — the CLI reported it for THIS turn (claude/grok
65
+ * `result.modelUsage.<m>.contextWindow`, codex rollout
66
+ * `token_count.info.model_context_window`, Ollama
67
+ * `/api/ps`). Wins over everything else.
68
+ * - `provider-catalog` — the seat's own served catalog on disk (codex/grok
69
+ * `native-state/models_cache.json`).
70
+ * - `cli-catalog` — read out of the pinned CLI binary's embedded catalog
71
+ * and recorded here with the version it was read from.
72
+ * - `documented` — the provider's published model table.
73
+ * - `fallback` — a named default; the UI marks it as an estimate.
74
+ */
75
+ export type VerseWindowSource = 'runtime' | 'provider-catalog' | 'cli-catalog' | 'documented' | 'fallback';
76
+ /**
77
+ * How much context a session is allowed to accumulate before the CLI compacts.
78
+ *
79
+ * - `standard` — the default. Evidence-based budget per engine: Claude 1M
80
+ * models compact near 400k instead of ~967k; Codex, Grok and
81
+ * local run their native windows.
82
+ * - `expansive` — opt-in. Claude runs its full native window (CLI `auto`),
83
+ * Codex raises `model_context_window` to the model's catalog
84
+ * maximum (872k on GPT-6 / GPT-5.6). Costs more usage per turn,
85
+ * buys simultaneous visibility for coupled, cross-cutting work.
86
+ *
87
+ * A mode that does not exist for a model (Grok, local, 200k Claude models,
88
+ * GPT-5.5) is simply absent from that model's option — never faked.
89
+ */
90
+ export type VerseContextMode = 'standard' | 'expansive';
91
+ export declare const VERSE_CONTEXT_MODES: readonly VerseContextMode[];
92
+ /** One mode's budget for a model on a seat. */
93
+ export interface VerseContextBudget {
94
+ /** The window the CLI measures against in this mode (the meter's denominator). */
95
+ contextWindow: number;
96
+ /** Token count at which the CLI auto-compacts in this mode; null when unknown. */
97
+ autoCompactAt: number | null;
98
+ /**
99
+ * The RAW window value the CLI must be TOLD to reach this budget, when that
100
+ * differs from `contextWindow` — codex measures against 95% of the window it
101
+ * is configured with, so expansive GPT-6 is `providerWindow: 872_000`
102
+ * (`-c model_context_window=872000`) but `contextWindow: 828_400`.
103
+ */
104
+ providerWindow?: number | null;
46
105
  }
47
106
  export interface VerseModelOption {
48
107
  id: string;
49
108
  label: string;
109
+ /** Standard-mode window the CLI measures against (codex: the 95% effective window). */
50
110
  contextWindow: number | null;
111
+ /**
112
+ * V3.9 ADDITIVE — every field below is optional so records and fixtures
113
+ * written before it keep validating. Absent means "not known", never zero.
114
+ */
115
+ /** Standard-mode auto-compaction point. */
116
+ autoCompactAt?: number | null;
117
+ /** Present ONLY when this model on this seat has a real expansive mode. */
118
+ expansive?: VerseContextBudget | null;
119
+ /** The CLI's default max output for this model, when known. */
120
+ maxOutputTokens?: number | null;
121
+ windowSource?: VerseWindowSource;
122
+ /** Minimum CLI version that knows this model id (e.g. '2.1.280'); null when any. */
123
+ minCliVersion?: string | null;
124
+ /**
125
+ * Why this model cannot run on this seat right now, in plain language (e.g.
126
+ * "needs Claude Code 2.1.280; this seat runs 2.1.257"). Null/absent = runnable.
127
+ * Listed rather than hidden so the operator learns WHY a model is missing.
128
+ */
129
+ unavailableReason?: string | null;
51
130
  }
52
131
  export interface VerseSeatHealth {
53
132
  state: 'ready' | 'degraded' | 'unavailable' | 'unknown';
@@ -324,9 +403,23 @@ export interface VerseUsage {
324
403
  outputTokens: number;
325
404
  cacheReadTokens: number;
326
405
  cacheCreationTokens: number;
327
- /** Prompt size of the most recent assistant call = live context occupancy. */
406
+ /**
407
+ * Prompt size of the most recent assistant call = live context occupancy.
408
+ * Stored UNCLAMPED: a reading above the window is information (the CLI is
409
+ * about to compact or overflow), and only the UI decides how to draw it.
410
+ */
328
411
  contextTokens: number;
329
412
  contextWindow: number | null;
413
+ /** V3.9 ADDITIVE. Where `contextWindow` came from; absent on old records. */
414
+ contextWindowSource?: VerseWindowSource;
415
+ /** V3.9 ADDITIVE. Auto-compaction point for the session's current mode. */
416
+ autoCompactAt?: number | null;
417
+ /**
418
+ * V3.9 ADDITIVE. False when `contextTokens` is an UPPER BOUND rather than a
419
+ * measurement (codex before its rollout is readable: the turn total sums
420
+ * every model call). Absent means exact.
421
+ */
422
+ contextTokensExact?: boolean;
330
423
  }
331
424
  export interface VerseSession {
332
425
  id: string;
@@ -365,6 +458,30 @@ export interface VerseSession {
365
458
  turnCount: number;
366
459
  usage: VerseUsage;
367
460
  lastError: string | null;
461
+ /**
462
+ * V3.9 ADDITIVE. The session's context budget. Every record written by 3.9+
463
+ * carries it explicitly, so ABSENT means a pre-3.9 record: the engine
464
+ * materializes it on first touch — `expansive` for a Claude 1M model (that
465
+ * is the native ~967k compaction those chats always ran with, so upgrading
466
+ * never forces a paid compaction), `standard` otherwise (= native).
467
+ * Changeable mid-session (POST /sessions/:id/context-mode): it changes CLI
468
+ * flags, not prompt content — but switching DOWN below the current
469
+ * occupancy makes the CLI compact on its next turn.
470
+ */
471
+ contextMode?: VerseContextMode;
472
+ /** V3.9 ADDITIVE. Native compactions observed so far (claude/grok compact_boundary, codex rollout `compacted`). */
473
+ compactionCount?: number;
474
+ /** V3.9 ADDITIVE. Set when this session was started as a handoff from another. */
475
+ handoffFrom?: {
476
+ sessionId: string;
477
+ title: string;
478
+ };
479
+ /**
480
+ * V3.9 ADDITIVE. Shared project memory was offered to this session's CLI at
481
+ * creation. Pinned, like the roots: a conversation must not silently gain a
482
+ * writable directory because a preference moved underneath it.
483
+ */
484
+ memoryEnabled?: boolean;
368
485
  }
369
486
  /** Normalized event stream. `seq` is monotonic per session and is the SSE id. */
370
487
  export type VerseEvent = {
@@ -438,6 +555,48 @@ export type VerseEvent = {
438
555
  at: string;
439
556
  type: 'cancelled';
440
557
  turnId: string;
558
+ }
559
+ /**
560
+ * V3.9. The CLI compacted its own conversation. Token counts are the CLI's
561
+ * own (`compact_metadata.pre_tokens/post_tokens` on claude/grok). Codex
562
+ * rollouts mark only THAT a compaction happened, so its counts are the last
563
+ * `token_count` reading before the marker and the first one after it —
564
+ * measured, but by Verse, not stated by the CLI; its `durationMs` is null.
565
+ * Any count may be null when no reading brackets the compaction.
566
+ */
567
+ | {
568
+ seq: number;
569
+ at: string;
570
+ type: 'compaction';
571
+ turnId: string | null;
572
+ trigger: 'auto' | 'manual';
573
+ preTokens: number | null;
574
+ postTokens: number | null;
575
+ durationMs: number | null;
576
+ }
577
+ /**
578
+ * V3.9. A context-occupancy READING that is not a usage delta — e.g. codex's
579
+ * rollout `token_count` (exact last-call prompt size and the window the CLI
580
+ * measured it against), polled mid-turn and read again after it. Replaces
581
+ * the session's `contextTokens`; never summed. The engine fills
582
+ * `autoCompactAt` for the window in force.
583
+ */
584
+ | {
585
+ seq: number;
586
+ at: string;
587
+ type: 'context';
588
+ turnId: string | null;
589
+ contextTokens: number;
590
+ contextWindow: number | null;
591
+ exact: boolean;
592
+ autoCompactAt?: number | null;
593
+ /**
594
+ * Where `contextWindow` came from. A CLI reading is `runtime`; the event
595
+ * the engine writes after a MODE SWITCH carries the new budget's catalog
596
+ * source, so a client never relabels a catalog figure as a measurement.
597
+ * Absent on events from before this field existed (treat as `runtime`).
598
+ */
599
+ contextWindowSource?: VerseWindowSource;
441
600
  };
442
601
  export type VerseEventType = VerseEvent['type'];
443
602
  /**
@@ -529,6 +688,18 @@ export interface VerseCreateSessionRequest {
529
688
  * from.
530
689
  */
531
690
  workspaceName?: string;
691
+ /**
692
+ * V3.9 ADDITIVE. Absent → the seat's preferred mode (preferences.json), else
693
+ * `standard`. `expansive` on a model with no expansive budget is rejected.
694
+ */
695
+ contextMode?: VerseContextMode;
696
+ /**
697
+ * V3.9 ADDITIVE. Start this session as a handoff from an existing one. The
698
+ * server resolves the source's title itself (never from the body) and pins
699
+ * `handoffFrom`; the handoff TEXT is sent by the client as turn 1, after the
700
+ * operator has read it — nothing is spent on their behalf.
701
+ */
702
+ handoffFromSessionId?: string;
532
703
  }
533
704
  /** POST /api/verse/sessions/:id/turns */
534
705
  export interface VerseTurnRequest {
@@ -555,4 +726,165 @@ export interface VerseTurnLaunch {
555
726
  export declare const VERSE_MAX_TURN_TEXT_BYTES: number;
556
727
  export declare const VERSE_MAX_EVENTS_PER_SESSION = 20000;
557
728
  export declare const VERSE_TURN_TIMEOUT_MS: number;
729
+ /**
730
+ * LAST-RESORT fallbacks, used only when neither the runtime, the seat's own
731
+ * catalog, nor the verified per-model table (core/verse/model-windows.ts) knows
732
+ * the model. Each mirrors what the CLI ITSELF assumes for an unknown id, so the
733
+ * meter never claims more than the CLI will allow:
734
+ *
735
+ * - claude 200_000 — Claude Code's `Mg()` default for an unrecognized model.
736
+ * Every KNOWN Claude id carries its real window (1M for the 5.x family,
737
+ * Opus 4.8 and Sonnet 5) in the per-model table instead.
738
+ * - codex 258_400 — 272_000 × the 95% `effective_context_window_percent`
739
+ * every codex catalog entry carries; the figure codex's own rollouts report
740
+ * as `model_context_window` (65,440 of 65,440 observed events).
741
+ * - grok 500_000 — `context_window` for every grok-4.x id in the seat catalog.
742
+ * - local 65_536 — the num_ctx the default local tag pins.
743
+ */
558
744
  export declare const VERSE_DEFAULT_CONTEXT_WINDOWS: Record<string, number>;
745
+ /** GET /api/verse/preferences — the operator's standing context choices. */
746
+ export interface VersePreferences {
747
+ version: 1;
748
+ /** Per-seat default context mode for NEW sessions. Absent = `standard`. */
749
+ seats: Record<string, {
750
+ contextMode?: VerseContextMode;
751
+ }>;
752
+ /** Shared project memory. On by default; listed canonical paths opt out. */
753
+ memory: {
754
+ enabled: boolean;
755
+ disabledProjects: string[];
756
+ };
757
+ }
758
+ /**
759
+ * POST /api/verse/preferences — exactly ONE of the three forms per request, so
760
+ * a body can never change more than the operator clicked.
761
+ */
762
+ export type VersePreferencesUpdate = {
763
+ seatId: string;
764
+ contextMode: VerseContextMode;
765
+ } | {
766
+ memoryEnabled: boolean;
767
+ } | {
768
+ projectPath: string;
769
+ memoryEnabled: boolean;
770
+ };
771
+ /** POST /api/verse/sessions/:id/context-mode */
772
+ export interface VerseContextModeRequest {
773
+ mode: VerseContextMode;
774
+ }
775
+ /** POST /api/verse/sessions/:id/handoff-preview */
776
+ export interface VerseHandoffPreviewRequest {
777
+ /**
778
+ * Include the latest assistant message verbatim (capped) as the agent's own
779
+ * summary — used after "Ask this seat to summarize" produced one.
780
+ */
781
+ includeLastAssistant?: boolean;
782
+ /** Optional operator focus line, e.g. "finish the migration of X". ≤ 500 chars. */
783
+ focus?: string;
784
+ }
785
+ /** Deterministic, zero-spend handoff note built from the session's event log. */
786
+ export interface VerseHandoffPreview {
787
+ sourceSessionId: string;
788
+ sourceTitle: string;
789
+ /** The exact text to send as turn 1 of the new session. ≤ VERSE_HANDOFF_MAX_CHARS. */
790
+ text: string;
791
+ stats: {
792
+ chars: number;
793
+ /** chars / 4, rounded up — the same estimator the fit badges use. */
794
+ estTokens: number;
795
+ turnsCovered: number;
796
+ filesTouched: number;
797
+ /** Sections dropped to respect the cap, by name; empty when nothing was cut. */
798
+ truncated: string[];
799
+ };
800
+ }
801
+ export declare const VERSE_HANDOFF_MAX_CHARS = 12000;
802
+ /**
803
+ * The canned turn behind "Ask this seat to summarize first". Shared by the UI
804
+ * (which sends it as an ordinary turn through the normal spend chokepoint) and
805
+ * the handoff builder (which must NOT carry it into the new session as the
806
+ * operator's "latest ask" — the new chat would otherwise be told to write a
807
+ * handoff note instead of continuing the work).
808
+ *
809
+ * Written so the reply is useful as turn 1 of a session that has NONE of this
810
+ * context. It asks for no tool use on purpose: a summary that starts editing
811
+ * files is not a summary, and a read-only turn is also the cheapest one.
812
+ */
813
+ export declare const VERSE_HANDOFF_SUMMARY_REQUEST: string;
814
+ /** GET /api/verse/context-fit — how big the reachable code is, in tokens. */
815
+ export interface VerseContextFitRoot {
816
+ path: string;
817
+ /** Tracked text files counted (git ls-files, binaries and >1 MB files skipped). */
818
+ files: number;
819
+ bytes: number;
820
+ estTokens: number;
821
+ /** True when the scan hit its file or time cap; estTokens is then a floor. */
822
+ truncated: boolean;
823
+ }
824
+ export interface VerseContextFit {
825
+ roots: VerseContextFitRoot[];
826
+ totalEstTokens: number;
827
+ /** Always 'bytes/4' today; named so a better estimator can be introduced honestly. */
828
+ estimator: 'bytes/4';
829
+ sampledAt: string;
830
+ }
831
+ /**
832
+ * Per-model verdict for a working set (see context-math.ts `fitVerdict`):
833
+ * fits in standard, tight in standard, fits only in expansive, or too big for
834
+ * any single context (split the work).
835
+ */
836
+ export type VerseFitVerdict = 'fits' | 'tight' | 'expansive' | 'split';
837
+ /** GET /api/verse/search?q=&limit= — keyword search over past sessions. Zero spend. */
838
+ export interface VerseSearchHit {
839
+ sessionId: string;
840
+ title: string;
841
+ projectPath: string;
842
+ engine: VerseEngine;
843
+ seq: number;
844
+ at: string;
845
+ kind: 'user' | 'assistant';
846
+ /** ≤ 240 chars around the first match, whitespace-collapsed. */
847
+ snippet: string;
848
+ score: number;
849
+ }
850
+ export interface VerseSearchResponse {
851
+ query: string;
852
+ hits: VerseSearchHit[];
853
+ /** Sessions actually scanned (the scan is bounded; see session-search.ts). */
854
+ scannedSessions: number;
855
+ truncated: boolean;
856
+ }
857
+ /** GET/POST /api/verse/memory — shared project memory for one project. */
858
+ export interface VerseProjectMemory {
859
+ projectPath: string;
860
+ enabled: boolean;
861
+ /** Contents of MEMORY.md ('' when absent). */
862
+ content: string;
863
+ bytes: number;
864
+ updatedAt: string | null;
865
+ /** Other files the agents created in the memory directory (names only). */
866
+ files: string[];
867
+ /**
868
+ * Present (true) only when `content` as SENT differs from the bytes on disk:
869
+ * the public-JSON sanitizer rewrote the home path to `~` or replaced a
870
+ * secret-looking value with `[REDACTED]`. The file itself is untouched; a
871
+ * save that would write placeholders over real values is refused (409
872
+ * VERSE_MEMORY_REDACTED), so the editor must say so before the operator types.
873
+ */
874
+ contentSanitized?: boolean;
875
+ }
876
+ /** POST /api/verse/memory */
877
+ export interface VerseProjectMemoryWrite {
878
+ projectPath: string;
879
+ content: string;
880
+ }
881
+ export declare const VERSE_MEMORY_MAX_BYTES: number;
882
+ /**
883
+ * POST /api/verse/memory's own JSON body cap: JSON escaping can double the
884
+ * content (every newline becomes `\\n`), plus room for the other fields. Every
885
+ * other route keeps the 64 KiB default. Shared so the browser's pre-check can
886
+ * never refuse a save the server would accept.
887
+ */
888
+ export declare const VERSE_MEMORY_BODY_MAX_BYTES: number;
889
+ /** Cap on the memory block appended to each session's system prompt. */
890
+ export declare const VERSE_MEMORY_BLOCK_MAX_BYTES: number;
@@ -15,6 +15,7 @@
15
15
  * Nothing in this module reads secrets. Account identity is pinned by the
16
16
  * launcher command recorded in ~/.ashlr/account-connections/connections.json.
17
17
  */
18
+ export const VERSE_CONTEXT_MODES = ['standard', 'expansive'];
18
19
  export const VERSE_ROOT_PRIORITIES = ['critical', 'high', 'normal', 'low'];
19
20
  export const VERSE_DEFAULT_ROOT_PRIORITY = 'normal';
20
21
  /** Sort weight; lower sorts first. Never a budget, never a multiplier. */
@@ -44,10 +45,61 @@ export function verseSessionRoots(session) {
44
45
  export const VERSE_MAX_TURN_TEXT_BYTES = 64 * 1024;
45
46
  export const VERSE_MAX_EVENTS_PER_SESSION = 20_000;
46
47
  export const VERSE_TURN_TIMEOUT_MS = 30 * 60 * 1000;
48
+ /**
49
+ * LAST-RESORT fallbacks, used only when neither the runtime, the seat's own
50
+ * catalog, nor the verified per-model table (core/verse/model-windows.ts) knows
51
+ * the model. Each mirrors what the CLI ITSELF assumes for an unknown id, so the
52
+ * meter never claims more than the CLI will allow:
53
+ *
54
+ * - claude 200_000 — Claude Code's `Mg()` default for an unrecognized model.
55
+ * Every KNOWN Claude id carries its real window (1M for the 5.x family,
56
+ * Opus 4.8 and Sonnet 5) in the per-model table instead.
57
+ * - codex 258_400 — 272_000 × the 95% `effective_context_window_percent`
58
+ * every codex catalog entry carries; the figure codex's own rollouts report
59
+ * as `model_context_window` (65,440 of 65,440 observed events).
60
+ * - grok 500_000 — `context_window` for every grok-4.x id in the seat catalog.
61
+ * - local 65_536 — the num_ctx the default local tag pins.
62
+ */
47
63
  export const VERSE_DEFAULT_CONTEXT_WINDOWS = {
48
64
  claude: 200_000,
49
- codex: 272_000,
50
- grok: 256_000,
65
+ codex: 258_400,
66
+ grok: 500_000,
51
67
  local: 65_536,
52
68
  };
69
+ export const VERSE_HANDOFF_MAX_CHARS = 12_000;
70
+ /**
71
+ * The canned turn behind "Ask this seat to summarize first". Shared by the UI
72
+ * (which sends it as an ordinary turn through the normal spend chokepoint) and
73
+ * the handoff builder (which must NOT carry it into the new session as the
74
+ * operator's "latest ask" — the new chat would otherwise be told to write a
75
+ * handoff note instead of continuing the work).
76
+ *
77
+ * Written so the reply is useful as turn 1 of a session that has NONE of this
78
+ * context. It asks for no tool use on purpose: a summary that starts editing
79
+ * files is not a summary, and a read-only turn is also the cheapest one.
80
+ */
81
+ export const VERSE_HANDOFF_SUMMARY_REQUEST = [
82
+ 'This conversation is about to continue in a fresh session that has none of its context.',
83
+ 'Write the handoff note that new session should start from. Do not edit files or run commands for this — only write the note.',
84
+ '',
85
+ 'Cover, concretely:',
86
+ '- The goal, and what "done" looks like.',
87
+ '- Decisions made so far, each with the reason it was made.',
88
+ '- Current state: what is finished, what is half-done, what is untested.',
89
+ '- Files changed and anything not yet committed.',
90
+ '- How to verify the work (exact commands).',
91
+ '- Open questions, risks, and approaches already tried that did not work.',
92
+ '',
93
+ 'Keep it under 600 words. Prefer file paths and commands over prose.',
94
+ ].join('\n');
95
+ export const VERSE_MEMORY_MAX_BYTES = 64 * 1024;
96
+ /**
97
+ * POST /api/verse/memory's own JSON body cap: JSON escaping can double the
98
+ * content (every newline becomes `\\n`), plus room for the other fields. Every
99
+ * other route keeps the 64 KiB default. Shared so the browser's pre-check can
100
+ * never refuse a save the server would accept.
101
+ */
102
+ export const VERSE_MEMORY_BODY_MAX_BYTES = 2 * VERSE_MEMORY_MAX_BYTES + 8 * 1024;
103
+ /** Cap on the memory block appended to each session's system prompt. */
104
+ export const VERSE_MEMORY_BLOCK_MAX_BYTES = 6 * 1024;
53
105
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/verse/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAyNH,MAAM,CAAC,MAAM,qBAAqB,GAChC,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AAExC,MAAM,CAAC,MAAM,2BAA2B,GAAsB,QAAQ,CAAC;AAEvE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,wBAAwB,GAAsC;IACzE,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;CACP,CAAC;AA2HF,wEAAwE;AACxE,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAyD;IAEzD,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;QAC5C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAoLD,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,IAAI,CAAC;AACnD,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AACnD,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACpD,MAAM,CAAC,MAAM,6BAA6B,GAA2B;IACnE,MAAM,EAAE,OAAO;IACf,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,MAAM;CACd,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/verse/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAgFH,MAAM,CAAC,MAAM,mBAAmB,GAAgC,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AA4N1F,MAAM,CAAC,MAAM,qBAAqB,GAChC,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AAExC,MAAM,CAAC,MAAM,2BAA2B,GAAsB,QAAQ,CAAC;AAEvE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,wBAAwB,GAAsC;IACzE,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;CACP,CAAC;AA2HF,wEAAwE;AACxE,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAyD;IAEzD,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;QAC5C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AA6QD,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,IAAI,CAAC;AACnD,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC;AACnD,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACpD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAA2B;IACnE,MAAM,EAAE,OAAO;IACf,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,MAAM;CACd,CAAC;AAyDF,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,yFAAyF;IACzF,8HAA8H;IAC9H,EAAE;IACF,oBAAoB;IACpB,yCAAyC;IACzC,4DAA4D;IAC5D,yEAAyE;IACzE,iDAAiD;IACjD,4CAA4C;IAC5C,0EAA0E;IAC1E,EAAE;IACF,qEAAqE;CACtE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AA4Eb,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,IAAI,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,GAAG,sBAAsB,GAAG,CAAC,GAAG,IAAI,CAAC;AAEjF,wEAAwE;AACxE,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,GAAG,IAAI,CAAC"}
@@ -6,10 +6,10 @@
6
6
  * - GETs sit behind the read-session boundary in server.ts.
7
7
  * - POSTs are 404 unless ctx.allowDispatch, then passesMutationGate()
8
8
  * (constant-time x-ashlr-token + JSON Content-Type), then readBody()
9
- * (64 KB cap).
9
+ * (64 KB cap; POST /memory alone gets VERSE_MEMORY_BODY_MAX_BYTES).
10
10
  * - Every response goes through sendJson() → sanitizePublicJson().
11
11
  *
12
- * Routes (see docs/VERSE-CONTRACT-V1.md):
12
+ * Routes (see docs/VERSE-CONTRACT-V1.md, docs/VERSE-CONTEXT.md for V3.9):
13
13
  * GET /api/verse/bootstrap → VerseBootstrap
14
14
  * GET /api/verse/seats → VerseSeatsResponse (pollable)
15
15
  * GET /api/verse/sessions → VerseSession[]
@@ -20,26 +20,48 @@
20
20
  * POST /api/verse/sessions/:id/delete → { ok: true }
21
21
  * POST /api/verse/sessions/:id/rename → VerseSession
22
22
  * GET /api/verse/sessions/:id/events → SSE (verse-stream.ts)
23
+ * V3.9 context orchestration — every one of these is ZERO SPEND: none starts
24
+ * a model call; the only way a token is spent is still POST .../turns.
25
+ * POST /api/verse/sessions/:id/context-mode → VerseSession
26
+ * POST /api/verse/sessions/:id/handoff-preview → VerseHandoffPreview
27
+ * GET /api/verse/preferences → VersePreferences
28
+ * POST /api/verse/preferences → VersePreferences
29
+ * GET /api/verse/context-fit → VerseContextFit
30
+ * GET /api/verse/search → VerseSearchResponse
31
+ * GET /api/verse/memory → VerseProjectMemory (+ contentSanitized?)
32
+ * POST /api/verse/memory → VerseProjectMemory (+ contentSanitized?)
23
33
  *
24
34
  * Errors: { error, code? } with VERSE_SESSION_NOT_FOUND 404,
25
- * VERSE_SESSION_BUSY 409, VERSE_INVALID 400, VERSE_TOO_LARGE 413.
35
+ * VERSE_SESSION_BUSY 409, VERSE_INVALID 400, VERSE_TOO_LARGE 413, and two
36
+ * route-local 409s: VERSE_MODEL_UNAVAILABLE (turns: the session's model is
37
+ * listed but unrunnable on its seat) and VERSE_MEMORY_REDACTED (memory: the
38
+ * content would write redaction placeholders over real values).
39
+ *
40
+ * STRICT BODIES AND QUERIES (V3.9 routes and POST /sessions): an unknown body
41
+ * key or query parameter is a 400, never silently ignored — a misspelt
42
+ * `contextMode` would otherwise create a session in a mode nobody asked for.
26
43
  *
27
44
  * ENGINE LIFETIME: one engine handle per server process — a module-level
28
45
  * lazy singleton (`getVerseEngine()`), with `resetVerseEngine()` for tests to
29
46
  * inject a fake or force re-creation under a relocated HOME. The engine
30
47
  * module is loaded lazily (dynamic import) so this file has no load-time
31
- * dependency on it; tests that inject a fake never touch it.
48
+ * dependency on it; tests that inject a fake never touch it. The V3.9 service
49
+ * modules keep that property: they report bad input with their own
50
+ * `VerseServiceError` (same `code` vocabulary, mapped by duck type below)
51
+ * rather than importing the engine's `VerseError`.
32
52
  *
33
53
  * PRIVACY: seat launchers (the native-profile commands) come from seats.ts's
34
54
  * private `launches` map and go straight to engine.createSession(). They are
35
55
  * never part of any response, log line, or session record this file writes.
56
+ * The same holds for the shared-memory snapshot (`VerseCreateOptions.memory`):
57
+ * its directory and prompt block go into the private launch record only.
36
58
  */
37
59
  import type { IncomingMessage, ServerResponse } from 'node:http';
38
60
  import type { AshlrConfig } from '../types.js';
39
61
  import { expandHomePrefix } from './path-guard.js';
40
62
  import { type VerseWorkspaceStore } from './workspaces.js';
41
63
  import type { VerseEngineHandle } from './session-engine.js';
42
- import { type VerseSession } from './types.js';
64
+ import { type VerseSession, VERSE_MEMORY_BODY_MAX_BYTES } from './types.js';
43
65
  export declare const VERSE_API_PREFIX = "/api/verse";
44
66
  export declare function isVerseApiPath(path: string): boolean;
45
67
  export interface VerseApiContext {
@@ -92,6 +114,19 @@ export declare function verseSessionsSnapshot(): VerseSession[];
92
114
  * caller and test already uses.
93
115
  */
94
116
  export { expandHomePrefix };
117
+ /**
118
+ * The body cap for POST /api/verse/memory ONLY.
119
+ *
120
+ * The memory file may be VERSE_MEMORY_MAX_BYTES of content, and JSON escaping
121
+ * can roughly double that (every `"`, `\` and newline becomes two bytes; a
122
+ * control character becomes six, but a memory file is prose). Under the shared
123
+ * 65,536-byte cap a file anywhere near the documented limit could never be
124
+ * saved: 64 KiB of content made a 65,711-byte body. 2× plus 8 KiB for the
125
+ * envelope (`projectPath`, keys) admits any realistic file at the limit while
126
+ * staying a bounded, small read. The CONTENT itself is still held to
127
+ * VERSE_MEMORY_MAX_BYTES (413) after parsing.
128
+ */
129
+ export { VERSE_MEMORY_BODY_MAX_BYTES };
95
130
  /**
96
131
  * Handle one /api/verse/* request. Returns true when a response was written
97
132
  * (including error responses); false when `path` is not a verse route so