@bendyline/gezel-service 1.0.0 → 1.0.2

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 (64) hide show
  1. package/dist/NOTICE.md +19 -4
  2. package/dist/bin/gezeld.js +5882 -1875
  3. package/dist/handboek-content/README.md +1 -1
  4. package/dist/handboek-content/whats-new/{1.26223.md → 1.26224.md} +6 -6
  5. package/dist/handboek-content/whats-new/whats-new-index.md +0 -8
  6. package/dist/handboek.d.ts +1 -1
  7. package/dist/handboek.js +6 -1
  8. package/dist/index-store/static-index-worker.js +42 -16
  9. package/dist/index.d.ts +187 -27
  10. package/dist/index.js +5834 -1864
  11. package/dist/ui/assets/{TerminalCodeEditor-DQICIlaI.js → TerminalCodeEditor-BpkIEKrW.js} +2 -2
  12. package/dist/ui/assets/{_.contribution-BXBGjh98.js → _.contribution-DESswd3T.js} +1 -1
  13. package/dist/ui/assets/{cpp.contribution-UC-hXlnG.js → cpp.contribution-DazoHQwn.js} +1 -1
  14. package/dist/ui/assets/{csharp.contribution-VSLdDbxa.js → csharp.contribution-B64MdTmq.js} +1 -1
  15. package/dist/ui/assets/{css.contribution-DC9uQgZx.js → css.contribution-u617lXYH.js} +1 -1
  16. package/dist/ui/assets/{cssMode-DZsXawIN.js → cssMode-Cf_QqRjJ.js} +1 -1
  17. package/dist/ui/assets/dist-DB6xxrlD.js +2 -0
  18. package/dist/ui/assets/dist-DucbO86I.js +222 -0
  19. package/dist/ui/assets/{dockerfile.contribution-PUE2MRJF.js → dockerfile.contribution-P8T1FxHz.js} +1 -1
  20. package/dist/ui/assets/{go.contribution-CsrBAk1W.js → go.contribution-BqqmPpYF.js} +1 -1
  21. package/dist/ui/assets/{handlebars-Bqyi6xyi.js → handlebars-B9qcPqjD.js} +1 -1
  22. package/dist/ui/assets/{handlebars.contribution-C6VLDI51.js → handlebars.contribution-vZslMuRE.js} +2 -2
  23. package/dist/ui/assets/{html-NLb64cMt.js → html-Fp2jEZ7j.js} +1 -1
  24. package/dist/ui/assets/{html.contribution-BP2a6izy.js → html.contribution-B1GzaEV-.js} +2 -2
  25. package/dist/ui/assets/{htmlMode-Utzysyes.js → htmlMode-B5cPhGim.js} +1 -1
  26. package/dist/ui/assets/{index-B_NFDPxp.js → index-CQSh1NUQ.js} +189 -203
  27. package/dist/ui/assets/{index-CgP6VwNS.css → index-Ch0qGtVJ.css} +1 -1
  28. package/dist/ui/assets/{ini.contribution-CfehzoVj.js → ini.contribution-XkZYQphY.js} +1 -1
  29. package/dist/ui/assets/{java.contribution-CXk8bwAp.js → java.contribution-CUQitE0a.js} +1 -1
  30. package/dist/ui/assets/{javascript-BcbxETop.js → javascript-CcBUpTjJ.js} +1 -1
  31. package/dist/ui/assets/{javascript.contribution-BRDuHf7J.js → javascript.contribution-D6DhUpPP.js} +2 -2
  32. package/dist/ui/assets/{jsonMode-BORbF5qz.js → jsonMode-D9SbmDW6.js} +1 -1
  33. package/dist/ui/assets/{kotlin.contribution-CbKeqa_3.js → kotlin.contribution-B9AYd3Yp.js} +1 -1
  34. package/dist/ui/assets/{less.contribution-BgNGBZAo.js → less.contribution-DtKQK3HF.js} +1 -1
  35. package/dist/ui/assets/{lua.contribution-CtZNsqOj.js → lua.contribution-DxFFDcBW.js} +1 -1
  36. package/dist/ui/assets/{markdown.contribution-CNyxmHy1.js → markdown.contribution-B7aCxle-.js} +1 -1
  37. package/dist/ui/assets/{monaco-Ryh-SF_b.js → monaco-BBFILBn6.js} +3 -3
  38. package/dist/ui/assets/{monaco-base-C-4W0je-.js → monaco-base-CvsKN-Z-.js} +1 -1
  39. package/dist/ui/assets/{monaco-setup-BW0DQ8MV.js → monaco-setup-BeHgXlpG.js} +1 -1
  40. package/dist/ui/assets/{monaco.contribution-Bu1oyef5.js → monaco.contribution-B-c9k_-7.js} +2 -2
  41. package/dist/ui/assets/{monaco.contribution-C57BAnnI.js → monaco.contribution-BBrbIBfd.js} +2 -2
  42. package/dist/ui/assets/{monaco.contribution-o3cuP4Wp.js → monaco.contribution-Baa3Cv83.js} +2 -2
  43. package/dist/ui/assets/{monaco.contribution-279M9-Uu.js → monaco.contribution-DRZnYNPl.js} +2 -2
  44. package/dist/ui/assets/{php.contribution-BulxzIpe.js → php.contribution-vC08ovbS.js} +1 -1
  45. package/dist/ui/assets/{python-D4zGC71G.js → python-CevkXHVb.js} +1 -1
  46. package/dist/ui/assets/{python.contribution-CDI1t73R.js → python.contribution-C6TUOYNZ.js} +2 -2
  47. package/dist/ui/assets/{ruby.contribution-DVOdhLt8.js → ruby.contribution-BMZvE4z1.js} +1 -1
  48. package/dist/ui/assets/{rust.contribution-DLR9kg2m.js → rust.contribution-DOWKjtrn.js} +1 -1
  49. package/dist/ui/assets/{scss.contribution-CYIIxBxH.js → scss.contribution-BXLJWEv0.js} +1 -1
  50. package/dist/ui/assets/{shell.contribution-D0KjHQ5E.js → shell.contribution-gbqcscnj.js} +1 -1
  51. package/dist/ui/assets/{sql.contribution-CKhLelqr.js → sql.contribution-DO39C_-u.js} +1 -1
  52. package/dist/ui/assets/{swift.contribution-DiAAMfJp.js → swift.contribution-D4Vjb2VA.js} +1 -1
  53. package/dist/ui/assets/{terminal-monaco-setup-Dhxpm7Z8.js → terminal-monaco-setup-Bb4lnoOq.js} +1 -1
  54. package/dist/ui/assets/{tsMode-DFEqg3P9.js → tsMode-BAHQHAnm.js} +1 -1
  55. package/dist/ui/assets/{typescript-X_x8S3Dp.js → typescript-15FhCrR2.js} +1 -1
  56. package/dist/ui/assets/{typescript.contribution-jI3BakLa.js → typescript.contribution-D30L44Mw.js} +2 -2
  57. package/dist/ui/assets/{xml-ANmRi8ZZ.js → xml-BA1mrkDo.js} +1 -1
  58. package/dist/ui/assets/{xml.contribution-D5t83zAi.js → xml.contribution-CN9NFcUA.js} +2 -2
  59. package/dist/ui/assets/{yaml-R4WpJved.js → yaml-CIqCv-ro.js} +1 -1
  60. package/dist/ui/assets/{yaml.contribution-DV8dAjKa.js → yaml.contribution-BsLB8vrs.js} +2 -2
  61. package/dist/ui/index.html +4 -4
  62. package/package.json +16 -11
  63. package/dist/ui/assets/dist-D6I_6t8i.js +0 -195
  64. package/dist/ui/assets/dist-a57vyaRu.js +0 -2
@@ -50,7 +50,7 @@ Body prose…
50
50
 
51
51
  `whats-new/` inverts this to get newest-first: a release article's order is
52
52
 
53
- the negated calendar line, so `1.26223` carries `order: -26223`, and the
53
+ the negated calendar line, so `1.26224` carries `order: -26224`, and the
54
54
 
55
55
  section index sits at `-999999`. Nothing else needs touching when a release
56
56
 
@@ -1,13 +1,13 @@
1
1
  ---
2
- id: whats-new/1.26223
3
- title: 1.2622311 August 2026
4
- order: -26223
2
+ id: whats-new/1.26224
3
+ title: 1.2622412 August 2026
4
+ order: -26224
5
5
  summary: Measured model scores, per-model memory controls, editable Office documents, and much stricter rules about what a gezel may install.
6
6
  ---
7
7
 
8
- # 1.2622311 August 2026
8
+ # 1.2622412 August 2026
9
9
 
10
- This is the first release with written release notes, so it covers everything since **1.26219** on 7 August. It is a big one: four days of work, and most of it is visible the moment you open the app.
10
+ This is the first release with written release notes, so it covers everything since **1.26219** on 7 August.
11
11
 
12
12
  If you read only one thing: gezel now publishes **measured scores** for the models it ships, your gezellen are much more tightly fenced in about what they may install on your machine, and Word, PowerPoint, Excel and PDF files in a project can now be edited as ordinary text.
13
13
 
@@ -21,7 +21,7 @@ The first published set covers four models measured on an M4 Max: gemma4-e4b, ge
21
21
 
22
22
  Two rules keep the board honest. A model that was only partly measured is **withheld** rather than shown with a flattering gap, and earlier rounds of testing are kept visible separately instead of being quietly overwritten.
23
23
 
24
- ## Your gezellen are on a shorter leash
24
+ ## Your gezellen are better managed
25
25
 
26
26
  A gezel that can install software is useful. A gezel that can install *anything, anywhere* is not something you should have to think about. This release closes that gap.
27
27
 
@@ -13,14 +13,6 @@ Gezel changes often. This section is the running record of those changes, one ar
13
13
 
14
14
  ::handboek-whats-new-list
15
15
 
16
- Each article is written for the person using gezel, not for the person who wrote the code. It says what is different now, what you can do that you could not do before, and what — if anything — you need to do about it. Where a change is only interesting to developers, it is marked as such so you can skip it.
17
-
18
- ## How to read a version number
19
-
20
- A gezel version looks like **1.26223.47**. The middle part is the date: `26` is the year 2026, and `223` is the day of that year — so `26223` is 11 August 2026. The last part is the build number for that day. You can always tell how old your copy of gezel is by reading the middle number.
21
-
22
- Your installed version is shown in **Settings → About**.
23
-
24
16
  ## Updating
25
17
 
26
18
  Packaged installs check for new releases on their own and offer the update when one is ready. Nothing in your work is touched by an update: your gezellen, projects, threads, memories and files all live in ordinary files on your disk, and a new version simply reads the same files.
@@ -76,7 +76,7 @@ interface CuratedArticle {
76
76
  declare function findHandboekContent(): string | null;
77
77
  /**
78
78
  * Release-note articles, newest first. Release `order` is the negated
79
- * calendar line (`1.26223` → `-26223`), so the area's own ascending sort
79
+ * calendar line (`1.26224` → `-26224`), so the area's own ascending sort
80
80
  * already puts the newest release on top; this only drops the section
81
81
  * index out of the list it introduces.
82
82
  */
package/dist/handboek.js CHANGED
@@ -356,6 +356,7 @@ var VOORMAN_STRIPPED_MEESTER_TOOLS = /* @__PURE__ */ new Set([
356
356
  "list_gilde",
357
357
  "list_projects",
358
358
  "start_project",
359
+ "start_project_from_type",
359
360
  "start_job",
360
361
  "fetch_repo",
361
362
  "fetch_diff"
@@ -516,7 +517,11 @@ var EXTERNAL_SERVICE_TOOLS = /* @__PURE__ */ new Set([
516
517
  // can neither sync-driven-send nor draft outbound while locked down.
517
518
  "draft_email",
518
519
  "queue_email",
519
- "send_email"
520
+ "send_email",
521
+ // Social post draft/publish are the same class of outbound agency.
522
+ "draft_post",
523
+ "queue_post",
524
+ "publish_post"
520
525
  ]);
521
526
  function applySecurityPolicyGates(allow, policy, stripWorkspaceWrite) {
522
527
  const policyClear = !policy || policy.allowExternalServices && policy.allowScriptExecution && policy.allowModelGit;
@@ -1012,7 +1012,7 @@ import { mkdtemp, realpath as realpath3, rm as rm2, writeFile as writeFile2 } fr
1012
1012
  import { tmpdir } from "os";
1013
1013
  import { isAbsolute as isAbsolute2, join as join3 } from "path";
1014
1014
  import { pathToFileURL } from "url";
1015
- import { windowsDetachedSpawnOptions } from "@bendyline/gezel/native";
1015
+ import { windowsHeadlessSpawnOptions } from "@bendyline/gezel/native";
1016
1016
 
1017
1017
  // src/sandbox/macos.ts
1018
1018
  function runUnderMacSandbox(command, args, ctx, opts = {}) {
@@ -1161,11 +1161,9 @@ async function readNodeHelp(nodeBin) {
1161
1161
  const child = spawn2(nodeBin, ["--help"], {
1162
1162
  stdio: ["ignore", "pipe", "pipe"],
1163
1163
  env: sandboxEnv(process.env),
1164
- // Bundled Node is a console-subsystem executable and the Session 0
1165
- // machine service can allocate no console so even this capability
1166
- // probe starts with DETACHED_PROCESS. `windowsHide`
1167
- // (CREATE_NO_WINDOW) does not do that; it still allocates.
1168
- ...windowsDetachedSpawnOptions()
1164
+ // The capability probe is owned and awaited. Hide its Windows console
1165
+ // rather than giving the short-lived process a detached one.
1166
+ ...windowsHeadlessSpawnOptions()
1169
1167
  });
1170
1168
  let stdout = "";
1171
1169
  let stderr = "";
@@ -1187,16 +1185,11 @@ async function runSandboxChild(command, args, opts) {
1187
1185
  const child = spawn2(command, args, {
1188
1186
  cwd: opts.cwd,
1189
1187
  stdio: opts.rpcChannel ? ["pipe", "pipe", "pipe", "pipe"] : ["pipe", "pipe", "pipe"],
1190
- // Keep sandbox children out of the Vitest/service process
1191
- // group. Workspace commands already do this; doing the same
1192
- // here prevents an unrelated process-group kill from closing a
1193
- // short-lived derive/script child with a null exit code.
1194
- // On Windows the same option is DETACHED_PROCESS, which also keeps a
1195
- // console-subsystem child from asking the Session 0 service for a
1196
- // console it cannot allocate — `windowsHide` (CREATE_NO_WINDOW) does
1197
- // not, because it still allocates one. `taskkill /T` is unaffected:
1198
- // detaching changes console and process group, not the recorded parent.
1199
- detached: true,
1188
+ // A separate process group is useful on POSIX for group termination.
1189
+ // Windows tree cleanup follows parent PIDs instead, so detaching there
1190
+ // only creates a new console window; keep it owned and hidden.
1191
+ detached: process.platform !== "win32",
1192
+ ...windowsHeadlessSpawnOptions(),
1200
1193
  // Allowlist-scrub the env — inheriting everything leaked tokens
1201
1194
  // (GEZEL_TOKEN, OPENAI_API_KEY, GITHUB_PERSONAL_ACCESS_TOKEN,
1202
1195
  // etc.) into the sandboxed script, letting it `fetch` them
@@ -2933,6 +2926,21 @@ var IndexStore = class _IndexStore {
2933
2926
  state='failed', attempts=COALESCE(shadow_state.attempts, 0) + 1,
2934
2927
  updated_at=excluded.updated_at`
2935
2928
  ).run(contentHash, this.collectionId, filePath, nowIso());
2929
+ return Number(
2930
+ this.db.prepare("SELECT attempts FROM shadow_state WHERE content_hash = ?").get(contentHash)?.attempts ?? 1
2931
+ );
2932
+ }
2933
+ /**
2934
+ * Terminally skip a media hash the AI-shadow tier can never process — a
2935
+ * format with no raster decoder in the vision stack (SVG is vector, ICO is
2936
+ * multi-frame). Jumps straight to the attempt cap under a distinct state so
2937
+ * the row reads as "wrong tool", not "engine flaked three times".
2938
+ */
2939
+ markAiShadowUnsupported(contentHash, filePath) {
2940
+ this.db.prepare(
2941
+ `INSERT OR REPLACE INTO shadow_state (content_hash, collection_id, file_path, state, attempts, updated_at)
2942
+ VALUES (?, ?, ?, 'unsupported', ${MAX_ENRICH_ATTEMPTS}, ?)`
2943
+ ).run(contentHash, this.collectionId, filePath, nowIso());
2936
2944
  }
2937
2945
  /**
2938
2946
  * Enrichment coverage counts. `summarized` counts real summaries (join on
@@ -3346,6 +3354,24 @@ var IndexStore = class _IndexStore {
3346
3354
  reviewedAt: row.reviewed_at
3347
3355
  };
3348
3356
  }
3357
+ /**
3358
+ * Current file-hash review observations, including clean reviews. The
3359
+ * durable Boekwachter registry consumes these so a later file edit cannot
3360
+ * erase identity/lifecycle merely by invalidating `file_reviews`.
3361
+ */
3362
+ currentFileReviewIssues() {
3363
+ return this.db.prepare(
3364
+ `SELECT f.path, f.hash AS content_hash, r.issues
3365
+ FROM files f
3366
+ JOIN file_reviews r ON r.content_hash = f.hash
3367
+ WHERE f.collection_id = ? AND f.hash IS NOT NULL AND r.notes_md IS NOT NULL
3368
+ ORDER BY f.path`
3369
+ ).all(this.collectionId).map((row) => ({
3370
+ path: row.path,
3371
+ contentHash: row.content_hash,
3372
+ issues: parseIssuesJson(row.issues)
3373
+ }));
3374
+ }
3349
3375
  /**
3350
3376
  * Files of one kind whose current hash needs a review under the given
3351
3377
  * rubric: no row yet, or covered by a DIFFERENT rubric and still holding
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,6 +287,28 @@ 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;
292
314
  /**
@@ -366,6 +388,7 @@ declare class Store {
366
388
  private readonly taskFiles;
367
389
  private projectCreationTail;
368
390
  private readonly findingLifecycleLocks;
391
+ private readonly boekwachterIssueLocks;
369
392
  /**
370
393
  * Notified after every session persist/delete — the single choke point all
371
394
  * ChatManager write paths funnel through, so a subscriber sees every
@@ -846,7 +869,31 @@ declare class Store {
846
869
  settleProjectFindingsForTask(id: string, taskRef: string, outcome: 'complete' | 'canceled'): Promise<number>;
847
870
  /** Remove lifecycle rows after a completed scan proves a finding is gone. */
848
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;
849
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;
850
897
  touchProject(id: string): Promise<void>;
851
898
  /**
852
899
  * A project `workingDir` is settable by the model (via `update_project`)
@@ -880,8 +927,11 @@ declare class Store {
880
927
  } | null;
881
928
  /** External-data connector bindings — `null` clears them. */
882
929
  connectors?: _bendyline_gezel.ProjectConnectorBinding[] | null;
930
+ managedWorkspaceWritePolicy?: ProjectManagedWorkspaceWritePolicy;
931
+ /** @deprecated Compatibility input; translated into the named policy. */
883
932
  allowGezelWrites?: boolean;
884
933
  codexPermissionMode?: _bendyline_gezel.CodexPermissionMode;
934
+ claudePermissionMode?: _bendyline_gezel.ClaudePermissionMode;
885
935
  /** Replaces the stored per-project ambient nudge override. */
886
936
  nudgeConfig?: ProjectNudgeConfig;
887
937
  /** Replaces the optional project-tab visibility overrides. */
@@ -1190,13 +1240,13 @@ declare class Store {
1190
1240
  /**
1191
1241
  * Gate mutations. Uses the same {@link resolveWorkspaceDir} as the
1192
1242
  * read path, then applies the per-project writability contract
1193
- * (`projectWorkspaceWritable` in core):
1243
+ * (`projectManagedWorkspaceWritable` in core):
1194
1244
  *
1195
1245
  * - `internal` / `githubCheckout`: writable unless the project
1196
- * explicitly set `allowGezelWrites: false` (the per-project
1246
+ * explicitly set the managed-write policy to `deny` (the per-project
1197
1247
  * "edits off" switch — gezel-initiated writes only; app-internal
1198
1248
  * `.gezel` bookkeeping and user-initiated writes stay exempt).
1199
- * - `workingDir`: writable iff `allowGezelWrites === true`. The
1249
+ * - `workingDir`: writable iff the managed-write policy is `allow`. The
1200
1250
  * user has to explicitly opt gezels into mutating their own folder.
1201
1251
  *
1202
1252
  * The global security policy deliberately does not factor in — the
@@ -1219,7 +1269,7 @@ declare class Store {
1219
1269
  external: boolean;
1220
1270
  } | {
1221
1271
  ok: false;
1222
- reason: 'missing-flag-external' | 'disabled-by-project';
1272
+ reason: 'external-consent-required' | 'disabled-by-project';
1223
1273
  workingDir: string;
1224
1274
  }>;
1225
1275
  statProjectWorkspacePath(id: string, filePath: string): Promise<{
@@ -4884,19 +4934,6 @@ interface IndexProvenance {
4884
4934
  appVersion?: string;
4885
4935
  }
4886
4936
 
4887
- /**
4888
- * AI-shadow producers: the second shadow-tree producer class. Office docs get
4889
- * deterministic squisq conversions in the STATIC pass; images and audio need
4890
- * a model — a vision description (+OCR) or an STT transcript — so they run in
4891
- * the idle-gated AI tier instead, writing the same
4892
- * `artifacts/shadow/<parent>/<basename>_files/<stem>.md` layout with a YAML
4893
- * frontmatter header carrying source hash + provenance.
4894
- *
4895
- * The sidecar is the artifact; `shadow_state` is only the work gate. The
4896
- * frontmatter's `source_hash` makes the file self-describing: a rebuilt DB
4897
- * (home-fallback flip, deleted index) re-adopts a fresh sidecar without
4898
- * re-paying the model call.
4899
- */
4900
4937
  interface AiShadowProducers {
4901
4938
  /** Absolute image path → markdown body (description + OCR), null when unavailable. */
4902
4939
  describeImage?: (absPath: string) => Promise<{
@@ -4914,7 +4951,7 @@ interface AiShadowDeps extends AiShadowProducers {
4914
4951
  }
4915
4952
 
4916
4953
  interface EnrichDeps {
4917
- summarize: (prompt: string) => Promise<string>;
4954
+ summarize: (prompt: string) => Promise<EnrichCompletionResult>;
4918
4955
  embed: (texts: string[]) => Promise<number[][]>;
4919
4956
  model?: string;
4920
4957
  /**
@@ -4923,12 +4960,12 @@ interface EnrichDeps {
4923
4960
  * output tokens than a 2-3 sentence summary. Absent when no local model is
4924
4961
  * configured; the review pass skips entirely then.
4925
4962
  */
4926
- review?: (prompt: string) => Promise<string>;
4963
+ review?: (prompt: string) => Promise<EnrichCompletionResult>;
4927
4964
  /**
4928
- * Stamped onto every row this deps object writes. Built once in
4929
- * `buildEnrichDeps` from the SAME resolved target that builds the
4930
- * completions, so it reflects reality (env overrides, Night Shift)
4931
- * output-only, never a routing input.
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.
4932
4969
  */
4933
4970
  provenance?: IndexProvenance;
4934
4971
  /**
@@ -4939,12 +4976,34 @@ interface EnrichDeps {
4939
4976
  */
4940
4977
  oneShotWidth?: () => number;
4941
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;
4942
4992
 
4943
4993
  interface AreaPassResult {
4944
4994
  areasUpdated: number;
4945
4995
  architectureUpdated: boolean;
4946
4996
  }
4947
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
+
4948
5007
  interface ContentIndexStats {
4949
5008
  scanned: number;
4950
5009
  changed: number;
@@ -4985,7 +5044,15 @@ declare class ContentIndex {
4985
5044
  /** Per-project village-file stores (they cache last-written content so
4986
5045
  * no-change builds skip touching the user's repo). */
4987
5046
  private readonly cityStores;
4988
- 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
+ });
4989
5056
  private cityStoreFor;
4990
5057
  /** Bring the project's content index up to date. Called by the indexer tick. */
4991
5058
  refresh(projectId: string): Promise<ContentIndexStats | null>;
@@ -5118,8 +5185,41 @@ declare class ContentIndex {
5118
5185
  fileReview(projectId: string, relPath: string): Promise<FileReviewResponse>;
5119
5186
  /** Flat issue query across current-hash reviews (`list_file_issues` tool). */
5120
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;
5121
5197
  searchDocs(projectId: string, query: string, maxResults?: number): Promise<SearchDocsResponse>;
5122
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;
5123
5223
  searchImages(projectId: string, query: string, maxResults?: number): Promise<SearchImagesResponse>;
5124
5224
  findSimilarImages(projectId: string, relPath: string, maxResults?: number): Promise<FindSimilarImagesResponse>;
5125
5225
  describeFolder(projectId: string, prefix?: string): Promise<DescribeFolderResponse>;
@@ -7473,6 +7573,17 @@ interface IncompleteModelDownload {
7473
7573
  * stray files left without one). */
7474
7574
  hasPartial: boolean;
7475
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
+ }
7476
7587
  /**
7477
7588
  * An incomplete download enriched with per-engine catalog context. Managers
7478
7589
  * return this from `listIncomplete()`; the route serializes it as-is.
@@ -7680,6 +7791,9 @@ declare class MlxModelManager {
7680
7791
  * In-flight installs are excluded. See {@link LlamaCppModelManager.listIncomplete}.
7681
7792
  */
7682
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[]>;
7683
7797
  resolveDefaultModel(): Promise<InstalledMlxModel | null>;
7684
7798
  resolveDefaultModelPath(): Promise<string | null>;
7685
7799
  resolveModel(id: string): Promise<InstalledMlxModel | null>;
@@ -8105,6 +8219,15 @@ declare class LlamaCppModelManager {
8105
8219
  * delete-only for stale ids. In-flight installs are excluded.
8106
8220
  */
8107
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[]>;
8108
8231
  /**
8109
8232
  * Path of the first installed model, or null when none exist. Phase 2
8110
8233
  * uses this to pick a default `--model` arg for the supervised
@@ -9384,6 +9507,12 @@ declare class ChatManager {
9384
9507
  projectId?: string;
9385
9508
  expectedDeliverable?: ExpectedDeliverable;
9386
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;
9387
9516
  /** Late-bind the fitness manager (see the field's docblock). */
9388
9517
  setModelFitness(manager: ModelFitnessManager): void;
9389
9518
  /**
@@ -9483,7 +9612,8 @@ declare class ChatManager {
9483
9612
  * (which is anchored to a task + phase and always opens a fresh session),
9484
9613
  * `messageGezel` drops a message into the target gezel's *active* project
9485
9614
  * session via `ensureOrCreateSession`, so the continuity of their thread
9486
- * 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.
9487
9617
  *
9488
9618
  * When the target's turn completes, its reply is injected back into the
9489
9619
  * sender's session as its own handoff-styled message (role=user, `from`
@@ -9503,6 +9633,10 @@ declare class ChatManager {
9503
9633
  toGezelIdOrName: string;
9504
9634
  projectId?: string;
9505
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;
9506
9640
  /**
9507
9641
  * Provider-queue lane for the target's reply turn. Defaults to
9508
9642
  * `interactive`. The scheduler passes `background` for ambient
@@ -10265,6 +10399,15 @@ declare class ChatManager {
10265
10399
  * "memory extraction".
10266
10400
  */
10267
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;
10268
10411
  /**
10269
10412
  * Truly-deferrable housekeeping (memory extraction, icon/about
10270
10413
  * generation, index enrichment, digests). On local engine queues
@@ -10726,6 +10869,14 @@ declare class ChatManager {
10726
10869
  */
10727
10870
  opts?: {
10728
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;
10729
10880
  }): Promise<{
10730
10881
  contextWindow?: number;
10731
10882
  plannedResidentBytes?: number;
@@ -10821,6 +10972,8 @@ declare class ChatManager {
10821
10972
  private immediateFileWriteConstraintActive;
10822
10973
  /** Resolve the mode that a Codex CLI session will actually receive. */
10823
10974
  private resolveCodexPermissionMode;
10975
+ /** Resolve the mode that a Claude CLI session will actually receive. */
10976
+ private resolveClaudePermissionMode;
10824
10977
  /**
10825
10978
  * D4 clamp-lifetime derivation for the rebuild check: read the
10826
10979
  * session's active step (step-scoped sessions only) and combine with
@@ -11229,7 +11382,7 @@ declare class ConnectorManager {
11229
11382
  code: string;
11230
11383
  }): Promise<ProjectConnectorBinding>;
11231
11384
  private gcPendingOAuth;
11232
- /** Remove a binding + its stored credential. */
11385
+ /** Remove a binding + its stored credential (and its origin allowlist entry). */
11233
11386
  unbind(project: ProjectDetail, bindingId: string): Promise<void>;
11234
11387
  status(project: ProjectDetail): Promise<ConnectorStatus>;
11235
11388
  /**
@@ -11243,6 +11396,13 @@ declare class ConnectorManager {
11243
11396
  }): Promise<BindingSyncResult>;
11244
11397
  /** Sync every enabled binding on a project (drives the sync manager). */
11245
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;
11246
11406
  /** The per-binding sync body — always called under the project lock. */
11247
11407
  private syncBindingInner;
11248
11408
  /**