prism-mcp-server 20.2.1 → 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 +119 -14
- package/dist/cli.js +221 -33
- package/dist/config.js +12 -6
- package/dist/connect.js +848 -15
- 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 +4 -3
- package/dist/storage/inferMetricsLedger.js +72 -14
- package/dist/storage/panelMetricsSpool.js +324 -0
- 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/inferenceMetrics.js +22 -3
- package/dist/utils/modelPicker.js +3 -3
- package/dist/utils/synaluxJwt.js +2 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -18,6 +18,75 @@ 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
|
+
|
|
21
90
|
## What's New in v20.2.1
|
|
22
91
|
|
|
23
92
|
### Subscription-Aware Memory Storage
|
|
@@ -133,16 +202,37 @@ Use `prism connect --all` to target all five, `--host <name>` for one host, or
|
|
|
133
202
|
`--dry-run` to preview the files that would change. Existing `prism` and
|
|
134
203
|
`prism-mcp` entries are never overwritten by default. `--refresh` updates only
|
|
135
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.
|
|
136
209
|
Close the target MCP hosts before a non-dry-run registration so they cannot
|
|
137
210
|
edit their configuration at the same time.
|
|
138
211
|
|
|
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
|
+
|
|
139
228
|
Set `PRISM_STORAGE` before running `prism connect` to preserve an explicit
|
|
140
229
|
storage choice in the generated host entries. This does not change local-model
|
|
141
230
|
routing; Synalux cloud storage separately requires an active cloud-memory
|
|
142
231
|
entitlement.
|
|
143
232
|
|
|
144
|
-
Codex registration preserves `~/.codex/config.toml`
|
|
145
|
-
|
|
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,
|
|
146
236
|
matching Codex's own contract. Restart Codex CLI, the
|
|
147
237
|
IDE extension, or the ChatGPT desktop app after connecting.
|
|
148
238
|
|
|
@@ -499,7 +589,8 @@ Prism exposes 40+ MCP tools. The core memory loop:
|
|
|
499
589
|
|
|
500
590
|
| Tool | What it does |
|
|
501
591
|
|---|---|
|
|
502
|
-
| `
|
|
592
|
+
| `session_bootstrap` | Hook-free first-turn greeting and dashboard-configured context |
|
|
593
|
+
| `session_load_context` | Explicit project reload or older-server startup fallback |
|
|
503
594
|
| `session_save_ledger` | Append an immutable session log entry |
|
|
504
595
|
| `session_save_handoff` | Save live state for the next session |
|
|
505
596
|
| `knowledge_search` | Semantic + keyword search over all memories |
|
|
@@ -508,7 +599,7 @@ Prism exposes 40+ MCP tools. The core memory loop:
|
|
|
508
599
|
| `verify_behavior` | Pre-edit scenario challenge — catch bad changes before they happen |
|
|
509
600
|
| `knowledge_ingest` | Teach Prism a codebase or document |
|
|
510
601
|
| `prism_infer` | Local-first inference (route/chat/code modes, thinking, cloud escalation) |
|
|
511
|
-
| `inference_metrics` | Session delegation
|
|
602
|
+
| `inference_metrics` | Session delegation or persisted MCP + VS Code panel local/cloud stats |
|
|
512
603
|
|
|
513
604
|
### `prism_infer` — local-first inference with cloud escalation
|
|
514
605
|
|
|
@@ -533,7 +624,7 @@ Full TypeScript signatures live in [`src/tools/`](src/tools/); architecture in [
|
|
|
533
624
|
|
|
534
625
|
### `inference_metrics` — see your local-model usage on demand
|
|
535
626
|
|
|
536
|
-
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:
|
|
537
628
|
|
|
538
629
|
```
|
|
539
630
|
📊 Inference Metrics — local-model delegation (this session):
|
|
@@ -547,28 +638,42 @@ Call `inference_metrics` anytime mid-session to see how many `prism_infer` calls
|
|
|
547
638
|
|
|
548
639
|
The same block also appears automatically in `session_save_ledger` and `session_save_handoff` responses at session end.
|
|
549
640
|
|
|
550
|
-
**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.
|
|
551
642
|
|
|
552
|
-
### Local-model delegation (
|
|
643
|
+
### Local-model delegation (default)
|
|
553
644
|
|
|
554
|
-
|
|
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.
|
|
555
650
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
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
|
+
}
|
|
559
663
|
```
|
|
560
664
|
|
|
561
|
-
|
|
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.
|
|
562
667
|
|
|
563
668
|
**Guardrails:**
|
|
564
|
-
- **
|
|
669
|
+
- **Local by default** — an explicit operator opt-out is preserved
|
|
565
670
|
- **Never delegates:** code/text that ships to the user, security/safety logic, planning/reasoning, anything where a silent quality drop isn't obvious
|
|
566
671
|
- **Always verifies:** checks `quality_gate_failed` and `used_cloud` before trusting local output
|
|
567
672
|
|
|
568
673
|
<details>
|
|
569
674
|
<summary>How Prism survives context compaction</summary>
|
|
570
675
|
|
|
571
|
-
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)
|
|
572
677
|
</details>
|
|
573
678
|
|
|
574
679
|
---
|
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,9 +131,144 @@ 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
273
|
console.log('PRISM_SYNALUX_API_KEY is not set; no Synalux subscription key was copied into new registrations.');
|
|
71
274
|
}
|
|
@@ -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
|
@@ -125,7 +125,9 @@ export const SUPABASE_CONFIGURED = !!SUPABASE_URL &&
|
|
|
125
125
|
// becomes a thin HTTP client of the synalux portal. This is the paid-tier
|
|
126
126
|
// default. Synalux portal owns project validation, tier gating, audit logs,
|
|
127
127
|
// and hivemind agent coordination.
|
|
128
|
-
|
|
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);
|
|
129
131
|
export const PRISM_SYNALUX_API_KEY = sanitizeEnv(process.env.PRISM_SYNALUX_API_KEY);
|
|
130
132
|
export const SYNALUX_CONFIGURED = !!PRISM_SYNALUX_BASE_URL &&
|
|
131
133
|
!!PRISM_SYNALUX_API_KEY &&
|
|
@@ -264,14 +266,18 @@ export const PRISM_ACTR_BUFFER_FLUSH_MS = parseInt(process.env.PRISM_ACTR_BUFFER
|
|
|
264
266
|
export const PRISM_ACTR_ACCESS_LOG_RETENTION_DAYS = parseInt(process.env.PRISM_ACTR_ACCESS_LOG_RETENTION_DAYS || "90", 10);
|
|
265
267
|
// ─── v7.1: Task Router Configuration ─────────────────────────
|
|
266
268
|
// Deterministic heuristic-based routing for delegating coding tasks
|
|
267
|
-
// between the host cloud model and
|
|
268
|
-
//
|
|
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.
|
|
269
271
|
/** Master switch for the task router tool. */
|
|
270
|
-
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";
|
|
271
273
|
/** Confidence threshold below which routing defaults to the host model. (Default: 0.6) */
|
|
272
274
|
export const PRISM_TASK_ROUTER_CONFIDENCE_THRESHOLD = parseFloat(process.env.PRISM_TASK_ROUTER_CONFIDENCE_THRESHOLD || "0.6");
|
|
273
|
-
/**
|
|
274
|
-
|
|
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);
|
|
275
281
|
// ─── v7.2: Verification Harness ──────────────────────────────
|
|
276
282
|
/** Master switch for the v7.2.0 enhanced verification harness. */
|
|
277
283
|
export const PRISM_VERIFICATION_HARNESS_ENABLED = process.env.PRISM_VERIFICATION_HARNESS_ENABLED === "true";
|