@jagilber-org/index-server 1.28.9 → 1.28.19

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 (75) hide show
  1. package/CHANGELOG.md +109 -1
  2. package/CONTRIBUTING.md +13 -0
  3. package/README.md +10 -14
  4. package/dist/config/featureConfig.js +4 -1
  5. package/dist/dashboard/client/admin.html +69 -29
  6. package/dist/dashboard/client/js/admin.embeddings.js +97 -5
  7. package/dist/dashboard/client/js/admin.instructions.js +1 -1
  8. package/dist/dashboard/server/AdminPanel.js +38 -0
  9. package/dist/dashboard/server/ApiRoutes.js +14 -1
  10. package/dist/dashboard/server/routes/embeddings.routes.js +76 -1
  11. package/dist/dashboard/server/routes/instructions.routes.js +4 -11
  12. package/dist/dashboard/server/routes/scripts.routes.js +35 -10
  13. package/dist/dashboard/server/routes/status.routes.js +77 -0
  14. package/dist/models/instruction.d.ts +2 -1
  15. package/dist/models/instruction.js +2 -0
  16. package/dist/schemas/index-server.code-schema.json +52478 -0
  17. package/dist/schemas/index.d.ts +7 -164
  18. package/dist/schemas/index.js +45 -63
  19. package/dist/schemas/instructionSchema.d.ts +46 -0
  20. package/dist/schemas/instructionSchema.js +159 -0
  21. package/{schemas → dist/schemas}/json-schema/instruction-content-type.schema.json +6 -4
  22. package/{schemas → dist/schemas}/json-schema/instruction-instruction-entry.schema.json +6 -4
  23. package/dist/schemas/manifest.json +78 -0
  24. package/dist/server/index-server.js +7 -1
  25. package/dist/services/bootstrapGating.js +2 -2
  26. package/dist/services/handlers/instructions.add.js +18 -0
  27. package/dist/services/handlers/instructions.groom.js +6 -1
  28. package/dist/services/handlers/instructions.import.js +42 -7
  29. package/dist/services/handlers.activation.js +3 -1
  30. package/dist/services/handlers.dashboardConfig.js +2 -1
  31. package/dist/services/handlers.feedback.d.ts +4 -4
  32. package/dist/services/handlers.feedback.js +390 -27
  33. package/dist/services/handlers.instructionSchema.js +73 -31
  34. package/dist/services/handlers.search.js +11 -6
  35. package/dist/services/indexLoader.js +7 -0
  36. package/dist/services/instructionRecordValidation.js +32 -84
  37. package/dist/services/mcpConfig/flagCatalog.d.ts +1 -1
  38. package/dist/services/mcpConfig/flagCatalog.js +2 -0
  39. package/dist/services/mcpConfig/formats.js +2 -6
  40. package/dist/services/messaging/agentMailbox.d.ts +6 -1
  41. package/dist/services/messaging/agentMailbox.js +10 -3
  42. package/dist/services/seedBootstrap.contentModel.d.ts +13 -0
  43. package/dist/services/seedBootstrap.contentModel.js +166 -0
  44. package/dist/services/seedBootstrap.contentTypes.d.ts +5 -0
  45. package/dist/services/seedBootstrap.contentTypes.js +76 -0
  46. package/dist/services/seedBootstrap.d.ts +1 -0
  47. package/dist/services/seedBootstrap.js +101 -15
  48. package/dist/services/toolRegistry.js +52 -24
  49. package/dist/services/toolRegistry.zod.js +84 -37
  50. package/dist/versioning/schemaVersion.d.ts +1 -1
  51. package/dist/versioning/schemaVersion.js +1 -13
  52. package/package.json +17 -3
  53. package/schemas/index-server.code-schema.json +31019 -25047
  54. package/schemas/instruction.schema.json +16 -6
  55. package/schemas/manifest.json +3 -3
  56. package/scripts/README.md +20 -0
  57. package/scripts/build/README.md +41 -0
  58. package/scripts/build/setup-wizard-paths.mjs +27 -0
  59. package/scripts/build/setup-wizard.mjs +7 -21
  60. package/scripts/client/README.md +26 -0
  61. package/scripts/client/index-server-client.ps1 +203 -0
  62. package/scripts/client/index-server-client.sh +149 -0
  63. package/scripts/client/powershell-mcp-server.ps1 +83 -0
  64. package/scripts/client/powershell-mcp-template.ps1 +85 -0
  65. package/scripts/hooks/README.md +40 -0
  66. package/server.json +2 -2
  67. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-persisted-admin-session.schema.json +0 -0
  68. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-persisted-session-history-entry.schema.json +0 -0
  69. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-persisted-web-socket-connection.schema.json +0 -0
  70. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-session-persistence-config.schema.json +0 -0
  71. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-session-persistence-data.schema.json +0 -0
  72. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-session-persistence-manifest.schema.json +0 -0
  73. /package/{schemas → dist/schemas}/json-schema/SessionPersistence-session-persistence-metadata.schema.json +0 -0
  74. /package/{schemas → dist/schemas}/json-schema/instruction-audience-scope.schema.json +0 -0
  75. /package/{schemas → dist/schemas}/json-schema/instruction-requirement-level.schema.json +0 -0
package/CHANGELOG.md CHANGED
@@ -6,6 +6,114 @@ The format is based on Keep a Changelog and this project adheres to Semantic Ver
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.28.19] - 2026-05-11
10
+
11
+ ### Added
12
+
13
+ - ci: windows-smoke timeout bump
14
+
15
+ ## [1.28.18] - 2026-05-11
16
+
17
+ ### Added
18
+
19
+ - fix(test): npmPackReadiness allowlist for setup-wizard-paths.mjs
20
+
21
+ ## [1.28.17] - 2026-05-10
22
+
23
+ ### Added
24
+
25
+ - fix(ci): skip integrity-pre-push hook in CI to avoid 'files modified by hook' false-positive
26
+
27
+ ## [1.28.16] - 2026-05-10
28
+
29
+ ### Added
30
+
31
+ - fix(ci): correct precommit.yml YAML indentation breaking workflow file
32
+
33
+ ## [1.28.15] - 2026-05-10
34
+
35
+ ### Added
36
+
37
+ - fix: ship setup-wizard-paths.mjs in npm pack; fix governance-hash workflow checkout collision; remove unused buildZIndexEntry/RECORD_SCHEMA
38
+
39
+ ## [1.28.14] - 2026-05-10
40
+
41
+ ### Fixed
42
+
43
+ - **SQLite restore leak: orphaned JSON files after backup restore**: `AdminPanel.restoreBackup`
44
+ now deletes per-instruction `.json` files from `instructionsDir` after a zero-error
45
+ SQLite ingest, preventing stale file accumulation and `ensureLoaded()` auto-migration
46
+ latch retriggering. Loader-generated metadata (`_manifest.json`, `_skipped.json`) is
47
+ preserved. JSON backend behavior is unchanged.
48
+ - **SQLite embedding leak: `embeddings.json` written in sqlite mode**: `sqliteVecEnabled`
49
+ now auto-derives `true` when `INDEX_SERVER_STORAGE_BACKEND=sqlite` and the flag is
50
+ unset. Explicit `INDEX_SERVER_SQLITE_VEC_ENABLED=0` opts out. `ApiRoutes` wires
51
+ `SqliteEmbeddingStore` for sqlite backends with `logWarn` fallback to JSON if the
52
+ native extension fails to load.
53
+ - **Embedding compute split-brain**: the dashboard compute endpoint now threads the
54
+ derived `SqliteEmbeddingStore` through to `getInstructionEmbeddings`, so reads and
55
+ writes use the same store in sqlite mode.
56
+ - **Embeddings tab false error banner**: the "not configured" banner is now hidden when
57
+ the embedding state is `ready`, preventing a misleading error when the LLM is working.
58
+
59
+ ### Added
60
+
61
+ - **`feedback_manage` MCP dispatcher**: adds a single feedback management tool
62
+ with `submit`, `list`, `get`, `update`, `delete`, and `stats` actions while
63
+ keeping `feedback_submit` as the standalone submit alias. Legacy standalone
64
+ feedback management tool names remain removed.
65
+ - **PPID watchdog opt-out** (`INDEX_SERVER_DISABLE_PPID_WATCHDOG`): disables the
66
+ parent-process watchdog for dev sandbox launchers that spawn through a transient shell.
67
+ - **Dashboard path reveal**: clickable paths in the build metadata panel open the
68
+ instructions, SQLite, or backups directory in the OS file manager (allowlist-gated).
69
+ - **`/api/status/sqlite-reset`**: exposes sqlite backend flag, vec store status, and
70
+ embedding file presence for dashboard diagnostics.
71
+
72
+ ### Changed
73
+
74
+ - **`dev-server.ps1`**: removed 4 env vars that duplicated compile-time defaults
75
+ (`SQLITE_WAL`, `SQLITE_MIGRATE_ON_START`, `DASHBOARD_HOST`, `SEMANTIC_DEVICE`);
76
+ added `INDEX_SERVER_DISABLE_PPID_WATCHDOG=1` for transient-shell spawning.
77
+
78
+ ### Added
79
+
80
+ - **Canonical seed `002-content-model`**: a third bootstrap-tier seed derived
81
+ at module-load time from `schemas/instruction.schema.json`. Surfaces the
82
+ required-field set, the `contentType` decision matrix
83
+ (`agent|skill|instruction|prompt|workflow|knowledge|template|integration`), and a pointer to
84
+ the live `index_schema` MCP tool — so search-first agents that hit the index
85
+ before reading the schema get the same conceptual model the schema enforces.
86
+ Generated by a pure function (`buildContentModelSeed`) with `instruction.schema.json`
87
+ as the single source of truth; a drift trip-wire spec
88
+ (`src/tests/contentModelSeed.spec.ts`) asserts every required field and every
89
+ `contentType` enum member appears in the rendered body.
90
+ - **Canonical seed `003-content-types`**: a schema-derived bootstrap seed that
91
+ lists the canonical eight-value `contentType` taxonomy.
92
+
93
+ ### Changed
94
+
95
+ - **Instruction content taxonomy**: bumped instruction schema version to v6 and
96
+ adopted the canonical `agent|skill|instruction|prompt|workflow|knowledge|template|integration`
97
+ enum across schema, registry, validation, search, bootstrap seeds, generated
98
+ schemas, and docs. Removed write/load normalization for removed values; invalid
99
+ submitted or persisted values now follow existing validation rejection paths.
100
+
101
+ ## [1.28.13] - 2026-05-08
102
+
103
+ ### Fixed
104
+
105
+ - **Release workflow: transient git-clone failures in `Publish-ToMirror.ps1`**: the public-mirror clone step now retries (3 attempts, exponential backoff: 5s, 10s) on transient Windows networking errors (`getaddrinfo() thread failed to start`, `Could not resolve host`, connection resets, RPC failures, 5xx responses, etc.) before giving up. The PR-branch push retries the same way. Previously a single transient DNS hiccup would abort `Invoke-ReleaseWorkflow.ps1 -CreatePR` mid-flight and force manual recovery.
106
+
107
+ ## [1.28.12] - 2026-05-07
108
+
109
+ ### Fixed
110
+
111
+ - **Dashboard `/api/scripts/:name` 404**: client wrapper scripts (`index-server-client.ps1`, `index-server-client.sh`) are now packaged in the npm tarball and the route resolves them from the installed package directory instead of `process.cwd()`. Previously every install returned `Script file not found on disk`.
112
+
113
+ ### Added
114
+
115
+ - **Embeddings panel — Clear Cache button + smarter Compute**: new `POST /api/embeddings/reset` endpoint clears the cached embeddings (works for both JSON and SQLite stores). The Compute button now pre-checks status and: warns when the model will download (~25MB), short-circuits when embeddings are already up to date with a confirm prompt, and surfaces an honest `cacheHit` / `computed` / `reused` summary instead of always reporting "Computed N embeddings". The old `Reset` button was renamed to `Reset View` to clarify that it only resets pan/zoom.
116
+
9
117
  ## [1.28.2] - 2026-05-06
10
118
 
11
119
  ### Added
@@ -29,7 +137,7 @@ The format is based on Keep a Changelog and this project adheres to Semantic Ver
29
137
  - **Publish scripts**: restored root-level entrypoints for `New-CleanRoomCopy.ps1`, `Publish-ToMirror.ps1`, and `publish-direct-to-remote.cjs` after the scripts reorganization so documented/template paths continue to work without introducing alternate release workflow names.
30
138
  - **Public publish/security gates**: clean-room publish now keeps ambient exact env-value leak detection active, GitHub Action scanner uploads fail visibly instead of being hidden, ggshield quota exhaustion fails closed, and internal release branch/tag verification compares exact local/remote SHAs.
31
139
  - **Tool classification docs**: clarified that registry `stable` vs `mutation` is a visibility/gating contract, so `feedback_submit` remains stable/core while still persisting feedback and audit entries.
32
- - **Instruction content type migration**: bumped instruction schema version to v5 and added a compatibility path mapping legacy `contentType: "chat-session"` records and writes to `workflow`, preserving workflow/runbook semantics instead of falling back to `instruction`.
140
+ - **Instruction content type migration**: bumped instruction schema version to v5 and mapped the former conversation-oriented content type to `workflow`, preserving workflow/runbook semantics instead of falling back to `instruction`.
33
141
 
34
142
  ### Fixed
35
143
 
package/CONTRIBUTING.md CHANGED
@@ -28,6 +28,10 @@ Use feature branches. Submit PRs to `main`.
28
28
 
29
29
  Fill out the PR template completely before requesting review.
30
30
 
31
+ ### Agent PR Preflight
32
+
33
+ Agents MUST complete the mandatory pre-PR checklist in [`.instructions/local/agent-pr-preflight.md`](.instructions/local/agent-pr-preflight.md) before opening a pull request, handing a branch to a coordinator, or reporting a change as PR-ready. The checklist covers `npm test`, `npm run typecheck`, `npm run lint`, `pre-commit run --all-files`, `git diff --check`, and staged-diff review for unintended changes.
34
+
31
35
  ### Mandatory AI-Generated Code/Test Review
32
36
 
33
37
  If any code or tests in the PR were generated or materially edited by an AI agent, reviewers MUST confirm all of the following before merge:
@@ -117,6 +121,15 @@ Critical or actively-exploited vulnerabilities may be merged with **zero pre-mer
117
121
 
118
122
  If the post-merge audit reveals problems, a follow-up fix must be prioritized immediately. Abuse of this policy to bypass review for non-critical changes will be treated as a process violation.
119
123
 
124
+ ## Shared Instructions
125
+
126
+ This project maintains shared instruction files in `.instructions/shared/` that define reusable patterns for security hooks, git workflow, observability, agent attestation, and more. Review these when contributing changes to the areas they cover:
127
+
128
+ - **Security**: `repository-security-hooks.md`, `adopter-hook-patterns.md`, `public-mirror-guard.md`
129
+ - **Workflow**: `git-workflow.md`, `template-adoption-workflow.md`, `mirrored-release-workflow.md`
130
+ - **Analysis**: `language-ecosystem-patterns.md`, `codeql-configuration-patterns.md`, `iac-patterns.md`
131
+ - **Operations**: `observability-logging.md`, `agent-attestation.md`, `index-server-bootstrap.md`
132
+
120
133
  ## Code Style
121
134
 
122
135
  Respect existing formatting. Run any lint scripts if present.
package/README.md CHANGED
@@ -29,27 +29,23 @@ winget install nodejs
29
29
 
30
30
  ## Quick Start Options
31
31
 
32
- ### Option A: Global install via `npm` (recommended)
32
+ ### Option A: MCP-native via `npx` (recommended)
33
33
 
34
- Install once and run from anywhere. Choose this when you want a stable `index-server` command on your `PATH` with no per-invocation download.
34
+ Run the latest published package without cloning the repo. Choose this when you want the fastest local start and already have Node.js installed.
35
35
 
36
- ```bash
37
- npm install -g @jagilber-org/index-server
38
- ```
39
-
40
- Then launch the setup wizard to generate the right MCP client config for VS Code, Copilot CLI, or Claude Desktop:
36
+ Start with the setup wizard so it can generate the right MCP client config for VS Code, Copilot CLI, or Claude Desktop:
41
37
 
42
38
  ```bash
43
- index-server --setup
39
+ npx -y @jagilber-org/index-server@latest --setup
44
40
  ```
45
41
 
46
42
  To launch the server directly without the wizard:
47
43
 
48
44
  ```bash
49
- index-server --dashboard
45
+ npx -y @jagilber-org/index-server@latest --dashboard
50
46
  ```
51
47
 
52
- > **No-install alternative:** `npx -y @jagilber-org/index-server@latest --setup` works too (resolves from npmjs.org). Use it for a one-shot try; prefer `-g` for routine use. The GitHub Packages mirror requires authentication, so `npx` against `npm.pkg.github.com` needs a per-scope `.npmrc` plus a `GITHUB_TOKEN` with `read:packages`.
48
+ > **Prefer a stable `index-server` command on `PATH`?** Install globally instead: `npm install -g @jagilber-org/index-server`, then run `index-server --setup` / `index-server --dashboard`. The GitHub Packages mirror requires authentication, so `npx` against `npm.pkg.github.com` needs a per-scope `.npmrc` plus a `GITHUB_TOKEN` with `read:packages`.
53
49
 
54
50
  > **Upgrading or hitting "unsupported INDEX_SERVER key" / "Cannot find module" errors after install?** See [Upgrading and Uninstalling](docs/quickstart.md#upgrading-and-uninstalling) for the clean-uninstall steps that clear stale non-global installs.
55
51
 
@@ -59,7 +55,7 @@ Generate a self-signed TLS cert+key in one command:
59
55
 
60
56
  ```bash
61
57
  # Generate at ~/.index-server/certs/, then start with HTTPS automatically
62
- index-server --init-cert --start --dashboard
58
+ npx -y @jagilber-org/index-server@latest --init-cert --start --dashboard
63
59
  ```
64
60
 
65
61
  `--init-cert` alone exits after generation. `--init-cert --start` continues
@@ -73,7 +69,7 @@ reference, security notes, and troubleshooting.
73
69
 
74
70
  ### Option B: VS Code MCP configuration
75
71
 
76
- Use VS Code's built-in MCP support with `.vscode/mcp.json` or your global `mcp.json`. You can add the server entry manually or run `index-server --setup` (after `npm install -g @jagilber-org/index-server`) to generate the config for you.
72
+ Use VS Code's built-in MCP support with `.vscode/mcp.json` or your global `mcp.json`. You can add the server entry manually or run `npx -y @jagilber-org/index-server@latest --setup` to generate the config for you.
77
73
 
78
74
  ### Option C: Docker
79
75
 
@@ -183,7 +179,7 @@ See [MCP Configuration Guide](docs/mcp_configuration.md) for advanced patterns,
183
179
  ### Verify
184
180
 
185
181
  - Server appears in your MCP client's server list
186
- - Run `tools/list` to see 40+ available tools
182
+ - Run `tools/list` to see 50+ available tools
187
183
  - Run `prompts/list` to discover setup/config/verification prompts
188
184
  - Run `resources/list` to discover quickstart and configuration guides
189
185
  - Dashboard (if enabled) at `http://localhost:8787`
@@ -260,7 +256,7 @@ See [dashboard.md](docs/dashboard.md) for full details. REST client scripts (`sc
260
256
  ## Key Features
261
257
 
262
258
  - **MCP protocol compliance** — Full JSON-RPC 2.0 over stdio with schema validation
263
- - **40+ tools** for search, CRUD, governance, analytics, messaging, and feedback
259
+ - **50+ tools** for search, CRUD, governance, analytics, messaging, and feedback
264
260
  - **Semantic search** — Optional embedding-based similarity search (HuggingFace models)
265
261
  - **Bootstrap security** — Mutations gated until human confirmation on fresh installs
266
262
  - **Cross-repo knowledge promotion** — Validate locally, then promote proven patterns to the shared catalog
@@ -120,7 +120,10 @@ function parseStorageConfig() {
120
120
  sqlitePath: (0, configUtils_1.toAbsolute)(process.env.INDEX_SERVER_SQLITE_PATH, path_1.default.join(configUtils_1.CWD, dirConstants_1.DIR.DATA_SQLITE)),
121
121
  sqliteWal: (0, envUtils_1.parseBooleanEnv)(process.env.INDEX_SERVER_SQLITE_WAL, true),
122
122
  sqliteMigrateOnStart: (0, envUtils_1.parseBooleanEnv)(process.env.INDEX_SERVER_SQLITE_MIGRATE_ON_START, true),
123
- sqliteVecEnabled: (0, envUtils_1.parseBooleanEnv)(process.env.INDEX_SERVER_SQLITE_VEC_ENABLED, false),
123
+ sqliteVecEnabled: (0, envUtils_1.parseBooleanEnv)(process.env.INDEX_SERVER_SQLITE_VEC_ENABLED,
124
+ // Default to enabled when the storage backend is already sqlite;
125
+ // factory.ts falls back to JSON automatically if the native extension fails.
126
+ backend === 'sqlite'),
124
127
  sqliteVecPath: process.env.INDEX_SERVER_SQLITE_VEC_PATH || '',
125
128
  };
126
129
  }
@@ -1,30 +1,30 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
3
  <head>
4
- <meta name="dashboard-build-version" content="1.28.9-0009101e">
4
+ <meta name="dashboard-build-version" content="1.28.19-52109005">
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>Index Server Admin</title>
8
- <link rel="stylesheet" href="css/admin.css?v=1.28.9-0009101e">
9
- <script defer src="js/admin.utils.js?v=1.28.9-0009101e"></script>
10
- <script defer src="js/admin.auth.js?v=1.28.9-0009101e"></script>
11
- <script defer src="js/admin.overview.js?v=1.28.9-0009101e"></script>
12
- <script defer src="js/admin.sessions.js?v=1.28.9-0009101e"></script>
13
- <script defer src="js/admin.monitor.js?v=1.28.9-0009101e"></script>
14
- <script defer src="js/admin.events.js?v=1.28.9-0009101e"></script>
15
- <script defer src="js/admin.graph.js?v=1.28.9-0009101e"></script>
8
+ <link rel="stylesheet" href="css/admin.css?v=1.28.19-52109005">
9
+ <script defer src="js/admin.utils.js?v=1.28.19-52109005"></script>
10
+ <script defer src="js/admin.auth.js?v=1.28.19-52109005"></script>
11
+ <script defer src="js/admin.overview.js?v=1.28.19-52109005"></script>
12
+ <script defer src="js/admin.sessions.js?v=1.28.19-52109005"></script>
13
+ <script defer src="js/admin.monitor.js?v=1.28.19-52109005"></script>
14
+ <script defer src="js/admin.events.js?v=1.28.19-52109005"></script>
15
+ <script defer src="js/admin.graph.js?v=1.28.19-52109005"></script>
16
16
  <script defer src="js/marked.umd.js"></script>
17
- <script defer src="js/admin.instructions.js?v=1.28.9-0009101e"></script>
18
- <script defer src="js/admin.logs.js?v=1.28.9-0009101e"></script>
19
- <script defer src="js/admin.maintenance.js?v=1.28.9-0009101e"></script>
20
- <script defer src="js/admin.config.js?v=1.28.9-0009101e"></script>
21
- <script defer src="js/admin.performance.js?v=1.28.9-0009101e"></script>
22
- <script defer src="js/admin.instances.js?v=1.28.9-0009101e"></script>
23
- <script defer src="js/admin.embeddings.js?v=1.28.9-0009101e"></script>
24
- <script defer src="js/admin.messaging.js?v=1.28.9-0009101e"></script>
25
- <script defer src="js/admin.sqlite.js?v=1.28.9-0009101e"></script>
26
- <script defer src="js/admin.boot.js?v=1.28.9-0009101e"></script>
27
- <script defer src="js/admin.feedback.js?v=1.28.9-0009101e"></script>
17
+ <script defer src="js/admin.instructions.js?v=1.28.19-52109005"></script>
18
+ <script defer src="js/admin.logs.js?v=1.28.19-52109005"></script>
19
+ <script defer src="js/admin.maintenance.js?v=1.28.19-52109005"></script>
20
+ <script defer src="js/admin.config.js?v=1.28.19-52109005"></script>
21
+ <script defer src="js/admin.performance.js?v=1.28.19-52109005"></script>
22
+ <script defer src="js/admin.instances.js?v=1.28.19-52109005"></script>
23
+ <script defer src="js/admin.embeddings.js?v=1.28.19-52109005"></script>
24
+ <script defer src="js/admin.messaging.js?v=1.28.19-52109005"></script>
25
+ <script defer src="js/admin.sqlite.js?v=1.28.19-52109005"></script>
26
+ <script defer src="js/admin.boot.js?v=1.28.19-52109005"></script>
27
+ <script defer src="js/admin.feedback.js?v=1.28.19-52109005"></script>
28
28
  </head>
29
29
  <body>
30
30
  <div class="admin-container admin-root">
@@ -443,7 +443,8 @@
443
443
  <div class="emb-controls">
444
444
  <button class="emb-btn" data-emb-action="compute">Compute</button>
445
445
  <button class="emb-btn" data-emb-action="load">Load</button>
446
- <button class="emb-btn" data-emb-action="reset">Reset</button>
446
+ <button class="emb-btn" data-emb-action="reset" title="Reset pan/zoom view">Reset View</button>
447
+ <button class="emb-btn" data-emb-action="clear" title="Clear cached embeddings (forces recompute on next Compute)">Clear Cache</button>
447
448
  <button class="emb-btn" id="emb-norm-btn" data-emb-action="norm">Norm</button>
448
449
  </div>
449
450
  <span id="emb-status" class="emb-status-text"></span>
@@ -909,10 +910,10 @@
909
910
  }
910
911
  }
911
912
 
912
- // Graph logic was extracted to js/admin.graph.js?v=1.28.9-0009101e
913
+ // Graph logic was extracted to js/admin.graph.js?v=1.28.19-52109005
913
914
  // Functions available globally: reloadGraphMermaid, initGraphScopeDefaults, copyMermaidSource, toggleGraphEdit, applyGraphEdit, cancelGraphEdit, refreshDrillCategories, loadDrillInstructions, clearSelections
914
915
 
915
- <!-- overview functions moved to js/admin.overview.js?v=1.28.9-0009101e -->
916
+ <!-- overview functions moved to js/admin.overview.js?v=1.28.19-52109005 -->
916
917
 
917
918
  // Lightweight overview-level maintenance display (optional)
918
919
  // Intentionally minimal to avoid blocking overview rendering.
@@ -1097,7 +1098,7 @@
1097
1098
  }
1098
1099
 
1099
1100
  // --- Backup / Restore ---
1100
- // Extracted to js/admin.maintenance.js?v=1.28.9-0009101e
1101
+ // Extracted to js/admin.maintenance.js?v=1.28.19-52109005
1101
1102
 
1102
1103
  async function performBackup() {
1103
1104
  try {
@@ -1163,7 +1164,7 @@
1163
1164
  }
1164
1165
 
1165
1166
  async function loadConfiguration() {
1166
- // Primary implementation in js/admin.config.js?v=1.28.9-0009101e (loaded via defer).
1167
+ // Primary implementation in js/admin.config.js?v=1.28.19-52109005 (loaded via defer).
1167
1168
  // This inline fallback only fires if the external script failed to load.
1168
1169
  if (window.__configExternalLoaded) return;
1169
1170
  try {
@@ -1223,10 +1224,10 @@
1223
1224
  return false;
1224
1225
  }
1225
1226
 
1226
- // Monitoring functions moved to js/admin.monitor.js?v=1.28.9-0009101e
1227
+ // Monitoring functions moved to js/admin.monitor.js?v=1.28.19-52109005
1227
1228
 
1228
1229
  // ===== Log Viewer =====
1229
- // Extracted to js/admin.logs.js?v=1.28.9-0009101e
1230
+ // Extracted to js/admin.logs.js?v=1.28.19-52109005
1230
1231
 
1231
1232
  // ===== Instruction Management =====
1232
1233
  let instructionEditing = null;
@@ -1651,7 +1652,7 @@
1651
1652
  categories: ['general'],
1652
1653
  primaryCategory: 'general',
1653
1654
  reviewIntervalDays: 180,
1654
- schemaVersion: '5',
1655
+ schemaVersion: '6',
1655
1656
  description: 'Describe purpose and scope.',
1656
1657
  createdAt: now,
1657
1658
  updatedAt: now
@@ -1723,7 +1724,7 @@
1723
1724
  setInterval(fetchResourceTrends, 10000);
1724
1725
  })();
1725
1726
 
1726
- // Instruction management logic extracted to js/admin.instructions.js?v=1.28.9-0009101e
1727
+ // Instruction management logic extracted to js/admin.instructions.js?v=1.28.19-52109005
1727
1728
  // Functions exposed globally: loadInstructions, renderInstructionList, editInstruction, saveInstruction, deleteInstruction, etc.
1728
1729
 
1729
1730
  function startAutoRefresh() {
@@ -1761,6 +1762,45 @@
1761
1762
  el.appendChild(buildBadge);
1762
1763
  }
1763
1764
  el.append(` • Built ${bt}`);
1765
+ if(j.paths){
1766
+ const p = j.paths;
1767
+ const addPath = (label, value, key) => {
1768
+ if(!value) return;
1769
+ const wrap = document.createElement('span');
1770
+ wrap.className = 'build-path';
1771
+ wrap.style.marginLeft = '0.75em';
1772
+ wrap.append(`${label}: `);
1773
+ const code = document.createElement('code');
1774
+ code.textContent = String(value);
1775
+ code.title = `Click to open ${value}`;
1776
+ code.style.cursor = 'pointer';
1777
+ code.style.textDecoration = 'underline dotted';
1778
+ code.addEventListener('click', async (ev) => {
1779
+ ev.preventDefault();
1780
+ code.style.opacity = '0.5';
1781
+ try {
1782
+ const r = await fetch('/api/system/reveal-path', {
1783
+ method: 'POST',
1784
+ headers: { 'Content-Type': 'application/json' },
1785
+ body: JSON.stringify({ key })
1786
+ });
1787
+ if (!r.ok) {
1788
+ const j2 = await r.json().catch(() => ({}));
1789
+ showError(`Open folder failed: ${j2.error || r.statusText}`);
1790
+ }
1791
+ } catch (e) {
1792
+ showError(`Open folder failed: ${e && e.message ? e.message : e}`);
1793
+ } finally {
1794
+ code.style.opacity = '1';
1795
+ }
1796
+ });
1797
+ wrap.appendChild(code);
1798
+ el.appendChild(wrap);
1799
+ };
1800
+ addPath('Instructions', p.instructionsDir, 'instructions');
1801
+ if(p.storageBackend === 'sqlite') addPath('SQLite', p.sqlitePath, 'sqlite');
1802
+ addPath('Backups', p.backupsDir, 'backups');
1803
+ }
1764
1804
  }
1765
1805
  } catch { const el = document.getElementById('buildMeta'); if(el) el.textContent='Build metadata unavailable'; }
1766
1806
  })();
@@ -400,12 +400,19 @@
400
400
  return;
401
401
  }
402
402
  var state = status.state || 'unknown';
403
+ // Suppress the informational banner once the model is cached and embeddings
404
+ // are ready — at that point there is nothing actionable to communicate, and
405
+ // the meta line (model/device/embeddings count) is already visible in the
406
+ // STATISTICS panel below.
407
+ if (state === 'ready') {
408
+ banner.classList.add('hidden');
409
+ return;
410
+ }
403
411
  var titleText = '', icon = '';
404
412
  if (state === 'disabled') { icon = '🚫'; titleText = 'Semantic embeddings are disabled'; }
405
413
  else if (state === 'missing') { icon = '⛔'; titleText = 'Model not available — compute will fail'; }
406
414
  else if (state === 'will-download') { icon = '⬇️'; titleText = 'Model will download on first compute'; }
407
415
  else if (state === 'no-embeddings') { icon = '⚠️'; titleText = 'No embeddings computed yet'; }
408
- else if (state === 'ready') { icon = '✅'; titleText = 'Embeddings ready'; }
409
416
  else { icon = 'ℹ️'; titleText = 'Embeddings status'; }
410
417
  banner.classList.add('state-' + state);
411
418
  var parts = [];
@@ -436,10 +443,76 @@
436
443
  }
437
444
  };
438
445
 
446
+ window.clearEmbeddingsCache = async function clearEmbeddingsCache() {
447
+ var statusEl = document.getElementById('emb-status');
448
+ var ok = window.confirm(
449
+ 'Clear cached embeddings?\n\nNext Compute will rebuild them and download the embedding model if not already cached.'
450
+ );
451
+ if (!ok) return;
452
+ try {
453
+ if (statusEl) statusEl.textContent = 'Clearing embeddings cache…';
454
+ var res = await adminAuth.adminFetch('/api/embeddings/reset', {
455
+ method: 'POST',
456
+ headers: { 'Content-Type': 'application/json' },
457
+ body: JSON.stringify({ clearModel: false }),
458
+ });
459
+ var data = await res.json().catch(function () { return {}; });
460
+ if (!res.ok) {
461
+ if (statusEl) statusEl.textContent = 'Clear failed: ' + (data.error || res.statusText);
462
+ return;
463
+ }
464
+ if (statusEl) statusEl.textContent = 'Cleared (embeddingsCleared=' + !!data.embeddingsCleared + '). Click Compute to rebuild.';
465
+ window.loadEmbeddingsStatus();
466
+ } catch (e) {
467
+ if (statusEl) statusEl.textContent = 'Clear failed: ' + e.message;
468
+ }
469
+ };
470
+
439
471
  window.computeEmbeddings = async function computeEmbeddings() {
440
472
  if (!canvas) init();
441
473
  var statusEl = document.getElementById('emb-status');
442
- if (statusEl) statusEl.textContent = 'Computing embeddings (model download on first run)…';
474
+
475
+ // Pre-flight: fetch status so we can give the user actionable info.
476
+ var pre = null;
477
+ try {
478
+ var sres = await adminAuth.adminFetch('/api/embeddings/status');
479
+ if (sres.ok) pre = await sres.json();
480
+ } catch (_e) { void _e; }
481
+
482
+ if (pre && pre.success) {
483
+ if (pre.state === 'disabled') {
484
+ if (statusEl) statusEl.textContent = 'Embeddings are disabled. Set INDEX_SERVER_SEMANTIC_ENABLED=1 and restart.';
485
+ return;
486
+ }
487
+ if (pre.state === 'missing') {
488
+ if (statusEl) statusEl.textContent = 'Model is not cached and remote downloads are disabled (localOnly=true). Set INDEX_SERVER_SEMANTIC_LOCAL_ONLY=0 and restart, or pre-stage the model.';
489
+ return;
490
+ }
491
+ if (pre.state === 'ready') {
492
+ var ok = window.confirm(
493
+ 'Embeddings are already up to date (' + (pre.embeddingsCount || 0) + ' cached, model=' + (pre.model || '?') + ').\n\nCompute will be a no-op unless the index has changed.\n\nProceed anyway?'
494
+ );
495
+ if (!ok) {
496
+ if (statusEl) statusEl.textContent = 'Cancelled. Use "Clear Cache" to force a full rebuild.';
497
+ return;
498
+ }
499
+ }
500
+ if (pre.state === 'will-download') {
501
+ var ok2 = window.confirm(
502
+ 'Model "' + (pre.model || '?') + '" is not cached. Compute will download ~25MB to:\n\n' + (pre.cacheDir || '?') + '\n\nProceed?'
503
+ );
504
+ if (!ok2) {
505
+ if (statusEl) statusEl.textContent = 'Cancelled.';
506
+ return;
507
+ }
508
+ if (statusEl) statusEl.textContent = 'Downloading model + computing embeddings (this may take 30-60s)…';
509
+ } else if (statusEl) {
510
+ statusEl.textContent = 'Computing embeddings…';
511
+ }
512
+ } else if (statusEl) {
513
+ statusEl.textContent = 'Computing embeddings (model download on first run)…';
514
+ }
515
+
443
516
  try {
444
517
  var res = await adminAuth.adminFetch('/api/embeddings/compute', { method: 'POST', headers: { 'Content-Type': 'application/json' } });
445
518
  if (!res.ok) {
@@ -448,13 +521,31 @@
448
521
  if (err.hint) detail += ' — ' + err.hint;
449
522
  else if (err.message) detail += ' — ' + err.message;
450
523
  if (statusEl) statusEl.textContent = 'Error: ' + detail;
451
- // Refresh banner so user sees the actionable state machine.
452
524
  window.loadEmbeddingsStatus();
453
525
  return;
454
526
  }
455
527
  var result = await res.json();
456
- if (statusEl) statusEl.textContent = 'Computed ' + result.count + ' embeddings (' + result.model + ', ' + result.elapsedMs + 'ms). Loading visualization…';
457
- // Auto-load the projection + refresh banner
528
+ if (statusEl) {
529
+ if (result.cacheHit) {
530
+ statusEl.textContent = 'Cache hit: ' + result.count + ' embeddings already current (no work done, ' + result.elapsedMs + 'ms). Use "Clear Cache" to force a rebuild.';
531
+ } else {
532
+ var summary = 'Computed ' + (result.computed != null ? result.computed : result.count) + ' new'
533
+ + (result.reused != null && result.reused > 0 ? ' (' + result.reused + ' reused)' : '')
534
+ + ' · model=' + result.model + ' · ' + result.elapsedMs + 'ms. Loading visualization…';
535
+ statusEl.textContent = summary;
536
+ }
537
+ }
538
+ // Follow-up: if the model was just downloaded (pre-state was will-download)
539
+ // and downloads are still allowed, suggest enabling local-only mode to
540
+ // prevent further network access. The flag requires a restart to take
541
+ // effect, so we just surface the recommendation.
542
+ if (pre && pre.state === 'will-download' && pre.localOnly === false && !result.cacheHit) {
543
+ setTimeout(function () {
544
+ window.alert(
545
+ 'Model is now cached locally.\n\nRecommended: set INDEX_SERVER_SEMANTIC_LOCAL_ONLY=1 and restart the server to disable further remote model downloads (offline mode).'
546
+ );
547
+ }, 50);
548
+ }
458
549
  window.loadEmbeddingsStatus();
459
550
  await window.loadEmbeddings();
460
551
  } catch (e) {
@@ -472,6 +563,7 @@
472
563
  if (action === 'compute') window.computeEmbeddings();
473
564
  else if (action === 'load') window.loadEmbeddings();
474
565
  else if (action === 'reset') window.resetEmbeddingsView();
566
+ else if (action === 'clear') window.clearEmbeddingsCache();
475
567
  else if (action === 'norm') window.toggleNormColor();
476
568
  });
477
569
  }
@@ -448,7 +448,7 @@
448
448
 
449
449
  function formatInstructionJson(){ const ta = document.getElementById('instruction-content'); if(!ta) return; try{ const parsed = JSON.parse(ta.value); ta.value = JSON.stringify(parsed, null, 2); updateInstructionEditorDiagnostics(); } catch { globals.showError && globals.showError('Cannot format: invalid JSON'); } }
450
450
 
451
- function applyInstructionTemplate(){ const ta = document.getElementById('instruction-content'); if(!ta) return; if(ta.value.trim() && !confirm('Replace current content with template?')) return; const now = new Date().toISOString(); const template = { id:'sample-instruction', title:'Sample Instruction', body:'Detailed instruction content here.\nAdd multi-line guidance and steps.', contentType:'instruction', priority:50, audience:'all', requirement:'optional', categories:['general'], primaryCategory:'general', schemaVersion:'5', status:'draft', owner:'you@example.com', version:'1.0.0', reviewIntervalDays:180, semanticSummary:'Brief summary of what this instruction covers.', createdAt: now, updatedAt: now }; ta.value = JSON.stringify(template, null, 2); updateInstructionEditorDiagnostics(); }
451
+ function applyInstructionTemplate(){ const ta = document.getElementById('instruction-content'); if(!ta) return; if(ta.value.trim() && !confirm('Replace current content with template?')) return; const now = new Date().toISOString(); const template = { id:'sample-instruction', title:'Sample Instruction', body:'Detailed instruction content here.\nAdd multi-line guidance and steps.', contentType:'instruction', priority:50, audience:'all', requirement:'optional', categories:['general'], primaryCategory:'general', schemaVersion:'6', status:'draft', owner:'you@example.com', version:'1.0.0', reviewIntervalDays:180, semanticSummary:'Brief summary of what this instruction covers.', createdAt: now, updatedAt: now }; ta.value = JSON.stringify(template, null, 2); updateInstructionEditorDiagnostics(); }
452
452
 
453
453
  async function deleteInstruction(name) {
454
454
  if (!confirm('Delete instruction ' + name + '?')) return;
@@ -308,6 +308,44 @@ class AdminPanel {
308
308
  ?? path_1.default.join(process.cwd(), 'data', 'index.db');
309
309
  const mr = (0, migrationEngine_1.migrateJsonToSqlite)(instructionsDir, dbPath);
310
310
  sqliteIngest = { migrated: mr.migrated, errors: mr.errors.length };
311
+ // RCA 2026-05-08: in sqlite mode the .db is the source of truth, so
312
+ // the per-instruction JSON files extracted from the zip are dead
313
+ // weight after a clean ingest. Leaving them on disk caused the
314
+ // ensureLoaded() auto-migration latch to detect jsonFiles >
315
+ // result.entries forever and bloated `instructions/` to 700+ files.
316
+ // Only clean up when ingest had zero errors — preserve files for
317
+ // operator triage when something failed.
318
+ if (mr.errors.length === 0) {
319
+ let cleaned = 0;
320
+ try {
321
+ for (const f of fs_1.default.readdirSync(instructionsDir)) {
322
+ // Keep loader-generated metadata (_manifest.json, _skipped.json)
323
+ // and anything not a .json file.
324
+ if (!f.toLowerCase().endsWith('.json'))
325
+ continue;
326
+ if (f.startsWith('_'))
327
+ continue;
328
+ try {
329
+ fs_1.default.unlinkSync(path_1.default.join(instructionsDir, f));
330
+ cleaned++;
331
+ }
332
+ catch (unlinkErr) {
333
+ (0, logger_1.logWarn)('[admin] post-restore sqlite cleanup unlink failed', {
334
+ backupId: safeId, file: f,
335
+ error: unlinkErr instanceof Error ? unlinkErr.message : String(unlinkErr),
336
+ });
337
+ }
338
+ }
339
+ sqliteIngest.cleanedJson = cleaned;
340
+ (0, logger_1.logInfo)('[admin] post-restore sqlite cleanup', { backupId: safeId, cleanedJson: cleaned });
341
+ }
342
+ catch (cleanupErr) {
343
+ (0, logger_1.logWarn)('[admin] post-restore sqlite cleanup scan failed', {
344
+ backupId: safeId,
345
+ error: cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr),
346
+ });
347
+ }
348
+ }
311
349
  (0, logger_1.logInfo)('[admin] post-restore sqlite re-ingest', { backupId: safeId, ...sqliteIngest });
312
350
  }
313
351
  catch (err) {
@@ -49,6 +49,7 @@ const runtimeConfig_js_1 = require("../../config/runtimeConfig.js");
49
49
  const index_js_1 = require("./routes/index.js");
50
50
  const ensureLoadedMiddleware_js_1 = require("./middleware/ensureLoadedMiddleware.js");
51
51
  const logger_js_1 = require("../../services/logger.js");
52
+ const factory_js_1 = require("../../services/storage/factory.js");
52
53
  /**
53
54
  * Path prefixes whose endpoints are *unconditionally* exempt from the dashboard
54
55
  * HTTP rate limiter. These are bulk-shaped, operator-driven operations
@@ -200,7 +201,19 @@ function createApiRoutes(options = {}) {
200
201
  router.use((0, index_js_1.createSyntheticRoutes)(metricsCollector));
201
202
  router.use((0, index_js_1.createInstancesRoutes)());
202
203
  router.use((0, index_js_1.createToolsRoutes)());
203
- router.use((0, index_js_1.createEmbeddingsRoutes)());
204
+ let embeddingStore;
205
+ {
206
+ const storageConfig = (0, runtimeConfig_js_1.getRuntimeConfig)().storage;
207
+ if ((storageConfig?.backend ?? 'json') === 'sqlite' && storageConfig?.sqliteVecEnabled !== false) {
208
+ try {
209
+ embeddingStore = (0, factory_js_1.createEmbeddingStore)('sqlite');
210
+ }
211
+ catch (err) {
212
+ (0, logger_js_1.logWarn)(`[api] sqlite-vec embedding store unavailable, using JSON fallback: ${err instanceof Error ? err.message : String(err)}`);
213
+ }
214
+ }
215
+ }
216
+ router.use((0, index_js_1.createEmbeddingsRoutes)(undefined, embeddingStore));
204
217
  router.use((0, index_js_1.createUsageRoutes)());
205
218
  router.use((0, index_js_1.createScriptsRoutes)());
206
219
  router.use((0, index_js_1.createMessagingRoutes)());