@ijfw/memory-server 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/fixtures/team/book.json +47 -0
- package/fixtures/team/business.json +47 -0
- package/fixtures/team/content.json +47 -0
- package/fixtures/team/design.json +47 -0
- package/fixtures/team/mixed.json +59 -0
- package/fixtures/team/research.json +47 -0
- package/fixtures/team/software.json +47 -0
- package/package.json +1 -9
- package/src/.registry-meta-key.pem +3 -0
- package/src/active-extension-writer.js +142 -0
- package/src/blackboard.js +360 -0
- package/src/cli-run.js +91 -0
- package/src/codex-agents.js +177 -0
- package/src/compute/extract.js +3 -0
- package/src/compute/fts5.js +4 -4
- package/src/compute/graph-lock.js +0 -2
- package/src/compute/migrations/003-tier-semantic.js +3 -3
- package/src/compute/runner.js +44 -15
- package/src/compute/schema.sql +1 -1
- package/src/cross-orchestrator-cli.js +974 -13
- package/src/cross-orchestrator.js +9 -1
- package/src/dashboard-client.html +353 -1
- package/src/dashboard-server.js +318 -2
- package/src/design-intelligence.js +721 -0
- package/src/dispatch/colon-syntax.js +31 -3
- package/src/dispatch/domain-manifest.js +251 -0
- package/src/dispatch/extension.js +637 -0
- package/src/dispatch/override.js +221 -0
- package/src/dispatch-planner.js +1 -0
- package/src/dream/runner.mjs +3 -3
- package/src/extension-installer.js +1269 -0
- package/src/extension-manifest-schema.js +301 -0
- package/src/extension-permission-check.mjs +79 -0
- package/src/extension-registry.js +619 -0
- package/src/extension-signer.js +905 -0
- package/src/gate-result-formatter.js +95 -0
- package/src/gate-result-schema.js +274 -0
- package/src/gate-result.js +195 -0
- package/src/intent-router.js +2 -0
- package/src/lib/npm-view.js +1 -0
- package/src/memory/fts5.js +3 -3
- package/src/memory/migrations/002-tier-semantic.js +2 -2
- package/src/memory/staleness.js +1 -1
- package/src/memory/tier-promotion.js +6 -6
- package/src/memory/tokenize.js +1 -1
- package/src/memory-feedback.js +372 -0
- package/src/override-manifest-schema.js +146 -0
- package/src/override-resolver.js +699 -0
- package/src/override-use-registry.js +307 -0
- package/src/overrides/presets/academic.md +101 -0
- package/src/overrides/presets/book.md +87 -0
- package/src/overrides/presets/campaign.md +95 -0
- package/src/overrides/presets/screenplay.md +99 -0
- package/src/recovery/checkpoint.js +191 -0
- package/src/redactor.js +2 -0
- package/src/runtime-mediator.js +207 -0
- package/src/sandbox.js +17 -3
- package/src/server.js +94 -2
- package/src/swarm/dispatch-prompt.js +154 -0
- package/src/swarm/planner.js +399 -0
- package/src/swarm/review.js +136 -0
- package/src/swarm/worktree.js +239 -0
- package/src/team/generator.js +119 -0
- package/src/team/schemas.js +341 -0
- package/src/trident/dispatch.js +47 -0
- package/src/update-check.js +1 -1
- package/src/vectors.js +7 -8
|
@@ -41,7 +41,21 @@ import { fileURLToPath } from 'url';
|
|
|
41
41
|
import { dirname, join } from 'path';
|
|
42
42
|
|
|
43
43
|
// Recognised namespaces -- gates dispatchRun against typos.
|
|
44
|
-
|
|
44
|
+
// v1.4.0 (F7): added 'override', 'extension', 'domain-manifest' for the
|
|
45
|
+
// Open Ecosystem dispatch surface.
|
|
46
|
+
// v1.4.0 (W6/S13): parser now accepts hyphens in namespace ([a-z_][a-z0-9_-]*)
|
|
47
|
+
// so the user-facing spelling 'domain-manifest:<op>' (matching the error
|
|
48
|
+
// message and CLI surface) parses + routes uniformly. The set entry is the
|
|
49
|
+
// hyphenated form so copy-paste from the error string Just Works.
|
|
50
|
+
const RUN_NAMESPACES = new Set([
|
|
51
|
+
'compute',
|
|
52
|
+
'index',
|
|
53
|
+
'detect',
|
|
54
|
+
'graph',
|
|
55
|
+
'override',
|
|
56
|
+
'extension',
|
|
57
|
+
'domain-manifest',
|
|
58
|
+
]);
|
|
45
59
|
const SEARCH_NAMESPACES = new Set(['compute', 'graph']);
|
|
46
60
|
|
|
47
61
|
// --- Parser ----------------------------------------------------------------
|
|
@@ -69,7 +83,9 @@ export function parseColonCommand(input) {
|
|
|
69
83
|
if (colon <= 0) return null;
|
|
70
84
|
|
|
71
85
|
const namespace = s.slice(0, colon);
|
|
72
|
-
|
|
86
|
+
// v1.4.0 (W6/S13): allow hyphens so multi-word namespaces like
|
|
87
|
+
// 'domain-manifest' parse without forcing a separate normalisation step.
|
|
88
|
+
if (!/^[a-z_][a-z0-9_-]*$/.test(namespace)) return null;
|
|
73
89
|
|
|
74
90
|
const remainder = s.slice(colon + 1);
|
|
75
91
|
// Empty remainder -> command present but blank; treat as malformed.
|
|
@@ -138,10 +154,22 @@ export async function dispatchRun(parsed, ctx = {}) {
|
|
|
138
154
|
if (parsed.namespace === 'graph') {
|
|
139
155
|
return dispatchGraph(parsed, { projectRoot, sessionId });
|
|
140
156
|
}
|
|
157
|
+
if (parsed.namespace === 'override') {
|
|
158
|
+
const m = await import('./override.js');
|
|
159
|
+
return m.overrideDispatch({ command: parsed.command, args: parsed.args, projectRoot });
|
|
160
|
+
}
|
|
161
|
+
if (parsed.namespace === 'extension') {
|
|
162
|
+
const m = await import('./extension.js');
|
|
163
|
+
return m.extensionDispatch({ command: parsed.command, args: parsed.args, projectRoot });
|
|
164
|
+
}
|
|
165
|
+
if (parsed.namespace === 'domain-manifest') {
|
|
166
|
+
const m = await import('./domain-manifest.js');
|
|
167
|
+
return m.domainManifestDispatch({ command: parsed.command, args: parsed.args, projectRoot });
|
|
168
|
+
}
|
|
141
169
|
|
|
142
170
|
return {
|
|
143
171
|
ok: false,
|
|
144
|
-
error: 'Unknown ijfw_run sub-command. Supported: compute:python, compute:js, index:<source>, detect:project_type, graph:traverse',
|
|
172
|
+
error: 'Unknown ijfw_run sub-command. Supported: compute:python, compute:js, index:<source>, detect:project_type, graph:traverse, override:<op>, extension:<op>, domain-manifest:<op>',
|
|
145
173
|
};
|
|
146
174
|
}
|
|
147
175
|
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* domain-manifest.js
|
|
3
|
+
*
|
|
4
|
+
* IJFW v1.4.0 Wave 3 / t13 — Domain-manifest auto-loading dispatch.
|
|
5
|
+
*
|
|
6
|
+
* Detects project type via the W1/t5 project-type-detector and maps the
|
|
7
|
+
* primary_type to a built-in domain preset (book / campaign), then records
|
|
8
|
+
* the override in ~/.ijfw/state/active-overrides.json by invoking the W1/t6
|
|
9
|
+
* override-resolver's recordActiveOverride() directly. No child-process
|
|
10
|
+
* shell-out — pure in-process dispatch so session-start can fire-and-forget
|
|
11
|
+
* via `( ... & disown )` and the detection completes regardless of hook
|
|
12
|
+
* exit timing.
|
|
13
|
+
*
|
|
14
|
+
* Mapping (intentional; matches t13 spec):
|
|
15
|
+
* book -> preset "book"
|
|
16
|
+
* content -> preset "book" (closest narrative match)
|
|
17
|
+
* business -> preset "campaign"
|
|
18
|
+
* design -> preset "campaign" (closest match)
|
|
19
|
+
* software -> no preset (no-op)
|
|
20
|
+
* mixed -> no preset (user must pick explicitly)
|
|
21
|
+
* unknown -> no preset (no-op)
|
|
22
|
+
*
|
|
23
|
+
* Contract (per R11 + F11):
|
|
24
|
+
* - Errors are SWALLOWED. Never throws. Fire-and-forget surface.
|
|
25
|
+
* - Idempotent. If the preset is already in active_overrides for this
|
|
26
|
+
* projectRoot it short-circuits and reports loaded:[] with cached:true
|
|
27
|
+
* semantics through domainManifestStatus.
|
|
28
|
+
* - Synchronous detect() is called inside an async function so the caller
|
|
29
|
+
* can `await` without blocking the event loop on the project walk;
|
|
30
|
+
* session-start backgrounds the entire invocation regardless.
|
|
31
|
+
*
|
|
32
|
+
* Discipline:
|
|
33
|
+
* - ESM only.
|
|
34
|
+
* - ASCII only in strings.
|
|
35
|
+
* - No new prod deps.
|
|
36
|
+
* - No console output on the happy path (session-start banner stays clean).
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import fs from 'node:fs/promises';
|
|
40
|
+
import path from 'node:path';
|
|
41
|
+
import os from 'node:os';
|
|
42
|
+
|
|
43
|
+
import { detect } from '../project-type-detector.js';
|
|
44
|
+
import { recordActiveOverride } from '../override-resolver.js';
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Mapping table
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
// project_type -> preset name (or null for no-op).
|
|
51
|
+
const TYPE_TO_PRESET = Object.freeze({
|
|
52
|
+
book: 'book',
|
|
53
|
+
content: 'book',
|
|
54
|
+
business: 'campaign',
|
|
55
|
+
design: 'campaign',
|
|
56
|
+
software: null,
|
|
57
|
+
mixed: null,
|
|
58
|
+
unknown: null,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// State helpers (read-side; recordActiveOverride owns the write-side)
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
function activeOverridesPath() {
|
|
66
|
+
return path.join(os.homedir(), '.ijfw', 'state', 'active-overrides.json');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function readActiveOverridesSafe() {
|
|
70
|
+
try {
|
|
71
|
+
const raw = await fs.readFile(activeOverridesPath(), 'utf8');
|
|
72
|
+
const parsed = JSON.parse(raw);
|
|
73
|
+
if (!parsed || typeof parsed !== 'object' || !parsed.projects) {
|
|
74
|
+
return { projects: {} };
|
|
75
|
+
}
|
|
76
|
+
return parsed;
|
|
77
|
+
} catch {
|
|
78
|
+
// ENOENT or invalid JSON -> empty state. Never throw from a fire-and-forget path.
|
|
79
|
+
return { projects: {} };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function activePresetsForProject(state, projectRoot) {
|
|
84
|
+
const proj = state && state.projects ? state.projects[projectRoot] : null;
|
|
85
|
+
if (!proj || !Array.isArray(proj.active_overrides)) return [];
|
|
86
|
+
return proj.active_overrides
|
|
87
|
+
.filter((o) => o && typeof o.preset === 'string')
|
|
88
|
+
.map((o) => o.preset);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Project type resolution
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Resolve the primary project type. detect() returns an object with both
|
|
97
|
+
* `primary_type` and a `type` alias (per W1/t5 contract). We prefer
|
|
98
|
+
* primary_type; fall back to type; finally fall back to 'unknown'.
|
|
99
|
+
*
|
|
100
|
+
* Wrapped so a thrown detect() (e.g. unreadable root) becomes 'unknown'
|
|
101
|
+
* rather than propagating.
|
|
102
|
+
*/
|
|
103
|
+
function resolveProjectType(projectRoot) {
|
|
104
|
+
try {
|
|
105
|
+
const result = detect(projectRoot, { resume: false });
|
|
106
|
+
if (!result || typeof result !== 'object') return 'unknown';
|
|
107
|
+
const t = result.primary_type || result.type || 'unknown';
|
|
108
|
+
return typeof t === 'string' ? t : 'unknown';
|
|
109
|
+
} catch {
|
|
110
|
+
return 'unknown';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Public API
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* domainManifestLoad(projectRoot) -> Promise<{
|
|
120
|
+
* loaded: string[], // preset names newly loaded this call
|
|
121
|
+
* project_type: string, // primary_type from detect()
|
|
122
|
+
* duration_ms: number, // wall-clock duration
|
|
123
|
+
* error?: string, // present only on swallowed failure
|
|
124
|
+
* }>
|
|
125
|
+
*
|
|
126
|
+
* Detects project type, maps to a preset, and records the override in
|
|
127
|
+
* ~/.ijfw/state/active-overrides.json if not already active. Idempotent.
|
|
128
|
+
* Fire-and-forget contract: NEVER throws.
|
|
129
|
+
*
|
|
130
|
+
* @param {string} projectRoot
|
|
131
|
+
* @returns {Promise<{loaded: string[], project_type: string, duration_ms: number, error?: string}>}
|
|
132
|
+
*/
|
|
133
|
+
export async function domainManifestLoad(projectRoot) {
|
|
134
|
+
const t0 = Date.now();
|
|
135
|
+
try {
|
|
136
|
+
const root = String(projectRoot || process.cwd());
|
|
137
|
+
const projectType = resolveProjectType(root);
|
|
138
|
+
const preset = TYPE_TO_PRESET[projectType] || null;
|
|
139
|
+
|
|
140
|
+
// No mapping -> nothing to load. Honest empty result.
|
|
141
|
+
if (!preset) {
|
|
142
|
+
return {
|
|
143
|
+
loaded: [],
|
|
144
|
+
project_type: projectType,
|
|
145
|
+
duration_ms: Date.now() - t0,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Idempotence check. If this preset is already recorded for this project
|
|
150
|
+
// we skip the write — repeated session-starts must not churn the state file.
|
|
151
|
+
const state = await readActiveOverridesSafe();
|
|
152
|
+
const active = activePresetsForProject(state, root);
|
|
153
|
+
if (active.includes(preset)) {
|
|
154
|
+
return {
|
|
155
|
+
loaded: [],
|
|
156
|
+
project_type: projectType,
|
|
157
|
+
duration_ms: Date.now() - t0,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Record the new override. Scope 'project' = auto-attached at the project
|
|
162
|
+
// tier, not the user/org tier. applied_at left to recordActiveOverride()
|
|
163
|
+
// to stamp.
|
|
164
|
+
await recordActiveOverride(root, {
|
|
165
|
+
preset,
|
|
166
|
+
scope: 'project',
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
loaded: [preset],
|
|
171
|
+
project_type: projectType,
|
|
172
|
+
duration_ms: Date.now() - t0,
|
|
173
|
+
};
|
|
174
|
+
} catch (err) {
|
|
175
|
+
// Swallow. Fire-and-forget surface — never let session-start's detached
|
|
176
|
+
// child crash and emit a stderr line into the user's terminal.
|
|
177
|
+
return {
|
|
178
|
+
loaded: [],
|
|
179
|
+
project_type: 'unknown',
|
|
180
|
+
duration_ms: Date.now() - t0,
|
|
181
|
+
error: err && err.message ? err.message : String(err),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* domainManifestStatus(projectRoot) -> Promise<{
|
|
188
|
+
* project_type: string,
|
|
189
|
+
* active_presets: string[],
|
|
190
|
+
* cached: boolean, // true if active_presets non-empty (i.e. a
|
|
191
|
+
* // prior session already auto-loaded one)
|
|
192
|
+
* }>
|
|
193
|
+
*
|
|
194
|
+
* Used by the prelude to surface a single-line "Active domain: book" beat
|
|
195
|
+
* once auto-load has fired at least once. Never throws.
|
|
196
|
+
*/
|
|
197
|
+
export async function domainManifestStatus(projectRoot) {
|
|
198
|
+
try {
|
|
199
|
+
const root = String(projectRoot || process.cwd());
|
|
200
|
+
const projectType = resolveProjectType(root);
|
|
201
|
+
const state = await readActiveOverridesSafe();
|
|
202
|
+
const active = activePresetsForProject(state, root);
|
|
203
|
+
return {
|
|
204
|
+
project_type: projectType,
|
|
205
|
+
active_presets: active,
|
|
206
|
+
cached: active.length > 0,
|
|
207
|
+
};
|
|
208
|
+
} catch (err) {
|
|
209
|
+
return {
|
|
210
|
+
project_type: 'unknown',
|
|
211
|
+
active_presets: [],
|
|
212
|
+
cached: false,
|
|
213
|
+
error: err && err.message ? err.message : String(err),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
// Test-only exports
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
export const __test = { TYPE_TO_PRESET, resolveProjectType, activePresetsForProject };
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* domainManifestDispatch — colon-syntax dispatch entry point (W3/t16).
|
|
226
|
+
*
|
|
227
|
+
* Commands:
|
|
228
|
+
* load — run domainManifestLoad(projectRoot)
|
|
229
|
+
* status — return domainManifestStatus(projectRoot)
|
|
230
|
+
*/
|
|
231
|
+
export async function domainManifestDispatch({ command, projectRoot }) {
|
|
232
|
+
const root = String(projectRoot || process.cwd());
|
|
233
|
+
try {
|
|
234
|
+
if (command === 'load') {
|
|
235
|
+
const result = await domainManifestLoad(root);
|
|
236
|
+
return { ok: true, command, result };
|
|
237
|
+
}
|
|
238
|
+
if (command === 'status') {
|
|
239
|
+
const result = await domainManifestStatus(root);
|
|
240
|
+
return { ok: true, command, result };
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
ok: false,
|
|
244
|
+
command,
|
|
245
|
+
error: `unknown domain-manifest command: ${command}. Supported: load | status`,
|
|
246
|
+
};
|
|
247
|
+
} catch (err) {
|
|
248
|
+
return { ok: false, command, error: err.message };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|