prism-mcp-server 20.2.0 → 20.2.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.
- package/README.md +137 -15
- package/dist/cli.js +222 -34
- package/dist/config.js +14 -7
- package/dist/connect.js +854 -16
- package/dist/dashboard/server.js +9 -14
- package/dist/dashboard/settingsPolicy.js +41 -0
- package/dist/localFirstPolicy.js +20 -0
- package/dist/onboarding/wizard.js +3 -6
- package/dist/server.js +77 -65
- package/dist/session/sessionContext.js +5 -3
- package/dist/skillManifestSync.js +943 -0
- package/dist/storage/configStorage.js +110 -1
- package/dist/storage/index.js +15 -4
- package/dist/storage/inferMetricsLedger.js +89 -16
- package/dist/storage/panelMetricsSpool.js +324 -0
- package/dist/storage/sqlite.js +1 -1
- package/dist/storage/synalux.js +18 -1
- package/dist/tools/__tests__/ledgerHandlers.test.js +13 -0
- package/dist/tools/index.js +2 -2
- package/dist/tools/ledgerHandlers.js +468 -53
- package/dist/tools/prismInferHandler.js +171 -12
- package/dist/tools/sessionMemoryDefinitions.js +51 -10
- package/dist/tools/skillRouting.js +39 -7
- package/dist/tools/taskRouterHandler.js +184 -29
- package/dist/utils/entitlements.js +6 -2
- package/dist/utils/inferenceMetrics.js +22 -3
- package/dist/utils/modelPicker.js +3 -3
- package/dist/utils/synaluxJwt.js +8 -3
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -18,6 +18,87 @@ A paid subscription adds cloud sync, higher model tiers, and team features throu
|
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
+
## What's New in v20.2.2
|
|
22
|
+
|
|
23
|
+
### One Local-First Workflow Across Every Agent
|
|
24
|
+
`prism connect` now installs one orchestration contract for Claude Code,
|
|
25
|
+
Claude Desktop, Cursor, Gemini CLI, and Codex. Bounded delegated work goes to
|
|
26
|
+
`session_task_route` and the local `prism_infer` worker first; routine work must
|
|
27
|
+
not create background host agents. Local workers can receive the active
|
|
28
|
+
project's dashboard-configured quick, standard, or deep memory and select a
|
|
29
|
+
RAM-safe 2B/4B/9B/27B model at call time. The router forwards complexity but
|
|
30
|
+
does not choose the model; `prism_infer` owns the final decision using memory
|
|
31
|
+
and context fit, installed models, live RAM, entitlements, and explicit caller
|
|
32
|
+
overrides.
|
|
33
|
+
|
|
34
|
+
Codex and Gemini native agent fan-out are disabled during connect. Codex keeps
|
|
35
|
+
a two-thread, one-level Terra/low fallback profile if the developer explicitly
|
|
36
|
+
re-enables native agents later. Claude Code keeps native agents as a last-resort
|
|
37
|
+
path but pins their model to Sonnet. Cursor and Claude Desktop do not expose a
|
|
38
|
+
supported global subagent-policy file, so they receive the identical workflow
|
|
39
|
+
through Prism's MCP server instructions. `prism_infer` safety boundaries and
|
|
40
|
+
the host's final verification responsibility are unchanged.
|
|
41
|
+
|
|
42
|
+
### Subscription-Tier Skills Arrive Before the First Host Launch
|
|
43
|
+
`prism connect` now downloads the authoritative Synalux skill manifest and
|
|
44
|
+
materializes entitled packages in the native `~/.agents/skills` directory
|
|
45
|
+
before the command exits. Codex therefore sees the current skillset on its
|
|
46
|
+
first launch instead of requiring a second restart. Prism rechecks the same
|
|
47
|
+
snapshot at MCP startup, session load, and every five minutes—without host
|
|
48
|
+
lifecycle hooks.
|
|
49
|
+
|
|
50
|
+
On the first user turn, Prism's native skill, MCP metadata, and managed host
|
|
51
|
+
instructions request one `session_bootstrap({})` call. Prism then uses the
|
|
52
|
+
dashboard's developer name, Auto-Load Projects, and quick, standard, or deep
|
|
53
|
+
setting. The response stays focused on greeting and session state because tier
|
|
54
|
+
skills are already present in the host's native skill directory.
|
|
55
|
+
|
|
56
|
+
Hook-free MCP can provide and prioritize that ready-to-display block, but the
|
|
57
|
+
host model still owns the final assistant message and may summarize it. Prism
|
|
58
|
+
does not claim a deterministic verbatim greeting on third-party chat surfaces;
|
|
59
|
+
that would require a host lifecycle hook, launcher, extension, or Prism-owned
|
|
60
|
+
panel. Context loading itself remains complete even when a host shortens the
|
|
61
|
+
visible reply.
|
|
62
|
+
|
|
63
|
+
Free accounts receive the protected 12-skill foundation. Paid accounts receive
|
|
64
|
+
the current subscribed routing set. Upgrades install newly entitled packages;
|
|
65
|
+
downgrades remove only Prism-owned packages while preserving local skills and
|
|
66
|
+
locally modified conflicts.
|
|
67
|
+
|
|
68
|
+
When upgrading an older Claude Code installation, `prism connect` removes only
|
|
69
|
+
the exact Prism-owned startup, skill-sync, handoff, and drift hook actions from
|
|
70
|
+
the legacy bootstrap. It also removes the recognized legacy Prism startup
|
|
71
|
+
sections from `~/CLAUDE.md`, preserves every other instruction, and installs a
|
|
72
|
+
small ownership-marked native block that selects `session_bootstrap({})` on the
|
|
73
|
+
first turn. User hooks, custom instruction sections, and near matches remain
|
|
74
|
+
untouched; native skills and server-side reminders preserve those Prism
|
|
75
|
+
features without host lifecycle hooks. Because hosts expose no native
|
|
76
|
+
session-end callback, handoff at shutdown is instruction-driven rather than a
|
|
77
|
+
guaranteed lifecycle event.
|
|
78
|
+
|
|
79
|
+
After Claude Code's native user registration succeeds, the same default or
|
|
80
|
+
`--refresh` command checks the nearest `.mcp.json` from the current directory
|
|
81
|
+
through the home directory. It removes only the exact legacy
|
|
82
|
+
`prism-mcp` entry `{ "command": "npx", "args": ["-y", "prism-mcp-server"] }`
|
|
83
|
+
that would otherwise shadow the user registration. Custom Prism entries and
|
|
84
|
+
their additional fields, plus unrelated servers, are preserved; malformed
|
|
85
|
+
files fail loud without changes. `--dry-run` reports the recognized migration
|
|
86
|
+
without changing the file.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## What's New in v20.2.1
|
|
91
|
+
|
|
92
|
+
### Subscription-Aware Memory Storage
|
|
93
|
+
`prism connect` now carries an explicit `PRISM_STORAGE=auto|local|synalux|supabase`
|
|
94
|
+
into every managed host registration and rejects invalid values before changing a
|
|
95
|
+
config file. In `auto`, a portal-confirmed free tier uses local SQLite, while
|
|
96
|
+
Standard, Advanced, and Enterprise use Synalux cloud memory. If entitlement
|
|
97
|
+
resolution is unavailable, Prism fails closed instead of splitting history across
|
|
98
|
+
backends. Storage remains independent of local-first model routing.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
21
102
|
## What's New in v20.2.0
|
|
22
103
|
|
|
23
104
|
### One Command Connects Every Supported Host
|
|
@@ -121,11 +202,37 @@ Use `prism connect --all` to target all five, `--host <name>` for one host, or
|
|
|
121
202
|
`--dry-run` to preview the files that would change. Existing `prism` and
|
|
122
203
|
`prism-mcp` entries are never overwritten by default. `--refresh` updates only
|
|
123
204
|
an entry previously created by Prism; custom entries remain untouched.
|
|
205
|
+
For Claude Code, both the default command and `--refresh` also remove the exact
|
|
206
|
+
legacy project-scoped `npx -y prism-mcp-server` entry from the effective
|
|
207
|
+
ancestor `.mcp.json` after the native user registration succeeds. No custom or
|
|
208
|
+
near-match project entry is changed.
|
|
124
209
|
Close the target MCP hosts before a non-dry-run registration so they cannot
|
|
125
210
|
edit their configuration at the same time.
|
|
126
211
|
|
|
127
|
-
|
|
128
|
-
|
|
212
|
+
The same connection installs the local-first orchestration contract:
|
|
213
|
+
|
|
214
|
+
| Host | Managed containment |
|
|
215
|
+
|---|---|
|
|
216
|
+
| Codex | `features.multi_agent=false`; a 2-thread, depth-1 Terra/low fallback profile is retained for explicit re-enable |
|
|
217
|
+
| Gemini CLI | `experimental.enableAgents=false` |
|
|
218
|
+
| Claude Code | `CLAUDE_CODE_SUBAGENT_MODEL=sonnet`; managed instructions reserve it for last-resort fallback |
|
|
219
|
+
| Cursor | Canonical policy delivered through MCP initialize instructions |
|
|
220
|
+
| Claude Desktop | Canonical policy delivered through MCP initialize instructions |
|
|
221
|
+
|
|
222
|
+
All five receive `PRISM_AGENT_POLICY=local-first` in their managed Prism MCP
|
|
223
|
+
entry. Routine tasks use the RAM-aware local worker; native/background fan-out
|
|
224
|
+
is not the default workflow. `session_task_route` supplies a complexity hint;
|
|
225
|
+
`prism_infer` remains the single owner of model and thinking selection and can
|
|
226
|
+
choose 27B when its viability gates support it.
|
|
227
|
+
|
|
228
|
+
Set `PRISM_STORAGE` before running `prism connect` to preserve an explicit
|
|
229
|
+
storage choice in the generated host entries. This does not change local-model
|
|
230
|
+
routing; Synalux cloud storage separately requires an active cloud-memory
|
|
231
|
+
entitlement.
|
|
232
|
+
|
|
233
|
+
Codex registration preserves unrelated `~/.codex/config.toml` content, appends
|
|
234
|
+
only the marked Prism MCP block, and updates only the documented local-first
|
|
235
|
+
feature/agent keys. `CODEX_HOME` is respected when set and must already exist,
|
|
129
236
|
matching Codex's own contract. Restart Codex CLI, the
|
|
130
237
|
IDE extension, or the ChatGPT desktop app after connecting.
|
|
131
238
|
|
|
@@ -482,7 +589,8 @@ Prism exposes 40+ MCP tools. The core memory loop:
|
|
|
482
589
|
|
|
483
590
|
| Tool | What it does |
|
|
484
591
|
|---|---|
|
|
485
|
-
| `
|
|
592
|
+
| `session_bootstrap` | Hook-free first-turn greeting and dashboard-configured context |
|
|
593
|
+
| `session_load_context` | Explicit project reload or older-server startup fallback |
|
|
486
594
|
| `session_save_ledger` | Append an immutable session log entry |
|
|
487
595
|
| `session_save_handoff` | Save live state for the next session |
|
|
488
596
|
| `knowledge_search` | Semantic + keyword search over all memories |
|
|
@@ -491,7 +599,7 @@ Prism exposes 40+ MCP tools. The core memory loop:
|
|
|
491
599
|
| `verify_behavior` | Pre-edit scenario challenge — catch bad changes before they happen |
|
|
492
600
|
| `knowledge_ingest` | Teach Prism a codebase or document |
|
|
493
601
|
| `prism_infer` | Local-first inference (route/chat/code modes, thinking, cloud escalation) |
|
|
494
|
-
| `inference_metrics` | Session delegation
|
|
602
|
+
| `inference_metrics` | Session delegation or persisted MCP + VS Code panel local/cloud stats |
|
|
495
603
|
|
|
496
604
|
### `prism_infer` — local-first inference with cloud escalation
|
|
497
605
|
|
|
@@ -516,7 +624,7 @@ Full TypeScript signatures live in [`src/tools/`](src/tools/); architecture in [
|
|
|
516
624
|
|
|
517
625
|
### `inference_metrics` — see your local-model usage on demand
|
|
518
626
|
|
|
519
|
-
Call `inference_metrics` anytime mid-session to see how many `prism_infer` calls ran locally vs cloud
|
|
627
|
+
Call `inference_metrics` anytime mid-session to see how many `prism_infer` calls ran locally vs cloud. Use `period: "all"` to atomically import the Synalux VS Code panel spool and include its local-serve rate in the persisted totals:
|
|
520
628
|
|
|
521
629
|
```
|
|
522
630
|
📊 Inference Metrics — local-model delegation (this session):
|
|
@@ -530,28 +638,42 @@ Call `inference_metrics` anytime mid-session to see how many `prism_infer` calls
|
|
|
530
638
|
|
|
531
639
|
The same block also appears automatically in `session_save_ledger` and `session_save_handoff` responses at session end.
|
|
532
640
|
|
|
533
|
-
**Note:**
|
|
641
|
+
**Note:** The default session view tracks this MCP process's `prism_infer` delegation. The all-time view combines persisted MCP calls with Synalux VS Code panel inference. Neither view includes your host model's (Claude's) own token spend; use Claude Code's `/cost` command for that.
|
|
534
642
|
|
|
535
|
-
### Local-model delegation (
|
|
643
|
+
### Local-model delegation (default)
|
|
536
644
|
|
|
537
|
-
|
|
645
|
+
Prism routes qualifying bounded work—bulk classification, field extraction,
|
|
646
|
+
mechanical formatting, test generation, and similar tasks—to local Ollama
|
|
647
|
+
models before any host-native subagent. The agent checks `gate_outcome`,
|
|
648
|
+
verifies the result, and continues in the current host thread when the local
|
|
649
|
+
worker is unavailable, refused, or degraded.
|
|
538
650
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
651
|
+
Pass project memory when the subtask depends on prior work:
|
|
652
|
+
|
|
653
|
+
```json
|
|
654
|
+
{
|
|
655
|
+
"prompt": "Generate the bounded regression-test cases.",
|
|
656
|
+
"project": "prism-mcp",
|
|
657
|
+
"context_depth": "standard",
|
|
658
|
+
"conversation_id": "<from session_bootstrap>",
|
|
659
|
+
"mode": "code",
|
|
660
|
+
"cloud_fallback": false,
|
|
661
|
+
"escalation": "report"
|
|
662
|
+
}
|
|
542
663
|
```
|
|
543
664
|
|
|
544
|
-
|
|
665
|
+
Omit `context_depth` to use the dashboard setting. Turn off the dashboard Task
|
|
666
|
+
Router toggle or set `PRISM_TASK_ROUTER_ENABLED=false` for an explicit opt-out.
|
|
545
667
|
|
|
546
668
|
**Guardrails:**
|
|
547
|
-
- **
|
|
669
|
+
- **Local by default** — an explicit operator opt-out is preserved
|
|
548
670
|
- **Never delegates:** code/text that ships to the user, security/safety logic, planning/reasoning, anything where a silent quality drop isn't obvious
|
|
549
671
|
- **Always verifies:** checks `quality_gate_failed` and `used_cloud` before trusting local output
|
|
550
672
|
|
|
551
673
|
<details>
|
|
552
674
|
<summary>How Prism survives context compaction</summary>
|
|
553
675
|
|
|
554
|
-
The LLM context window is treated as ephemeral scratch space; durable state lives in the persistent store (SQLite locally, the portal in the cloud). Every session begins with a mandatory `
|
|
676
|
+
The LLM context window is treated as ephemeral scratch space; durable state lives in the persistent store (SQLite locally, the portal in the cloud). Every session begins with a mandatory no-argument `session_bootstrap` call, so Prism applies the dashboard's project and quick/standard/deep setting before the agent writes a response. When a project exceeds a threshold (default 50 entries), `session_compact_ledger` summarizes old entries into a rollup, soft-archives the originals, and links them in the graph. See [`docs/COMPACTION.md`](docs/COMPACTION.md)
|
|
555
677
|
</details>
|
|
556
678
|
|
|
557
679
|
---
|
|
@@ -692,7 +814,7 @@ Routing is automatic: `9b → 4b → cloud fallback` on desktop/server, `2b →
|
|
|
692
814
|
| `PRISM_FORCE_LOCAL` | Force local SQLite regardless of credentials | `false` |
|
|
693
815
|
| `TELEMETRY_WRITE_TOKEN` | Portal analytics token (optional — metrics display works without it) | -- |
|
|
694
816
|
|
|
695
|
-
With no variables set, Prism runs fully local.
|
|
817
|
+
With no variables set, Prism runs fully local. With an active cloud-memory subscription, set `PRISM_SYNALUX_API_KEY` (and leave `PRISM_STORAGE=auto`) to use the Synalux backend; a portal-confirmed free tier remains on local SQLite.
|
|
696
818
|
|
|
697
819
|
---
|
|
698
820
|
|
package/dist/cli.js
CHANGED
|
@@ -7,13 +7,81 @@ import { getStorage, closeStorage } from './storage/index.js';
|
|
|
7
7
|
import { getSetting } from './storage/configStorage.js';
|
|
8
8
|
import { PRISM_USER_ID, SERVER_CONFIG } from './config.js';
|
|
9
9
|
import { getCurrentGitState } from './utils/git.js';
|
|
10
|
-
import { sessionLoadContextHandler, sessionSaveLedgerHandler, sessionSaveHandoffHandler } from './tools/ledgerHandlers.js';
|
|
11
|
-
import { connectHosts, normalizeHostName } from './connect.js';
|
|
10
|
+
import { sessionBootstrapHandler, sessionLoadContextHandler, sessionSaveLedgerHandler, sessionSaveHandoffHandler, } from './tools/ledgerHandlers.js';
|
|
11
|
+
import { configureClaudeAgentPolicy, configureClaudeNativeStartup, configureCodexAgentPolicy, configureCodexNativeStartup, configureGeminiAgentPolicy, configureGeminiNativeStartup, connectHosts, migrateLegacyClaudeHooks, migrateLegacyClaudeInstructions, migrateLegacyClaudeManagedStartup, migrateLegacyClaudeProjectMcp, normalizeHostName, } from './connect.js';
|
|
12
12
|
const program = new Command();
|
|
13
|
+
/** Build the stable `prism load --json` envelope from depth-specific context. */
|
|
14
|
+
export function buildLoadJsonOutput(project, data, level, metadata) {
|
|
15
|
+
let history = [];
|
|
16
|
+
let historyLimit = 0;
|
|
17
|
+
if (level === 'standard') {
|
|
18
|
+
history = Array.isArray(data.recent_sessions) ? data.recent_sessions : [];
|
|
19
|
+
historyLimit = 5;
|
|
20
|
+
}
|
|
21
|
+
else if (level === 'deep') {
|
|
22
|
+
// Canonical deep context uses session_history. Fall back only when that
|
|
23
|
+
// field is absent so older portal responses remain consumable.
|
|
24
|
+
history = Array.isArray(data.session_history)
|
|
25
|
+
? data.session_history
|
|
26
|
+
: Array.isArray(data.recent_sessions)
|
|
27
|
+
? data.recent_sessions
|
|
28
|
+
: [];
|
|
29
|
+
historyLimit = 50;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
agent_name: metadata.agentName || null,
|
|
33
|
+
handoff: [{
|
|
34
|
+
project,
|
|
35
|
+
role: metadata.effectiveRole || data.role || 'global',
|
|
36
|
+
last_summary: data.last_summary || null,
|
|
37
|
+
pending_todo: data.pending_todo || null,
|
|
38
|
+
active_decisions: data.active_decisions || null,
|
|
39
|
+
keywords: data.keywords || null,
|
|
40
|
+
key_context: data.key_context || null,
|
|
41
|
+
active_branch: data.active_branch || null,
|
|
42
|
+
version: data.version ?? null,
|
|
43
|
+
updated_at: data.updated_at || null,
|
|
44
|
+
}],
|
|
45
|
+
// Keep the established key for callers while sourcing the canonical field
|
|
46
|
+
// for the requested depth.
|
|
47
|
+
recent_ledger: history.slice(0, historyLimit).map((entry) => ({
|
|
48
|
+
summary: entry?.summary || null,
|
|
49
|
+
decisions: entry?.decisions || null,
|
|
50
|
+
keywords: entry?.keywords || null,
|
|
51
|
+
created_at: entry?.session_date || entry?.created_at || null,
|
|
52
|
+
})),
|
|
53
|
+
git_hash: metadata.gitHash,
|
|
54
|
+
git_branch: metadata.gitBranch,
|
|
55
|
+
pkg_version: metadata.packageVersion,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
13
58
|
program
|
|
14
59
|
.name('prism')
|
|
15
60
|
.description('Prism — The Mind Palace for AI Agents')
|
|
16
61
|
.version(SERVER_CONFIG.version);
|
|
62
|
+
/** Print the canonical hook-free first-turn display used by the MCP tool. */
|
|
63
|
+
export async function runBootstrapCommand() {
|
|
64
|
+
try {
|
|
65
|
+
const result = await sessionBootstrapHandler({});
|
|
66
|
+
const output = result.content?.map((part) => part?.text).filter(Boolean).join('\n') || '';
|
|
67
|
+
if (!output)
|
|
68
|
+
throw new Error('session_bootstrap returned no display content');
|
|
69
|
+
console.log(output);
|
|
70
|
+
if (result.isError)
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
console.error(`Bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
await closeStorage().catch(() => { });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
program
|
|
82
|
+
.command('bootstrap')
|
|
83
|
+
.description('Print the canonical dashboard-configured first-turn Prism greeting')
|
|
84
|
+
.action(runBootstrapCommand);
|
|
17
85
|
// ─── prism connect ────────────────────────────────────────────
|
|
18
86
|
// Registers this installed package with supported MCP hosts. The
|
|
19
87
|
// merge is additive: an existing `prism` or `prism-mcp` entry is
|
|
@@ -25,7 +93,7 @@ program
|
|
|
25
93
|
.option('--all', 'Target all supported hosts instead of auto-detecting installed hosts')
|
|
26
94
|
.option('--dry-run', 'Preview configuration changes without writing files')
|
|
27
95
|
.option('--refresh', 'Refresh only entries previously created by Prism; custom entries stay untouched')
|
|
28
|
-
.action((options) => {
|
|
96
|
+
.action(async (options) => {
|
|
29
97
|
try {
|
|
30
98
|
if (!options.dryRun) {
|
|
31
99
|
console.log('Close target MCP hosts before registration so they cannot edit configuration concurrently.');
|
|
@@ -63,11 +131,146 @@ program
|
|
|
63
131
|
process.exitCode = 1;
|
|
64
132
|
}
|
|
65
133
|
}
|
|
134
|
+
const connectedClaude = summary.results.some((result) => result.host === 'claude-code' && result.status !== 'error' && result.startupCompatible);
|
|
135
|
+
const connectedGemini = summary.results.some((result) => result.host === 'gemini' && result.status !== 'error' && result.startupCompatible);
|
|
136
|
+
const connectedCodex = summary.results.some((result) => result.host === 'codex' && result.status !== 'error' && result.startupCompatible);
|
|
66
137
|
if (options.dryRun) {
|
|
138
|
+
if (connectedClaude) {
|
|
139
|
+
const projectMcpMigration = migrateLegacyClaudeProjectMcp(undefined, undefined, true);
|
|
140
|
+
const hookMigration = migrateLegacyClaudeHooks(undefined, true);
|
|
141
|
+
const instructionMigration = migrateLegacyClaudeInstructions(undefined, true);
|
|
142
|
+
const managedMigration = migrateLegacyClaudeManagedStartup(undefined, true);
|
|
143
|
+
const nativeStartup = configureClaudeNativeStartup(undefined, true);
|
|
144
|
+
const agentPolicy = configureClaudeAgentPolicy(undefined, true);
|
|
145
|
+
if (projectMcpMigration.status === 'would-remove') {
|
|
146
|
+
console.log(`• Claude Code: would remove exact legacy project Prism registration (${projectMcpMigration.path})`);
|
|
147
|
+
}
|
|
148
|
+
if (hookMigration.status === 'would-remove') {
|
|
149
|
+
console.log(`• Claude Code: would remove ${hookMigration.removed} legacy Prism hook action(s) (${hookMigration.path})`);
|
|
150
|
+
}
|
|
151
|
+
if (instructionMigration.status === 'would-remove') {
|
|
152
|
+
console.log(`• Claude Code: would remove ${instructionMigration.removed} legacy Prism startup section(s) (${instructionMigration.path})`);
|
|
153
|
+
}
|
|
154
|
+
if (managedMigration.status === 'would-remove') {
|
|
155
|
+
console.log(`• Claude Code: would remove legacy managed startup block (${managedMigration.path})`);
|
|
156
|
+
}
|
|
157
|
+
if (nativeStartup.status === 'would-install' || nativeStartup.status === 'would-refresh') {
|
|
158
|
+
console.log(`• Claude Code: would ${nativeStartup.status === 'would-install' ? 'install' : 'refresh'} native startup instructions (${nativeStartup.path})`);
|
|
159
|
+
}
|
|
160
|
+
if (agentPolicy.status === 'would-install' || agentPolicy.status === 'would-refresh') {
|
|
161
|
+
console.log(`• Claude Code: would ${agentPolicy.status === 'would-install' ? 'install' : 'refresh'} local-first fallback policy (${agentPolicy.path})`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (connectedGemini) {
|
|
165
|
+
const nativeStartup = configureGeminiNativeStartup(undefined, true);
|
|
166
|
+
const agentPolicy = configureGeminiAgentPolicy(undefined, true);
|
|
167
|
+
if (nativeStartup.status === 'would-install' || nativeStartup.status === 'would-refresh') {
|
|
168
|
+
console.log(`• Gemini CLI: would ${nativeStartup.status === 'would-install' ? 'install' : 'refresh'} native startup instructions (${nativeStartup.path})`);
|
|
169
|
+
}
|
|
170
|
+
if (agentPolicy.status === 'would-install' || agentPolicy.status === 'would-refresh') {
|
|
171
|
+
console.log(`• Gemini CLI: would ${agentPolicy.status === 'would-install' ? 'disable native agents for' : 'refresh'} local-first policy (${agentPolicy.path})`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (connectedCodex) {
|
|
175
|
+
const nativeStartup = configureCodexNativeStartup(undefined, true);
|
|
176
|
+
const agentPolicy = configureCodexAgentPolicy(undefined, true);
|
|
177
|
+
if (nativeStartup.status === 'would-install' || nativeStartup.status === 'would-refresh') {
|
|
178
|
+
console.log(`• Codex: would ${nativeStartup.status === 'would-install' ? 'install' : 'refresh'} native startup instructions (${nativeStartup.path})`);
|
|
179
|
+
}
|
|
180
|
+
if (agentPolicy.status === 'would-install' || agentPolicy.status === 'would-refresh') {
|
|
181
|
+
console.log(`• Codex: would ${agentPolicy.status === 'would-install' ? 'install' : 'refresh'} hard local-first agent limits (${agentPolicy.path})`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
67
184
|
console.log('\nDry run complete — no files changed.');
|
|
68
185
|
}
|
|
186
|
+
else if (summary.results.some((result) => result.status !== 'error' && result.startupCompatible)) {
|
|
187
|
+
// Codex discovers native skills before it starts MCP servers. Syncing
|
|
188
|
+
// only from server startup therefore makes a fresh install require a
|
|
189
|
+
// second host restart. Materialize the entitlement snapshot before
|
|
190
|
+
// this registration command exits so the first launch sees it.
|
|
191
|
+
const { triggerSkillManifestSync } = await import('./skillManifestSync.js');
|
|
192
|
+
const skillSync = await triggerSkillManifestSync();
|
|
193
|
+
if (skillSync.status === 'failed' || skillSync.status === 'partial') {
|
|
194
|
+
throw new Error(`Synalux skill synchronization failed: ${skillSync.error || skillSync.status}`);
|
|
195
|
+
}
|
|
196
|
+
if (skillSync.status !== 'disabled') {
|
|
197
|
+
const changed = skillSync.installed.length + skillSync.updated.length + skillSync.pruned.length;
|
|
198
|
+
console.log(`✓ Synalux skills: ${skillSync.tier || 'free'} tier (${changed} changed)`);
|
|
199
|
+
if (skillSync.conflicts.length > 0) {
|
|
200
|
+
console.error(`⚠ Preserved locally modified skill conflicts: ${skillSync.conflicts.join(', ')}`);
|
|
201
|
+
}
|
|
202
|
+
if (connectedClaude) {
|
|
203
|
+
// Validate every affected surface before mutating any file.
|
|
204
|
+
migrateLegacyClaudeHooks(undefined, true);
|
|
205
|
+
migrateLegacyClaudeInstructions(undefined, true);
|
|
206
|
+
migrateLegacyClaudeManagedStartup(undefined, true);
|
|
207
|
+
configureClaudeNativeStartup(undefined, true);
|
|
208
|
+
configureClaudeAgentPolicy(undefined, true);
|
|
209
|
+
// Install the canonical block first. Never remove the legacy block
|
|
210
|
+
// unless the canonical path is already current or commits safely.
|
|
211
|
+
const nativeStartup = configureClaudeNativeStartup();
|
|
212
|
+
const agentPolicy = configureClaudeAgentPolicy();
|
|
213
|
+
const projectMcpMigration = migrateLegacyClaudeProjectMcp();
|
|
214
|
+
const hookMigration = migrateLegacyClaudeHooks();
|
|
215
|
+
const instructionMigration = migrateLegacyClaudeInstructions();
|
|
216
|
+
const managedMigration = migrateLegacyClaudeManagedStartup();
|
|
217
|
+
if (projectMcpMigration.status === 'removed') {
|
|
218
|
+
console.log(`✓ Claude Code: removed exact legacy project Prism registration (${projectMcpMigration.path}); user-scope registration now owns Prism`);
|
|
219
|
+
}
|
|
220
|
+
if (hookMigration.status === 'removed') {
|
|
221
|
+
console.log(`✓ Claude Code: removed ${hookMigration.removed} legacy Prism hook action(s); native skills now own startup and sync`);
|
|
222
|
+
}
|
|
223
|
+
if (instructionMigration.status === 'removed') {
|
|
224
|
+
console.log(`✓ Claude Code: removed ${instructionMigration.removed} legacy Prism startup section(s); MCP bootstrap now owns first-turn context`);
|
|
225
|
+
}
|
|
226
|
+
if (managedMigration.status === 'removed') {
|
|
227
|
+
console.log('✓ Claude Code: removed legacy managed startup block from ~/CLAUDE.md');
|
|
228
|
+
}
|
|
229
|
+
if (nativeStartup.status === 'installed' || nativeStartup.status === 'refreshed') {
|
|
230
|
+
console.log(`✓ Claude Code: ${nativeStartup.status} hook-free native startup instructions`);
|
|
231
|
+
}
|
|
232
|
+
if (agentPolicy.status === 'installed' || agentPolicy.status === 'refreshed') {
|
|
233
|
+
console.log(`✓ Claude Code: ${agentPolicy.status} local-first policy with Sonnet fallback`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (connectedGemini) {
|
|
237
|
+
configureGeminiNativeStartup(undefined, true);
|
|
238
|
+
configureGeminiAgentPolicy(undefined, true);
|
|
239
|
+
const nativeStartup = configureGeminiNativeStartup();
|
|
240
|
+
const agentPolicy = configureGeminiAgentPolicy();
|
|
241
|
+
if (nativeStartup.status === 'installed' || nativeStartup.status === 'refreshed') {
|
|
242
|
+
console.log(`✓ Gemini CLI: ${nativeStartup.status} hook-free native startup instructions`);
|
|
243
|
+
}
|
|
244
|
+
if (agentPolicy.status === 'installed' || agentPolicy.status === 'refreshed') {
|
|
245
|
+
console.log(`✓ Gemini CLI: ${agentPolicy.status} local-first policy; native agents disabled`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (connectedCodex) {
|
|
249
|
+
configureCodexNativeStartup(undefined, true);
|
|
250
|
+
configureCodexAgentPolicy(undefined, true);
|
|
251
|
+
const nativeStartup = configureCodexNativeStartup();
|
|
252
|
+
const agentPolicy = configureCodexAgentPolicy();
|
|
253
|
+
if (nativeStartup.status === 'installed' || nativeStartup.status === 'refreshed') {
|
|
254
|
+
console.log(`✓ Codex: ${nativeStartup.status} hook-free native startup instructions`);
|
|
255
|
+
}
|
|
256
|
+
if (agentPolicy.status === 'installed' || agentPolicy.status === 'refreshed') {
|
|
257
|
+
console.log(`✓ Codex: ${agentPolicy.status} hard local-first agent limits`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
else if (connectedClaude) {
|
|
262
|
+
// The project-level npx registration is redundant once the
|
|
263
|
+
// user-level installed-server entry succeeds. Removing that exact
|
|
264
|
+
// legacy tuple is independent of native skill delivery; leave hooks
|
|
265
|
+
// and startup instructions untouched while synchronization is off.
|
|
266
|
+
const projectMcpMigration = migrateLegacyClaudeProjectMcp();
|
|
267
|
+
if (projectMcpMigration.status === 'removed') {
|
|
268
|
+
console.log(`✓ Claude Code: removed exact legacy project Prism registration (${projectMcpMigration.path}); user-scope registration now owns Prism`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
69
272
|
if (!summary.usedApiKey) {
|
|
70
|
-
console.log('PRISM_SYNALUX_API_KEY is not set;
|
|
273
|
+
console.log('PRISM_SYNALUX_API_KEY is not set; no Synalux subscription key was copied into new registrations.');
|
|
71
274
|
}
|
|
72
275
|
}
|
|
73
276
|
catch (err) {
|
|
@@ -101,13 +304,13 @@ program
|
|
|
101
304
|
program
|
|
102
305
|
.command('load <project>')
|
|
103
306
|
.description('Load session context for a project (same output as session_load_context MCP tool)')
|
|
104
|
-
.option('-l, --level <level>', 'Context depth: quick, standard, deep
|
|
307
|
+
.option('-l, --level <level>', 'Context depth: quick, standard, deep (defaults to dashboard setting)')
|
|
105
308
|
.option('-r, --role <role>', 'Role scope for context loading')
|
|
106
309
|
.option('-s, --storage <backend>', 'Storage backend: auto (default, prefers cloud), local (SQLite), or supabase. Overrides PRISM_STORAGE env var.')
|
|
107
310
|
.option('--json', 'Emit machine-readable JSON instead of formatted text')
|
|
108
311
|
.action(async (project, options) => {
|
|
109
312
|
try {
|
|
110
|
-
const { level, role, storage, json: jsonOutput } = options;
|
|
313
|
+
const { level: requestedLevel, role, storage, json: jsonOutput } = options;
|
|
111
314
|
// v9.2.2: --storage flag overrides PRISM_STORAGE env var to prevent
|
|
112
315
|
// split-brain when CLI environment differs from MCP server config.
|
|
113
316
|
if (storage) {
|
|
@@ -119,10 +322,12 @@ program
|
|
|
119
322
|
process.env.PRISM_STORAGE = storage;
|
|
120
323
|
}
|
|
121
324
|
const validLevels = ['quick', 'standard', 'deep'];
|
|
122
|
-
if (!validLevels.includes(
|
|
123
|
-
console.error(`Error: Invalid level "${
|
|
325
|
+
if (requestedLevel && !validLevels.includes(requestedLevel)) {
|
|
326
|
+
console.error(`Error: Invalid level "${requestedLevel}". Must be one of: ${validLevels.join(', ')}`);
|
|
124
327
|
process.exit(1);
|
|
125
328
|
}
|
|
329
|
+
const configuredLevel = requestedLevel ?? await getSetting('default_context_depth', 'standard');
|
|
330
|
+
const level = validLevels.includes(configuredLevel) ? configuredLevel : 'standard';
|
|
126
331
|
if (jsonOutput) {
|
|
127
332
|
// ── JSON mode: structured output for programmatic consumption ──
|
|
128
333
|
const storageBackend = await getStorage();
|
|
@@ -136,30 +341,13 @@ program
|
|
|
136
341
|
}
|
|
137
342
|
const d = data;
|
|
138
343
|
const gitState = getCurrentGitState();
|
|
139
|
-
const output = {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
active_decisions: d.active_decisions || null,
|
|
147
|
-
keywords: d.keywords || null,
|
|
148
|
-
key_context: d.key_context || null,
|
|
149
|
-
active_branch: d.active_branch || null,
|
|
150
|
-
version: d.version ?? null,
|
|
151
|
-
updated_at: d.updated_at || null,
|
|
152
|
-
}],
|
|
153
|
-
recent_ledger: (d.recent_sessions || []).map((s) => ({
|
|
154
|
-
summary: s.summary || null,
|
|
155
|
-
decisions: s.decisions || null,
|
|
156
|
-
keywords: s.keywords || null,
|
|
157
|
-
created_at: s.session_date || s.created_at || null,
|
|
158
|
-
})),
|
|
159
|
-
git_hash: gitState.commitSha ? gitState.commitSha.substring(0, 7) : null,
|
|
160
|
-
git_branch: gitState.branch || null,
|
|
161
|
-
pkg_version: SERVER_CONFIG.version,
|
|
162
|
-
};
|
|
344
|
+
const output = buildLoadJsonOutput(project, d, level, {
|
|
345
|
+
agentName,
|
|
346
|
+
effectiveRole,
|
|
347
|
+
gitHash: gitState.commitSha ? gitState.commitSha.substring(0, 7) : null,
|
|
348
|
+
gitBranch: gitState.branch || null,
|
|
349
|
+
packageVersion: SERVER_CONFIG.version,
|
|
350
|
+
});
|
|
163
351
|
console.log(JSON.stringify(output, null, 2));
|
|
164
352
|
}
|
|
165
353
|
else {
|
|
@@ -167,7 +355,7 @@ program
|
|
|
167
355
|
// Delegates to the real handler so all enrichments (morning briefing,
|
|
168
356
|
// reality drift, SDM recall, visual memory, skill injection,
|
|
169
357
|
// behavioral warnings, etc.) are included automatically.
|
|
170
|
-
const result = await sessionLoadContextHandler({ project, level, role });
|
|
358
|
+
const result = await sessionLoadContextHandler({ project, ...(requestedLevel ? { level: requestedLevel } : {}), role });
|
|
171
359
|
// Surface handler-level errors (e.g. invalid args, storage failures)
|
|
172
360
|
if (result.isError) {
|
|
173
361
|
console.error(result.content[0]?.text || 'Unknown error loading context');
|
|
@@ -646,4 +834,4 @@ program
|
|
|
646
834
|
if (fail > 0)
|
|
647
835
|
process.exit(1);
|
|
648
836
|
});
|
|
649
|
-
program.
|
|
837
|
+
await program.parseAsync(process.argv);
|
package/dist/config.js
CHANGED
|
@@ -81,7 +81,8 @@ export const GOOGLE_SEARCH_CX = process.env.GOOGLE_SEARCH_CX;
|
|
|
81
81
|
//
|
|
82
82
|
// Auto-resolution (PRISM_STORAGE=auto, the default) picks in this order:
|
|
83
83
|
// 1. PRISM_FORCE_LOCAL=true → "local" (override everything)
|
|
84
|
-
// 2.
|
|
84
|
+
// 2. Synalux credentials + portal-confirmed cloud-memory entitlement → "synalux"
|
|
85
|
+
// (portal-confirmed free → "local"; unverifiable entitlement → fail closed)
|
|
85
86
|
// 3. SUPABASE_URL + SUPABASE_KEY set → "supabase" (legacy)
|
|
86
87
|
// 4. else → "local"
|
|
87
88
|
export const PRISM_STORAGE = process.env.PRISM_STORAGE || "auto";
|
|
@@ -124,7 +125,9 @@ export const SUPABASE_CONFIGURED = !!SUPABASE_URL &&
|
|
|
124
125
|
// becomes a thin HTTP client of the synalux portal. This is the paid-tier
|
|
125
126
|
// default. Synalux portal owns project validation, tier gating, audit logs,
|
|
126
127
|
// and hivemind agent coordination.
|
|
127
|
-
|
|
128
|
+
// PRISM_SYNALUX_BASE_URL is canonical. SYNALUX_BASE_URL remains a legacy
|
|
129
|
+
// compatibility alias so every portal client resolves the same origin.
|
|
130
|
+
export const PRISM_SYNALUX_BASE_URL = sanitizeEnv(process.env.PRISM_SYNALUX_BASE_URL || process.env.SYNALUX_BASE_URL);
|
|
128
131
|
export const PRISM_SYNALUX_API_KEY = sanitizeEnv(process.env.PRISM_SYNALUX_API_KEY);
|
|
129
132
|
export const SYNALUX_CONFIGURED = !!PRISM_SYNALUX_BASE_URL &&
|
|
130
133
|
!!PRISM_SYNALUX_API_KEY &&
|
|
@@ -263,14 +266,18 @@ export const PRISM_ACTR_BUFFER_FLUSH_MS = parseInt(process.env.PRISM_ACTR_BUFFER
|
|
|
263
266
|
export const PRISM_ACTR_ACCESS_LOG_RETENTION_DAYS = parseInt(process.env.PRISM_ACTR_ACCESS_LOG_RETENTION_DAYS || "90", 10);
|
|
264
267
|
// ─── v7.1: Task Router Configuration ─────────────────────────
|
|
265
268
|
// Deterministic heuristic-based routing for delegating coding tasks
|
|
266
|
-
// between the host cloud model and
|
|
267
|
-
//
|
|
269
|
+
// between the host cloud model and Prism's local worker. Local-first is the
|
|
270
|
+
// default; set PRISM_TASK_ROUTER_ENABLED=false for an explicit operator opt-out.
|
|
268
271
|
/** Master switch for the task router tool. */
|
|
269
|
-
export const PRISM_TASK_ROUTER_ENABLED_ENV = process.env.PRISM_TASK_ROUTER_ENABLED
|
|
272
|
+
export const PRISM_TASK_ROUTER_ENABLED_ENV = process.env.PRISM_TASK_ROUTER_ENABLED !== "false";
|
|
270
273
|
/** Confidence threshold below which routing defaults to the host model. (Default: 0.6) */
|
|
271
274
|
export const PRISM_TASK_ROUTER_CONFIDENCE_THRESHOLD = parseFloat(process.env.PRISM_TASK_ROUTER_CONFIDENCE_THRESHOLD || "0.6");
|
|
272
|
-
/**
|
|
273
|
-
|
|
275
|
+
/**
|
|
276
|
+
* Operator ceiling for bounded local work (1-10). The router independently
|
|
277
|
+
* rejects architecture, security, tool-heavy, and other host-only work.
|
|
278
|
+
* Default 10 lets prism_infer select every entitled/RAM-safe local tier.
|
|
279
|
+
*/
|
|
280
|
+
export const PRISM_TASK_ROUTER_MAX_CLAW_COMPLEXITY = parseInt(process.env.PRISM_TASK_ROUTER_MAX_CLAW_COMPLEXITY || "10", 10);
|
|
274
281
|
// ─── v7.2: Verification Harness ──────────────────────────────
|
|
275
282
|
/** Master switch for the v7.2.0 enhanced verification harness. */
|
|
276
283
|
export const PRISM_VERIFICATION_HARNESS_ENABLED = process.env.PRISM_VERIFICATION_HARNESS_ENABLED === "true";
|