@bendyline/gezel-service 0.1.0 → 1.0.1

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/dist/NOTICE.md +13 -5
  2. package/dist/bin/gezeld.js +56269 -50686
  3. package/dist/handboek-content/README.md +24 -2
  4. package/dist/handboek-content/conceptual/connected-apps.md +2 -0
  5. package/dist/handboek-content/technical/building-connected-apps-with-gezel-app-sdk.md +144 -0
  6. package/dist/handboek-content/technical/cli-reference.md +11 -0
  7. package/dist/handboek-content/technical/how-we-test-models.md +57 -9
  8. package/dist/handboek-content/technical/model-scorecard.md +27 -9
  9. package/dist/handboek-content/technical/npm-packages.md +67 -0
  10. package/dist/handboek-content/technical/security-model.md +18 -1
  11. package/dist/handboek-content/technical/where-files-live.md +3 -0
  12. package/dist/handboek-content/technical/writing-scripts-with-gezel-sdk.md +106 -0
  13. package/dist/handboek-content/whats-new/1.26224.md +82 -0
  14. package/dist/handboek-content/whats-new/whats-new-index.md +20 -0
  15. package/dist/handboek.d.ts +53 -26
  16. package/dist/handboek.js +109 -27
  17. package/dist/index-store/static-index-worker.js +518 -98
  18. package/dist/index.d.ts +377 -22
  19. package/dist/index.js +56204 -50670
  20. package/dist/ui/assets/{TerminalCodeEditor-CB-AXtKI.js → TerminalCodeEditor-BpkIEKrW.js} +2 -2
  21. package/dist/ui/assets/{_.contribution-C6KlZ1yR.js → _.contribution-DESswd3T.js} +1 -1
  22. package/dist/ui/assets/{chunk-6N2J7C2B-DIF7DJtk.js → chunk-6N2J7C2B-Du7clJkV.js} +1 -1
  23. package/dist/ui/assets/chunk-ILCJ3WFD-fHFoZ9wm.js +4 -0
  24. package/dist/ui/assets/{chunk-S5PCVMKU-CmFF6ff9.js → chunk-S5PCVMKU-DopU3Q9j.js} +2 -2
  25. package/dist/ui/assets/chunk-USU6HTKB-B-ElCUQS.js +1 -0
  26. package/dist/ui/assets/{chunk-WATAOG4Y-perfmDXF.js → chunk-WATAOG4Y-BnCgSncI.js} +1 -1
  27. package/dist/ui/assets/{cpp.contribution-BBBWN3Dm.js → cpp.contribution-DazoHQwn.js} +1 -1
  28. package/dist/ui/assets/{csharp.contribution-jBA6TWhG.js → csharp.contribution-B64MdTmq.js} +1 -1
  29. package/dist/ui/assets/{css.contribution-DQSNU6vC.js → css.contribution-u617lXYH.js} +1 -1
  30. package/dist/ui/assets/{cssMode-68UP_lrS.js → cssMode-Cf_QqRjJ.js} +1 -1
  31. package/dist/ui/assets/{csv-Ctux8fO7.js → csv-RAgECoUg.js} +1 -1
  32. package/dist/ui/assets/dist-DB6xxrlD.js +2 -0
  33. package/dist/ui/assets/dist-DucbO86I.js +222 -0
  34. package/dist/ui/assets/{doc-BdMAKGDm.js → doc-B7NXSLxH.js} +33 -23
  35. package/dist/ui/assets/{dockerfile.contribution-CSpSPKgK.js → dockerfile.contribution-P8T1FxHz.js} +1 -1
  36. package/dist/ui/assets/docx-IZY6hX8A.js +2 -0
  37. package/dist/ui/assets/{epub-oLed1tAU.js → epub-00L2M424.js} +1 -1
  38. package/dist/ui/assets/extract-UTAOG7LV-BZ_L7lLL.js +1 -0
  39. package/dist/ui/assets/{go.contribution-BjibqVfF.js → go.contribution-BqqmPpYF.js} +1 -1
  40. package/dist/ui/assets/{handlebars-Dq5dJ5WJ.js → handlebars-B9qcPqjD.js} +1 -1
  41. package/dist/ui/assets/handlebars.contribution-vZslMuRE.js +2 -0
  42. package/dist/ui/assets/{html-fQ6X2xaa.js → html-Cgejr9YM.js} +24 -24
  43. package/dist/ui/assets/{html-Bgyk9kiZ.js → html-Fp2jEZ7j.js} +1 -1
  44. package/dist/ui/assets/html.contribution-B1GzaEV-.js +2 -0
  45. package/dist/ui/assets/{htmlMode-D5UKInqK.js → htmlMode-B5cPhGim.js} +1 -1
  46. package/dist/ui/assets/{index-CPXp6BEZ.js → index-CQSh1NUQ.js} +193 -205
  47. package/dist/ui/assets/index-Ch0qGtVJ.css +1 -0
  48. package/dist/ui/assets/infer-v-bCXeT9.js +2 -0
  49. package/dist/ui/assets/{ini.contribution-BiJ4T5K-.js → ini.contribution-XkZYQphY.js} +1 -1
  50. package/dist/ui/assets/{java.contribution-B8o1lBqP.js → java.contribution-CUQitE0a.js} +1 -1
  51. package/dist/ui/assets/{javascript-COWkS1tr.js → javascript-CcBUpTjJ.js} +1 -1
  52. package/dist/ui/assets/javascript.contribution-D6DhUpPP.js +2 -0
  53. package/dist/ui/assets/{jsonMode-CWWZvfzr.js → jsonMode-D9SbmDW6.js} +1 -1
  54. package/dist/ui/assets/{kotlin.contribution-CxSOPQ6P.js → kotlin.contribution-B9AYd3Yp.js} +1 -1
  55. package/dist/ui/assets/layouts-2A7QNF6Y-D94iKLLQ.js +1 -0
  56. package/dist/ui/assets/{less.contribution-Cthyflev.js → less.contribution-DtKQK3HF.js} +1 -1
  57. package/dist/ui/assets/{lua.contribution-894DRgPR.js → lua.contribution-DxFFDcBW.js} +1 -1
  58. package/dist/ui/assets/{markdown.contribution-d_TBTsNA.js → markdown.contribution-B7aCxle-.js} +1 -1
  59. package/dist/ui/assets/{monaco-PebRhLpo.js → monaco-BBFILBn6.js} +3 -3
  60. package/dist/ui/assets/{monaco-base-4S0M5BiY.js → monaco-base-CvsKN-Z-.js} +1 -1
  61. package/dist/ui/assets/{monaco-setup-7hp0Z9jn.js → monaco-setup-BeHgXlpG.js} +1 -1
  62. package/dist/ui/assets/{monaco.contribution-CKDvyGVE.js → monaco.contribution-B-c9k_-7.js} +2 -2
  63. package/dist/ui/assets/{monaco.contribution-BL_zgiu2.js → monaco.contribution-BBrbIBfd.js} +2 -2
  64. package/dist/ui/assets/{monaco.contribution-D61txvUB.js → monaco.contribution-Baa3Cv83.js} +2 -2
  65. package/dist/ui/assets/{monaco.contribution-DQjPk7Rx.js → monaco.contribution-DRZnYNPl.js} +2 -2
  66. package/dist/ui/assets/{pdf-CwJ6ZJmB.js → pdf-66KslaXf.js} +1 -1
  67. package/dist/ui/assets/{php.contribution-B8kn8QoR.js → php.contribution-vC08ovbS.js} +1 -1
  68. package/dist/ui/assets/pptx-CsdVR0xn.js +6 -0
  69. package/dist/ui/assets/{python-CHygH1eM.js → python-CevkXHVb.js} +1 -1
  70. package/dist/ui/assets/python.contribution-C6TUOYNZ.js +2 -0
  71. package/dist/ui/assets/{ruby.contribution-BQdPKFM2.js → ruby.contribution-BMZvE4z1.js} +1 -1
  72. package/dist/ui/assets/{rust.contribution-D7wKqShc.js → rust.contribution-DOWKjtrn.js} +1 -1
  73. package/dist/ui/assets/{scss.contribution-kSTLMoty.js → scss.contribution-BXLJWEv0.js} +1 -1
  74. package/dist/ui/assets/{shell.contribution-BQnT3BBJ.js → shell.contribution-gbqcscnj.js} +1 -1
  75. package/dist/ui/assets/{sql.contribution-C9z55nWb.js → sql.contribution-DO39C_-u.js} +1 -1
  76. package/dist/ui/assets/{swift.contribution-BZoW5ZZj.js → swift.contribution-D4Vjb2VA.js} +1 -1
  77. package/dist/ui/assets/{terminal-monaco-setup-DbSXji0d.js → terminal-monaco-setup-Bb4lnoOq.js} +1 -1
  78. package/dist/ui/assets/{tsMode-jPpKNC6E.js → tsMode-BAHQHAnm.js} +1 -1
  79. package/dist/ui/assets/{typescript-Djn_8BHF.js → typescript-15FhCrR2.js} +1 -1
  80. package/dist/ui/assets/typescript.contribution-D30L44Mw.js +2 -0
  81. package/dist/ui/assets/xlsx-CdLmEC42.js +4 -0
  82. package/dist/ui/assets/{xml-DmE4Aqa-.js → xml-BA1mrkDo.js} +1 -1
  83. package/dist/ui/assets/xml.contribution-CN9NFcUA.js +2 -0
  84. package/dist/ui/assets/{yaml-BjmK3C-c.js → yaml-CIqCv-ro.js} +1 -1
  85. package/dist/ui/assets/yaml.contribution-BsLB8vrs.js +2 -0
  86. package/dist/ui/index.html +15 -15
  87. package/package.json +16 -15
  88. package/dist/postinstall.d.ts +0 -2
  89. package/dist/postinstall.js +0 -40
  90. package/dist/ui/assets/chunk-ILCJ3WFD-uwKSpQd8.js +0 -4
  91. package/dist/ui/assets/chunk-USU6HTKB-hbaA8KyS.js +0 -1
  92. package/dist/ui/assets/dist-i9jV8CX8.js +0 -2
  93. package/dist/ui/assets/dist-jh33rACm.js +0 -195
  94. package/dist/ui/assets/docx-CNW-xqHT.js +0 -2
  95. package/dist/ui/assets/extract-UTAOG7LV-WxmEnrrc.js +0 -1
  96. package/dist/ui/assets/handlebars.contribution-DDSQBNPu.js +0 -2
  97. package/dist/ui/assets/html.contribution-DrClfj2L.js +0 -2
  98. package/dist/ui/assets/index-Caz-FRQy.css +0 -1
  99. package/dist/ui/assets/infer-Cc7ks6NX.js +0 -2
  100. package/dist/ui/assets/javascript.contribution-DMEzq0y4.js +0 -2
  101. package/dist/ui/assets/layouts-2A7QNF6Y-DR7-KboS.js +0 -1
  102. package/dist/ui/assets/pptx-Bmj2v9CT.js +0 -6
  103. package/dist/ui/assets/python.contribution-CmwCxp-v.js +0 -2
  104. package/dist/ui/assets/typescript.contribution-D4JmB9tz.js +0 -2
  105. package/dist/ui/assets/xlsx-BCnO44v8.js +0 -4
  106. package/dist/ui/assets/xml.contribution-BepqhMeK.js +0 -2
  107. package/dist/ui/assets/yaml.contribution-BwT8qEVt.js +0 -2
  108. package/postinstall.mjs +0 -8
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _bendyline_gezel from '@bendyline/gezel';
2
- import { HistoryEvent, HistoryFilter, HistoryEntry, GezelGender, Poppetje, ProjectFileEntry, GezelDetail, GezelConfig, GezelSummary, ProviderName, ChatModelTuning, GezelTrait, ProjectLocalConfig, ImportProvenance, PendingImports, GezelFrontmatter, Project, ProjectDetail, ProjectNudgeConfig, ProjectTabVisibility, ProjectActivity, MeesterStatusReport, MeesterStatusState, ProjectGitHub, ChatSession, ChatSessionSummary, TimelineMessage, TerminalTimelineEntry, TerminalThread, TerminalThreadSummary, TerminalMessage, GezelGrowthState, Task, Question, TaskNote, ToolsetsScope, InstalledToolset, ToolsetConfig, CraftbookSummary, Craftbook, ProjectCraftbookProvenance, ChannelStatus, ChannelName, ChatEvent, ChatEventEnvelope, HookSpec, HookPhase, HookResult, ModelTier, ModelStyle, BehaviorId, ModelFamily, ChatMessageToolCall, SamplingBlock, CodexPermissionModeCompat, ModelFitnessTrigger, ModelFitnessRecord, OutlineFileResponse, FileContextResponse, FindSymbolResponse, ReadSymbolResponse, MapRepoResponse, SecurityScanResponse, ScanFindingsRequest, ScanFindingsResponse, SecurityFindingWire, MapAttackSurfaceResponse, ListDependenciesResponse, SecurityOverviewResponse, TraceTaintResponse, FileMapRequest, FileMapResponse, SearchCodeResponse, FileReviewResponse, ListFileIssuesRequest, ListFileIssuesResponse, SearchDocsResponse, ReadDocAsMarkdownResponse, SearchImagesResponse, FindSimilarImagesResponse, DescribeFolderResponse, FindEntityResponse, ListEntityMentionsResponse, KeurmeesterCaseRecord, TaskStatus, KeurmeesterTriggerKind, KeurmeesterAction, KeurmeesterCaseOutcome, GateCheck, TaskCraftbookStep, TaskCraftbookSource, CraftbookConnectorNeed, CreateTaskRequest, UpdateTaskRequest, TaskAssignee, NewCraftbookStep, StepPosition, UpdateTaskStepRequest, TaskNoteAuthor, TaskVariation, ScriptCapability, ScriptScope, ScriptRunTrigger, ScriptRun, NativeEngineResolveEvent, NativeEngineName, ImageRecognition, RecognitionMode, RecognitionHealth, PreviewLogEntry, SessionGpuTask, SessionTelemetry, AIEngagementMode, ChatMessage, SessionDebugSnapshot, ExpectedDeliverable, CodexSetupStatusResponse, ConfigureCodexRequest, ProjectConnectorBinding, GildeUpdateStatusResponse, GitWorkingChange, GitFileDiffResponse, CodeReviewKind, GitLogEntry, GitCommitDetailResponse, GitSyncResponse, GitConflictFile, GitConflictVersionsResponse, WorkspaceIndexStatus, WorkspaceCommandIndex, WorkspaceSkillIndex, WorkspaceInstructionIndex, CodeReviewRecord, CodeReview, GitHubPullSummary, GitHubPullDetail, GitHubPullFile, GitHubPullComment, ImageModelPullEvent as ImageModelPullEvent$1, VideoAccelerator, RenderImageRequest, ReportActionsResponse, FireReportActionResponse, ReportActionRecord, UnifiedSearchResult, SystemToolsetInstallEvent, SystemToolsetInstallSnapshot, NightShiftWindow, TerminalEventEnvelope, DiscoveredCommand, ServiceRole, ScriptMeta } from '@bendyline/gezel';
2
+ import { HistoryEvent, HistoryFilter, HistoryEntry, GezelGender, Poppetje, ProjectFileEntry, GezelDetail, GezelConfig, GezelSummary, ProviderName, ChatModelTuning, GezelTrait, ProjectLocalConfig, ImportProvenance, PendingImports, GezelFrontmatter, Project, ProjectDetail, FileReviewIssue, BoekwachterIssueStatus, BoekwachterIssueDismissalReason, ProjectManagedWorkspaceWritePolicy, ProjectNudgeConfig, ProjectTabVisibility, ProjectActivity, MeesterStatusReport, MeesterStatusState, ProjectGitHub, ChatSession, ChatSessionSummary, TimelineMessage, TerminalTimelineEntry, TerminalThread, TerminalThreadSummary, TerminalMessage, GezelGrowthState, Task, Question, TaskNote, ToolsetsScope, InstalledToolset, ToolsetConfig, CraftbookSummary, Craftbook, ProjectCraftbookProvenance, ChannelStatus, ChannelName, ChatEvent, ChatEventEnvelope, HookSpec, HookPhase, HookResult, ModelTier, ModelStyle, BehaviorId, ModelFamily, ChatMessageToolCall, SamplingBlock, CodexPermissionModeCompat, ModelFitnessTrigger, ModelFitnessRecord, OutlineFileResponse, FileContextResponse, FindSymbolResponse, ReadSymbolResponse, MapRepoResponse, SecurityScanResponse, ScanFindingsRequest, ScanFindingsResponse, SecurityFindingWire, MapAttackSurfaceResponse, ListDependenciesResponse, SecurityOverviewResponse, TraceTaintResponse, FileMapRequest, FileMapResponse, SearchCodeResponse, FileReviewResponse, ListFileIssuesRequest, ListFileIssuesResponse, BoekwachterIssue, SearchDocsResponse, ReadDocAsMarkdownResponse, SearchImagesResponse, FindSimilarImagesResponse, DescribeFolderResponse, FindEntityResponse, ListEntityMentionsResponse, KeurmeesterCaseRecord, TaskStatus, KeurmeesterTriggerKind, KeurmeesterAction, KeurmeesterCaseOutcome, GateCheck, TaskCraftbookStep, TaskCraftbookSource, CraftbookConnectorNeed, CreateTaskRequest, UpdateTaskRequest, TaskAssignee, NewCraftbookStep, StepPosition, UpdateTaskStepRequest, TaskNoteAuthor, TaskVariation, ScriptCapability, ScriptScope, ScriptRunTrigger, ScriptRun, NativeEngineResolveEvent, NativeEngineName, ImageRecognition, RecognitionMode, RecognitionHealth, PreviewLogEntry, SessionGpuTask, SessionTelemetry, AIEngagementMode, ChatMessage, SessionDebugSnapshot, ExpectedDeliverable, CodexSetupStatusResponse, ConfigureCodexRequest, ProjectConnectorBinding, GildeUpdateStatusResponse, GitWorkingChange, GitFileDiffResponse, CodeReviewKind, GitLogEntry, GitCommitDetailResponse, GitSyncResponse, GitConflictFile, GitConflictVersionsResponse, WorkspaceIndexStatus, WorkspaceCommandIndex, WorkspaceSkillIndex, WorkspaceInstructionIndex, CodeReviewRecord, CodeReview, GitHubPullSummary, GitHubPullDetail, GitHubPullFile, GitHubPullComment, ImageModelPullEvent as ImageModelPullEvent$1, VideoAccelerator, RenderImageRequest, ReportActionsResponse, FireReportActionResponse, ReportActionRecord, UnifiedSearchResult, SystemToolsetInstallEvent, SystemToolsetInstallSnapshot, NightShiftWindow, TerminalEventEnvelope, DiscoveredCommand, ServiceRole, ScriptMeta } from '@bendyline/gezel';
3
3
  import { ServerType } from '@hono/node-server';
4
4
  import { CatalogService } from '@bendyline/gezel-catalog';
5
5
  import { ExternalFolders } from '@bendyline/gezel/paths';
@@ -287,8 +287,39 @@ interface ProjectFindingLifecycleEntry {
287
287
  resolvedAt?: string;
288
288
  }
289
289
  type ProjectFindingLifecycle = Record<string, ProjectFindingLifecycleEntry>;
290
+ interface ProjectBoekwachterIssueRecord extends FileReviewIssue {
291
+ id: string;
292
+ ref: string;
293
+ fingerprint: string;
294
+ path: string;
295
+ status: BoekwachterIssueStatus;
296
+ taskRef?: string;
297
+ dismissalReason?: BoekwachterIssueDismissalReason;
298
+ createdAt: string;
299
+ lastSeenAt: string;
300
+ lastSeenContentHash: string;
301
+ lastCheckedAt?: string;
302
+ lastCheckedContentHash?: string;
303
+ seenAt?: string;
304
+ resolvedAt?: string;
305
+ dismissedAt?: string;
306
+ }
307
+ interface BoekwachterReviewObservation {
308
+ path: string;
309
+ contentHash: string;
310
+ issues: FileReviewIssue[];
311
+ }
290
312
  interface StoreOptions {
291
313
  home: string;
314
+ /**
315
+ * Whether `home` is private to one OS user. On Unix these homes are created
316
+ * and repaired as 0700 so ordinary 0644 state files remain unreachable to
317
+ * other local accounts. System/machine homes must opt out because their
318
+ * installer-managed runtime discovery is intentionally shared.
319
+ *
320
+ * Defaults to true. Production system-scope stores opt out explicitly.
321
+ */
322
+ privateUserHome?: boolean;
292
323
  /**
293
324
  * Optional history recorder. When set, the Store emits audit events after
294
325
  * successful writes (create/rename/update/delete for gezels + projects +
@@ -348,6 +379,8 @@ declare class Store {
348
379
  private readonly external?;
349
380
  /** True for a `machine-engine` broker: engines and models, no product state. */
350
381
  private readonly engineOnly;
382
+ /** Whether the Store owns a home private to the current OS account. */
383
+ private readonly privateUserHome;
351
384
  private readonly poppetjes;
352
385
  private readonly documents;
353
386
  private readonly artifacts;
@@ -355,6 +388,7 @@ declare class Store {
355
388
  private readonly taskFiles;
356
389
  private projectCreationTail;
357
390
  private readonly findingLifecycleLocks;
391
+ private readonly boekwachterIssueLocks;
358
392
  /**
359
393
  * Notified after every session persist/delete — the single choke point all
360
394
  * ChatManager write paths funnel through, so a subscriber sees every
@@ -835,7 +869,31 @@ declare class Store {
835
869
  settleProjectFindingsForTask(id: string, taskRef: string, outcome: 'complete' | 'canceled'): Promise<number>;
836
870
  /** Remove lifecycle rows after a completed scan proves a finding is gone. */
837
871
  reconcileProjectFindingLifecycle(id: string, liveFingerprints: Set<string>): Promise<void>;
872
+ /**
873
+ * Materialize current review output into durable BW records. Review rows are
874
+ * disposable and content-hash keyed; these records intentionally are not.
875
+ */
876
+ observeProjectBoekwachterReviews(id: string, observations: readonly BoekwachterReviewObservation[]): Promise<void>;
877
+ listProjectBoekwachterIssues(id: string): Promise<ProjectBoekwachterIssueRecord[]>;
878
+ getProjectBoekwachterIssue(id: string, ref: string): Promise<ProjectBoekwachterIssueRecord | null>;
879
+ updateProjectBoekwachterIssue(id: string, ref: string, patch: {
880
+ status?: BoekwachterIssueStatus;
881
+ seen?: boolean;
882
+ taskRef?: string;
883
+ dismissalReason?: BoekwachterIssueDismissalReason;
884
+ }): Promise<ProjectBoekwachterIssueRecord | null>;
885
+ settleProjectBoekwachterIssuesForTask(id: string, taskRef: string, outcome: 'complete' | 'canceled'): Promise<number>;
886
+ private readProjectBoekwachterIssuesFile;
887
+ private writeProjectBoekwachterIssuesFile;
888
+ private withBoekwachterIssueLock;
838
889
  private withFindingLifecycleLock;
890
+ /**
891
+ * Persist project metadata through one compatibility boundary. Any write to
892
+ * a legacy project transparently replaces `allowGezelWrites` with the named
893
+ * managed-write policy, so old input remains readable without letting the
894
+ * deprecated field spread through newly written project files.
895
+ */
896
+ private writeProjectMeta;
839
897
  touchProject(id: string): Promise<void>;
840
898
  /**
841
899
  * A project `workingDir` is settable by the model (via `update_project`)
@@ -869,8 +927,11 @@ declare class Store {
869
927
  } | null;
870
928
  /** External-data connector bindings — `null` clears them. */
871
929
  connectors?: _bendyline_gezel.ProjectConnectorBinding[] | null;
930
+ managedWorkspaceWritePolicy?: ProjectManagedWorkspaceWritePolicy;
931
+ /** @deprecated Compatibility input; translated into the named policy. */
872
932
  allowGezelWrites?: boolean;
873
933
  codexPermissionMode?: _bendyline_gezel.CodexPermissionMode;
934
+ claudePermissionMode?: _bendyline_gezel.ClaudePermissionMode;
874
935
  /** Replaces the stored per-project ambient nudge override. */
875
936
  nudgeConfig?: ProjectNudgeConfig;
876
937
  /** Replaces the optional project-tab visibility overrides. */
@@ -1179,13 +1240,13 @@ declare class Store {
1179
1240
  /**
1180
1241
  * Gate mutations. Uses the same {@link resolveWorkspaceDir} as the
1181
1242
  * read path, then applies the per-project writability contract
1182
- * (`projectWorkspaceWritable` in core):
1243
+ * (`projectManagedWorkspaceWritable` in core):
1183
1244
  *
1184
1245
  * - `internal` / `githubCheckout`: writable unless the project
1185
- * explicitly set `allowGezelWrites: false` (the per-project
1246
+ * explicitly set the managed-write policy to `deny` (the per-project
1186
1247
  * "edits off" switch — gezel-initiated writes only; app-internal
1187
1248
  * `.gezel` bookkeeping and user-initiated writes stay exempt).
1188
- * - `workingDir`: writable iff `allowGezelWrites === true`. The
1249
+ * - `workingDir`: writable iff the managed-write policy is `allow`. The
1189
1250
  * user has to explicitly opt gezels into mutating their own folder.
1190
1251
  *
1191
1252
  * The global security policy deliberately does not factor in — the
@@ -1208,7 +1269,7 @@ declare class Store {
1208
1269
  external: boolean;
1209
1270
  } | {
1210
1271
  ok: false;
1211
- reason: 'missing-flag-external' | 'disabled-by-project';
1272
+ reason: 'external-consent-required' | 'disabled-by-project';
1212
1273
  workingDir: string;
1213
1274
  }>;
1214
1275
  statProjectWorkspacePath(id: string, filePath: string): Promise<{
@@ -2589,10 +2650,18 @@ interface ProviderUsage {
2589
2650
  quotaBuckets: QuotaBucket[];
2590
2651
  todayTurns: number;
2591
2652
  todayTokensIn: number;
2653
+ /**
2654
+ * Portion of `todayTokensIn` the provider reported as prompt-cache reads.
2655
+ * Zero both when nothing was cached and when the provider reports no
2656
+ * breakdown (e.g. Copilot) — absence of data and absence of hits are
2657
+ * indistinguishable here, so render 0 as "no cache info", not "0% hit".
2658
+ */
2659
+ todayTokensCached: number;
2592
2660
  todayTokensOut: number;
2593
2661
  todayCost: number;
2594
2662
  totalTurns: number;
2595
2663
  totalTokensIn: number;
2664
+ totalTokensCached: number;
2596
2665
  totalTokensOut: number;
2597
2666
  totalCost: number;
2598
2667
  /**
@@ -2844,7 +2913,7 @@ declare class ProviderQueue {
2844
2913
  private readonly affinityWindowMs;
2845
2914
  private readonly maxWaitMs;
2846
2915
  /** See {@link ProviderQueueOptions.ambientQuietMs}. 0 = gate off. */
2847
- private readonly ambientQuietMs;
2916
+ readonly ambientQuietMs: number;
2848
2917
  /**
2849
2918
  * Last time NON-ambient work touched this queue (enqueue, dispatch,
2850
2919
  * or release — any lane). Initialized to construction time so a
@@ -4853,18 +4922,36 @@ declare class ModelFitnessManager {
4853
4922
  }
4854
4923
 
4855
4924
  /**
4856
- * Per-file semantic enrichment (the boekwachter's unit of work): produce an
4857
- * optional one-paragraph summary via a local LLM, build embeddable chunks, and
4858
- * store their vectors. Embeddings run locally (transformers.js) and are the
4859
- * always-on part; the LLM summary is best-effort (skipped when no local model
4860
- * is configured). Hash-gated by the caller via `filesNeedingEnrichment`.
4861
- *
4862
- * Deps are injected so this is unit-testable without a real model:
4863
- * - `summarize(prompt)` → summary text, or '' to skip (e.g. no LLM)
4864
- * - `embed(texts)` → one vector per text
4925
+ * Who produced an LLM-written index row. Output-only bookkeeping never an
4926
+ * input to model routing. Absent fields stay NULL (rows written before the
4927
+ * v10 migration, or deps built without a boekwachter) and renderers degrade
4928
+ * to whatever segments exist.
4865
4929
  */
4930
+ interface IndexProvenance {
4931
+ provider?: string;
4932
+ gezelId?: string;
4933
+ gezelName?: string;
4934
+ appVersion?: string;
4935
+ }
4936
+
4937
+ interface AiShadowProducers {
4938
+ /** Absolute image path → markdown body (description + OCR), null when unavailable. */
4939
+ describeImage?: (absPath: string) => Promise<{
4940
+ body: string;
4941
+ model?: string;
4942
+ } | null>;
4943
+ /** Absolute audio path → markdown transcript body, null when unavailable. */
4944
+ transcribeAudio?: (absPath: string) => Promise<{
4945
+ body: string;
4946
+ model?: string;
4947
+ } | null>;
4948
+ }
4949
+ interface AiShadowDeps extends AiShadowProducers {
4950
+ provenance?: IndexProvenance;
4951
+ }
4952
+
4866
4953
  interface EnrichDeps {
4867
- summarize: (prompt: string) => Promise<string>;
4954
+ summarize: (prompt: string) => Promise<EnrichCompletionResult>;
4868
4955
  embed: (texts: string[]) => Promise<number[][]>;
4869
4956
  model?: string;
4870
4957
  /**
@@ -4873,14 +4960,50 @@ interface EnrichDeps {
4873
4960
  * output tokens than a 2-3 sentence summary. Absent when no local model is
4874
4961
  * configured; the review pass skips entirely then.
4875
4962
  */
4876
- review?: (prompt: string) => Promise<string>;
4963
+ review?: (prompt: string) => Promise<EnrichCompletionResult>;
4964
+ /**
4965
+ * Default/primary attribution for legacy bare-string completions and
4966
+ * non-completion work. Runtime completions carry their own actual target so
4967
+ * a fallback can override this per result. Output-only, never a routing
4968
+ * input.
4969
+ */
4970
+ provenance?: IndexProvenance;
4971
+ /**
4972
+ * Live concurrent-one-shot width of the resolved target's queue. Full
4973
+ * drives dispatch this many per-file scans at once instead of one; the
4974
+ * value is re-read as slots free because a lazily-initialized provider
4975
+ * reports 1 until its first call spins it up.
4976
+ */
4977
+ oneShotWidth?: () => number;
4877
4978
  }
4979
+ /**
4980
+ * One LLM completion plus the identity that actually produced it. Hand-built
4981
+ * deps may still return a bare string; consumers resolve those through the
4982
+ * deps-level defaults for backwards compatibility. Runtime deps always return
4983
+ * this richer shape so a policy fallback cannot inherit the primary target's
4984
+ * provenance.
4985
+ */
4986
+ interface EnrichCompletion {
4987
+ text: string;
4988
+ model: string;
4989
+ provenance?: IndexProvenance;
4990
+ }
4991
+ type EnrichCompletionResult = string | EnrichCompletion;
4878
4992
 
4879
4993
  interface AreaPassResult {
4880
4994
  areasUpdated: number;
4881
4995
  architectureUpdated: boolean;
4882
4996
  }
4883
4997
 
4998
+ interface ArtifactsIndexStats {
4999
+ /** Markdown records the walk saw (post-skip rules). */
5000
+ files: number;
5001
+ /** Records whose content was (re)ingested this pass. */
5002
+ indexed: number;
5003
+ /** Rows pruned because their record vanished. */
5004
+ removed: number;
5005
+ }
5006
+
4884
5007
  interface ContentIndexStats {
4885
5008
  scanned: number;
4886
5009
  changed: number;
@@ -4897,13 +5020,39 @@ interface ResolvedRubric {
4897
5020
  source: 'builtin' | 'override';
4898
5021
  }
4899
5022
 
5023
+ /**
5024
+ * Pool options for the AI passes. `concurrency` is the live width of the
5025
+ * summarizer target's queue (full drives pass it; background stays serial).
5026
+ * `drain` switches a call from "one fixed batch" to "run to empty": the
5027
+ * work-list is re-queried as slots open so the pool never sits idle at a
5028
+ * batch tail. `shouldStop` (the indexing job's pause switch) is checked and
5029
+ * `onProgress` fired at each refill — the same cadence the caller's old
5030
+ * per-batch loop provided.
5031
+ */
5032
+ interface EnrichDriveOpts {
5033
+ concurrency?: () => number;
5034
+ drain?: {
5035
+ shouldStop?: () => Promise<boolean> | boolean;
5036
+ onProgress?: (progress: {
5037
+ files: number;
5038
+ }) => void;
5039
+ };
5040
+ }
4900
5041
  declare class ContentIndex {
4901
5042
  private readonly store;
4902
5043
  private readonly home;
4903
5044
  /** Per-project village-file stores (they cache last-written content so
4904
5045
  * no-change builds skip touching the user's repo). */
4905
5046
  private readonly cityStores;
4906
- constructor(store: Store, home: string);
5047
+ /** Debounce window for {@link refreshArtifacts}; tests shrink it. */
5048
+ private readonly artifactsDebounceMs;
5049
+ /** Per-project pending artifacts pass — calls within the window join it. */
5050
+ private readonly artifactsRefreshPending;
5051
+ /** Per-project settle of the last dispatched pass, so passes never overlap. */
5052
+ private readonly artifactsRefreshLast;
5053
+ constructor(store: Store, home: string, opts?: {
5054
+ artifactsDebounceMs?: number;
5055
+ });
4907
5056
  private cityStoreFor;
4908
5057
  /** Bring the project's content index up to date. Called by the indexer tick. */
4909
5058
  refresh(projectId: string): Promise<ContentIndexStats | null>;
@@ -4974,13 +5123,26 @@ declare class ContentIndex {
4974
5123
  summarized: number;
4975
5124
  embedded: number;
4976
5125
  pending: number;
5126
+ skipped: number;
5127
+ shadowsPending: number;
4977
5128
  embedModel?: string;
4978
5129
  } | null>;
4979
- enrich(projectId: string, deps: EnrichDeps, limit?: number): Promise<{
5130
+ enrich(projectId: string, deps: EnrichDeps, limit?: number, opts?: EnrichDriveOpts): Promise<{
4980
5131
  files: number;
4981
5132
  summarized: number;
4982
5133
  embedded: number;
4983
5134
  } | null>;
5135
+ /**
5136
+ * Run one batch of the AI-shadow tier: describe images / transcribe audio
5137
+ * into `artifacts/shadow/` sidecars. Runs BEFORE the summary tier so a
5138
+ * produced shadow immediately joins the enrichment work-list. No-op when
5139
+ * neither producer is wired (no vision model / no STT configured).
5140
+ */
5141
+ aiShadows(projectId: string, deps: AiShadowDeps, limit?: number): Promise<{
5142
+ files: number;
5143
+ produced: number;
5144
+ called: number;
5145
+ } | null>;
4984
5146
  /**
4985
5147
  * Cheap existence probe — true when a content index db is on disk for
4986
5148
  * this project. File-stat only; never opens (an open would CREATE the
@@ -5008,7 +5170,7 @@ declare class ContentIndex {
5008
5170
  * whole night budget in timeouts). Rubrics can be injected (one resolve per
5009
5171
  * manager tick; also the test seam). Null when no index exists.
5010
5172
  */
5011
- review(projectId: string, deps: EnrichDeps, limit?: number, rubrics?: Map<string, ResolvedRubric>): Promise<{
5173
+ review(projectId: string, deps: EnrichDeps, limit?: number, rubrics?: Map<string, ResolvedRubric>, opts?: EnrichDriveOpts): Promise<{
5012
5174
  files: number;
5013
5175
  reviewed: number;
5014
5176
  } | null>;
@@ -5023,8 +5185,41 @@ declare class ContentIndex {
5023
5185
  fileReview(projectId: string, relPath: string): Promise<FileReviewResponse>;
5024
5186
  /** Flat issue query across current-hash reviews (`list_file_issues` tool). */
5025
5187
  listFileIssues(projectId: string, req: ListFileIssuesRequest): Promise<ListFileIssuesResponse>;
5188
+ getBoekwachterIssue(projectId: string, ref: string): Promise<BoekwachterIssue | null>;
5189
+ updateBoekwachterIssue(projectId: string, ref: string, patch: {
5190
+ status?: BoekwachterIssueStatus;
5191
+ seen?: boolean;
5192
+ taskRef?: string;
5193
+ dismissalReason?: BoekwachterIssueDismissalReason;
5194
+ }): Promise<BoekwachterIssue | null>;
5195
+ settleBoekwachterIssuesForTask(projectId: string, taskRef: string, outcome: 'complete' | 'canceled'): Promise<number>;
5196
+ private boekwachterIssuesForPath;
5026
5197
  searchDocs(projectId: string, query: string, maxResults?: number): Promise<SearchDocsResponse>;
5027
5198
  readDocAsMarkdown(projectId: string, relPath: string): Promise<ReadDocAsMarkdownResponse>;
5199
+ /**
5200
+ * Schedule a rebuild of the project's artifacts-corpus index. Debounced per
5201
+ * project: calls landing within the window join the same pass, so a burst
5202
+ * of connector syncs costs one walk; passes for a project never overlap.
5203
+ * This is the only artifacts-index method that CREATES the database.
5204
+ */
5205
+ refreshArtifacts(projectId: string): Promise<ArtifactsIndexStats | null>;
5206
+ /** Docs-FTS over the project's artifacts corpora. Empty until a refresh has built the index. */
5207
+ searchArtifacts(projectId: string, query: string, maxResults?: number): Promise<{
5208
+ results: Array<{
5209
+ path: string;
5210
+ lineStart: number;
5211
+ snippet: string;
5212
+ }>;
5213
+ truncated: boolean;
5214
+ }>;
5215
+ /** Indexed artifact record paths (artifacts-relative), for the search catalog. */
5216
+ listArtifactIndexFiles(projectId: string, cap?: number): Promise<string[]>;
5217
+ /**
5218
+ * Open the artifacts-corpus collection for a READ. Stat-first — opening a
5219
+ * missing db would create it (same discipline as {@link hasIndex}), so pure
5220
+ * reads return null until {@link refreshArtifacts} has built one.
5221
+ */
5222
+ private openArtifacts;
5028
5223
  searchImages(projectId: string, query: string, maxResults?: number): Promise<SearchImagesResponse>;
5029
5224
  findSimilarImages(projectId: string, relPath: string, maxResults?: number): Promise<FindSimilarImagesResponse>;
5030
5225
  describeFolder(projectId: string, prefix?: string): Promise<DescribeFolderResponse>;
@@ -7378,6 +7573,17 @@ interface IncompleteModelDownload {
7378
7573
  * stray files left without one). */
7379
7574
  hasPartial: boolean;
7380
7575
  }
7576
+ /** Inventory row for a model directory whose manifest exists but is not
7577
+ * loadable by the current engine manager. */
7578
+ interface UnrecognizedModelInfo {
7579
+ id: string;
7580
+ name?: string;
7581
+ bytes: number;
7582
+ updatedAt: string;
7583
+ reason: string;
7584
+ canUpdate: boolean;
7585
+ readOnly?: boolean;
7586
+ }
7381
7587
  /**
7382
7588
  * An incomplete download enriched with per-engine catalog context. Managers
7383
7589
  * return this from `listIncomplete()`; the route serializes it as-is.
@@ -7585,6 +7791,9 @@ declare class MlxModelManager {
7585
7791
  * In-flight installs are excluded. See {@link LlamaCppModelManager.listIncomplete}.
7586
7792
  */
7587
7793
  listIncomplete(): Promise<IncompleteModelDownloadInfo[]>;
7794
+ /** Management-only inventory for MLX directories whose manifest exists
7795
+ * but no longer matches the current installed-manifest contract. */
7796
+ listUnrecognized(): Promise<UnrecognizedModelInfo[]>;
7588
7797
  resolveDefaultModel(): Promise<InstalledMlxModel | null>;
7589
7798
  resolveDefaultModelPath(): Promise<string | null>;
7590
7799
  resolveModel(id: string): Promise<InstalledMlxModel | null>;
@@ -8010,6 +8219,15 @@ declare class LlamaCppModelManager {
8010
8219
  * delete-only for stale ids. In-flight installs are excluded.
8011
8220
  */
8012
8221
  listIncomplete(): Promise<IncompleteModelDownloadInfo[]>;
8222
+ /**
8223
+ * Model directories that do have a manifest, but whose metadata cannot be
8224
+ * loaded by this build. These used to disappear from every inventory
8225
+ * surface: `listInstalled()` correctly refused to run them, while the
8226
+ * incomplete-download scan correctly ignored anything with a manifest.
8227
+ * Keep that safety boundary, but return a management-only row so Settings
8228
+ * can update a known catalog id or explicitly remove the held bytes.
8229
+ */
8230
+ listUnrecognized(): Promise<UnrecognizedModelInfo[]>;
8013
8231
  /**
8014
8232
  * Path of the first installed model, or null when none exist. Phase 2
8015
8233
  * uses this to pick a default `--model` arg for the supervised
@@ -9289,6 +9507,12 @@ declare class ChatManager {
9289
9507
  projectId?: string;
9290
9508
  expectedDeliverable?: ExpectedDeliverable;
9291
9509
  }): Promise<ChatSession>;
9510
+ /**
9511
+ * Resolve the active session for one exact task step. Scheduler re-drives
9512
+ * use this path so task tools inherit GEZEL_TASK_REF / GEZEL_STEP_ID and
9513
+ * continue the original work thread instead of landing in lobby chat.
9514
+ */
9515
+ private ensureOrCreateTaskSession;
9292
9516
  /** Late-bind the fitness manager (see the field's docblock). */
9293
9517
  setModelFitness(manager: ModelFitnessManager): void;
9294
9518
  /**
@@ -9388,7 +9612,8 @@ declare class ChatManager {
9388
9612
  * (which is anchored to a task + phase and always opens a fresh session),
9389
9613
  * `messageGezel` drops a message into the target gezel's *active* project
9390
9614
  * session via `ensureOrCreateSession`, so the continuity of their thread
9391
- * is preserved.
9615
+ * is preserved. Internal task re-drives may also pass `taskRef` + `stepId`
9616
+ * to resume that exact task-step session with its MCP context intact.
9392
9617
  *
9393
9618
  * When the target's turn completes, its reply is injected back into the
9394
9619
  * sender's session as its own handoff-styled message (role=user, `from`
@@ -9408,6 +9633,10 @@ declare class ChatManager {
9408
9633
  toGezelIdOrName: string;
9409
9634
  projectId?: string;
9410
9635
  text: string;
9636
+ /** Internal task context for scheduler re-drives. */
9637
+ taskRef?: string;
9638
+ /** Exact step paired with taskRef when re-driving task work. */
9639
+ stepId?: string;
9411
9640
  /**
9412
9641
  * Provider-queue lane for the target's reply turn. Defaults to
9413
9642
  * `interactive`. The scheduler passes `background` for ambient
@@ -10170,6 +10399,15 @@ declare class ChatManager {
10170
10399
  * "memory extraction".
10171
10400
  */
10172
10401
  jobLabel?: string;
10402
+ /**
10403
+ * Per-call catalog tuning profile. Used by tightly-scoped utility work
10404
+ * whose inference shape differs from the owning gezel's conversational
10405
+ * default (for example, index summaries should use an `instruct`
10406
+ * profile rather than spend their deadline on hidden reasoning).
10407
+ * The profile keeps its catalog-authored sampling/output allowance;
10408
+ * this option does not impose an extra token cap.
10409
+ */
10410
+ tuningProfileId?: string;
10173
10411
  /**
10174
10412
  * Truly-deferrable housekeeping (memory extraction, icon/about
10175
10413
  * generation, index enrichment, digests). On local engine queues
@@ -10229,6 +10467,15 @@ declare class ChatManager {
10229
10467
  }): Promise<ProviderName>;
10230
10468
  /** Return the provider if it has already been initialized; never creates one. */
10231
10469
  getProviderIfReady(name: ProviderName): LLMProvider | null;
10470
+ /**
10471
+ * Concurrent one-shot width for a provider: its queue's configured
10472
+ * concurrency once initialized (singleton or pooled local engine), else 1.
10473
+ * Bulk callers (index drives) dispatch width-many one-shots to keep the
10474
+ * queue full — the queue still arbitrates its interactive/background lanes.
10475
+ * Live value: an uninitialized provider reports 1 and widens after the
10476
+ * first call spins it up, so callers should re-read between dispatches.
10477
+ */
10478
+ oneShotQueueWidth(name: ProviderName): number;
10232
10479
  /**
10233
10480
  * Snapshot of the Claude CLI worker pool, enriched with gezel + project
10234
10481
  * display names so the UI pill can render "Maya · Atari Adventure
@@ -10622,6 +10869,14 @@ declare class ChatManager {
10622
10869
  */
10623
10870
  opts?: {
10624
10871
  allowUninstalled?: boolean;
10872
+ /**
10873
+ * Price the model as the only resident engine. Inventory/catalog rows
10874
+ * answer "can this model run on this device?", so their estimate must
10875
+ * not change merely because another model happens to be warm. Actual
10876
+ * launch admission keeps the default live-reservation behavior and may
10877
+ * evict an idle model (or report that a busy one is blocking the swap).
10878
+ */
10879
+ standalone?: boolean;
10625
10880
  }): Promise<{
10626
10881
  contextWindow?: number;
10627
10882
  plannedResidentBytes?: number;
@@ -10717,6 +10972,8 @@ declare class ChatManager {
10717
10972
  private immediateFileWriteConstraintActive;
10718
10973
  /** Resolve the mode that a Codex CLI session will actually receive. */
10719
10974
  private resolveCodexPermissionMode;
10975
+ /** Resolve the mode that a Claude CLI session will actually receive. */
10976
+ private resolveClaudePermissionMode;
10720
10977
  /**
10721
10978
  * D4 clamp-lifetime derivation for the rebuild check: read the
10722
10979
  * session's active step (step-scoped sessions only) and combine with
@@ -11125,7 +11382,7 @@ declare class ConnectorManager {
11125
11382
  code: string;
11126
11383
  }): Promise<ProjectConnectorBinding>;
11127
11384
  private gcPendingOAuth;
11128
- /** Remove a binding + its stored credential. */
11385
+ /** Remove a binding + its stored credential (and its origin allowlist entry). */
11129
11386
  unbind(project: ProjectDetail, bindingId: string): Promise<void>;
11130
11387
  status(project: ProjectDetail): Promise<ConnectorStatus>;
11131
11388
  /**
@@ -11139,6 +11396,13 @@ declare class ConnectorManager {
11139
11396
  }): Promise<BindingSyncResult>;
11140
11397
  /** Sync every enabled binding on a project (drives the sync manager). */
11141
11398
  syncProject(project: ProjectDetail): Promise<BindingSyncResult[]>;
11399
+ /**
11400
+ * Idempotent sync-time heal for the origin allowlist: bindings created
11401
+ * before their type declared `allowedOrigins` (or before this write path
11402
+ * existed) get the entry on their next sync, with no migration step.
11403
+ * Called under the project lock.
11404
+ */
11405
+ private ensureDeclaredOrigins;
11142
11406
  /** The per-binding sync body — always called under the project lock. */
11143
11407
  private syncBindingInner;
11144
11408
  /**
@@ -11924,6 +12188,13 @@ interface TaskRunnerOptions {
11924
12188
  * to "always pending" (only the active-shift gate applies).
11925
12189
  */
11926
12190
  isNightShiftPending?: (task: Task) => boolean;
12191
+ /**
12192
+ * True while the night-shift index catch-up sweep runs. Night-shift
12193
+ * handoffs stay held on the queue until it finishes, so batch work starts
12194
+ * against a current static + AI index instead of racing the indexer for
12195
+ * the engine. Defaults to never-active.
12196
+ */
12197
+ isIndexCatchUpActive?: () => boolean;
11927
12198
  }
11928
12199
  declare class TaskRunner {
11929
12200
  private readonly store;
@@ -11932,6 +12203,7 @@ declare class TaskRunner {
11932
12203
  private readonly now;
11933
12204
  private readonly isNightShiftActive;
11934
12205
  private readonly isNightShiftPending;
12206
+ private readonly isIndexCatchUpActive;
11935
12207
  private readonly pending;
11936
12208
  private readonly activeDispatches;
11937
12209
  private nextId;
@@ -12108,6 +12380,13 @@ declare class WorkspaceIndexManager {
12108
12380
  * - on-mount of the Commands panel when status is `never`
12109
12381
  */
12110
12382
  refresh(projectId: string): Promise<WorkspaceIndexStatus>;
12383
+ /**
12384
+ * Like {@link refresh}, but awaits the whole scan (structural pass + the
12385
+ * content-index refresh it drives). For callers that sequence work on a
12386
+ * CURRENT index — the on-demand enrichment drive and the night-shift
12387
+ * catch-up must not summarize/review against yesterday's file list.
12388
+ */
12389
+ refreshAndWait(projectId: string): Promise<WorkspaceIndexStatus>;
12111
12390
  /**
12112
12391
  * Periodic tick: pick the highest-priority stale project and index
12113
12392
  * exactly one. One project per tick keeps the loop bounded; with a
@@ -12599,6 +12878,39 @@ interface IndexEnrichmentManagerOptions {
12599
12878
  events?: Pick<ChatEventBus, 'publishGlobalEvent'>;
12600
12879
  /** Test seam; production resolves the role from each project's roster. */
12601
12880
  resolveBoekwachter?: (projectId: string) => Promise<GezelDetail | null>;
12881
+ /**
12882
+ * AI-shadow producers (vision describe / STT transcribe). Absent when the
12883
+ * install has neither capability; the shadow tier is skipped entirely then.
12884
+ */
12885
+ shadowProducers?: AiShadowProducers;
12886
+ /** Audit log for the once-per-wave `project.index.reviewed` drain event. */
12887
+ history?: {
12888
+ log: (event: Omit<HistoryEvent, 'id' | 'at'> & {
12889
+ at?: string;
12890
+ id?: string;
12891
+ }) => Promise<void>;
12892
+ };
12893
+ /**
12894
+ * Awaitable structural refresh (WorkspaceIndexManager.refreshAndWait) run
12895
+ * at the head of every drive so the AI tiers see current files. When
12896
+ * absent (tests), the drive falls back to `contentIndex.refresh` directly.
12897
+ */
12898
+ refreshStatic?: (projectId: string) => Promise<unknown>;
12899
+ }
12900
+ /**
12901
+ * On-demand drive intensity:
12902
+ * - `background` — start now (skip the idle wait) but stay polite: ambient
12903
+ * one-shots that the local engine holds behind live chat, day-size
12904
+ * batches.
12905
+ * - `full` — occupy the engine: non-ambient one-shots that compete like any
12906
+ * interactive work, night-size batches, run to drain without yielding.
12907
+ * Both honor the indexing job's pause switch between batches.
12908
+ */
12909
+ type DriveIntensity = 'background' | 'full';
12910
+ interface DriveOptions {
12911
+ intensity: DriveIntensity;
12912
+ /** Run the review tier after summaries drain (default true). */
12913
+ reviews?: boolean;
12602
12914
  }
12603
12915
  declare class IndexEnrichmentManager {
12604
12916
  private readonly store;
@@ -12612,6 +12924,13 @@ declare class IndexEnrichmentManager {
12612
12924
  private readonly isPaused;
12613
12925
  private readonly events;
12614
12926
  private readonly resolveBoekwachter;
12927
+ private readonly shadowProducers;
12928
+ private readonly history;
12929
+ private readonly refreshStatic;
12930
+ /** In-flight on-demand drives, one per project (joiners get the same run). */
12931
+ private readonly drives;
12932
+ /** Nonzero while a night-shift catch-up sweep is holding task dispatch. */
12933
+ private catchUpRuns;
12615
12934
  private startupTimer;
12616
12935
  private tickTimer;
12617
12936
  private running;
@@ -12621,6 +12940,42 @@ declare class IndexEnrichmentManager {
12621
12940
  stop(): void;
12622
12941
  /** Live, durable-for-the-tick status consumed by the Night Shift menu. */
12623
12942
  getActivity(): IndexEnrichmentActivity | null;
12943
+ /**
12944
+ * Emit the once-per-wave `project.index.reviewed` audit event: only on the
12945
+ * transition where this tick stored reviews AND the queue is now empty.
12946
+ * Per-file events would flood the ~150-kind flat log (day batches alone
12947
+ * would emit dozens per sweep); the drain is the meaningful moment.
12948
+ */
12949
+ private logReviewDrain;
12950
+ /**
12951
+ * True while a night-shift catch-up sweep runs — the TaskRunner holds
12952
+ * night-shift task dispatch on it so batch work starts against a current
12953
+ * index, per the explicit "index first, then tasks" activation contract.
12954
+ */
12955
+ isCatchUpActive(): boolean;
12956
+ /** True while an on-demand drive is running (optionally for one project). */
12957
+ isDriving(projectId?: string): boolean;
12958
+ /** Mode of the drive running for a project, or null when idle. */
12959
+ driveMode(projectId: string): DriveIntensity | null;
12960
+ /**
12961
+ * Start an on-demand drive for one project: static refresh first, then the
12962
+ * AI tiers (shadows → summaries → areas → reviews) to drain. Returns
12963
+ * immediately; progress flows over the usual `index_progress` events and
12964
+ * `/index/status` polling. One drive per project — a second request joins
12965
+ * the running one.
12966
+ */
12967
+ drive(projectId: string, opts: DriveOptions): {
12968
+ started: boolean;
12969
+ alreadyRunning: boolean;
12970
+ };
12971
+ /**
12972
+ * Bring every indexing-enabled project's static AND AI index up to date,
12973
+ * sequentially at full intensity — the night-shift activation sweep. The
12974
+ * catch-up flag is raised synchronously so a caller that kicks this (not
12975
+ * awaited) and then wakes the TaskRunner still gets the dispatch hold.
12976
+ */
12977
+ catchUpAll(): Promise<void>;
12978
+ private runDrive;
12624
12979
  /** Exposed for tests: run one batch ignoring the timers (still idle-gated). */
12625
12980
  tick(): Promise<void>;
12626
12981
  private isIdle;