@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.
Files changed (68) hide show
  1. package/README.md +67 -0
  2. package/fixtures/team/book.json +47 -0
  3. package/fixtures/team/business.json +47 -0
  4. package/fixtures/team/content.json +47 -0
  5. package/fixtures/team/design.json +47 -0
  6. package/fixtures/team/mixed.json +59 -0
  7. package/fixtures/team/research.json +47 -0
  8. package/fixtures/team/software.json +47 -0
  9. package/package.json +1 -9
  10. package/src/.registry-meta-key.pem +3 -0
  11. package/src/active-extension-writer.js +142 -0
  12. package/src/blackboard.js +360 -0
  13. package/src/cli-run.js +91 -0
  14. package/src/codex-agents.js +177 -0
  15. package/src/compute/extract.js +3 -0
  16. package/src/compute/fts5.js +4 -4
  17. package/src/compute/graph-lock.js +0 -2
  18. package/src/compute/migrations/003-tier-semantic.js +3 -3
  19. package/src/compute/runner.js +44 -15
  20. package/src/compute/schema.sql +1 -1
  21. package/src/cross-orchestrator-cli.js +974 -13
  22. package/src/cross-orchestrator.js +9 -1
  23. package/src/dashboard-client.html +353 -1
  24. package/src/dashboard-server.js +318 -2
  25. package/src/design-intelligence.js +721 -0
  26. package/src/dispatch/colon-syntax.js +31 -3
  27. package/src/dispatch/domain-manifest.js +251 -0
  28. package/src/dispatch/extension.js +637 -0
  29. package/src/dispatch/override.js +221 -0
  30. package/src/dispatch-planner.js +1 -0
  31. package/src/dream/runner.mjs +3 -3
  32. package/src/extension-installer.js +1269 -0
  33. package/src/extension-manifest-schema.js +301 -0
  34. package/src/extension-permission-check.mjs +79 -0
  35. package/src/extension-registry.js +619 -0
  36. package/src/extension-signer.js +905 -0
  37. package/src/gate-result-formatter.js +95 -0
  38. package/src/gate-result-schema.js +274 -0
  39. package/src/gate-result.js +195 -0
  40. package/src/intent-router.js +2 -0
  41. package/src/lib/npm-view.js +1 -0
  42. package/src/memory/fts5.js +3 -3
  43. package/src/memory/migrations/002-tier-semantic.js +2 -2
  44. package/src/memory/staleness.js +1 -1
  45. package/src/memory/tier-promotion.js +6 -6
  46. package/src/memory/tokenize.js +1 -1
  47. package/src/memory-feedback.js +372 -0
  48. package/src/override-manifest-schema.js +146 -0
  49. package/src/override-resolver.js +699 -0
  50. package/src/override-use-registry.js +307 -0
  51. package/src/overrides/presets/academic.md +101 -0
  52. package/src/overrides/presets/book.md +87 -0
  53. package/src/overrides/presets/campaign.md +95 -0
  54. package/src/overrides/presets/screenplay.md +99 -0
  55. package/src/recovery/checkpoint.js +191 -0
  56. package/src/redactor.js +2 -0
  57. package/src/runtime-mediator.js +207 -0
  58. package/src/sandbox.js +17 -3
  59. package/src/server.js +94 -2
  60. package/src/swarm/dispatch-prompt.js +154 -0
  61. package/src/swarm/planner.js +399 -0
  62. package/src/swarm/review.js +136 -0
  63. package/src/swarm/worktree.js +239 -0
  64. package/src/team/generator.js +119 -0
  65. package/src/team/schemas.js +341 -0
  66. package/src/trident/dispatch.js +47 -0
  67. package/src/update-check.js +1 -1
  68. package/src/vectors.js +7 -8
@@ -0,0 +1,221 @@
1
+ /**
2
+ * dispatch/override.js
3
+ *
4
+ * IJFW v1.4.0 / W3/t16 — override colon-namespace dispatch handler.
5
+ *
6
+ * Routes `override:<command>` invocations from colon-syntax dispatch and
7
+ * `ijfw override <command>` invocations from the CLI to the W1
8
+ * override-resolver primitives.
9
+ *
10
+ * Commands:
11
+ * add <preset> [scope] — record + redeploy
12
+ * list — read active-overrides.json for project
13
+ * audit — summarise active overrides + extends chains
14
+ * promote <preset> — copy project-scope override to user scope
15
+ * remove <preset> — remove active override + redeploy base
16
+ * deploy <skill> — force redeploy of one skill
17
+ */
18
+
19
+ import {
20
+ recordActiveOverride,
21
+ removeActiveOverride,
22
+ deployResolvedSkill,
23
+ resolveSkill,
24
+ resolveOverridePaths,
25
+ loadOverrideFile,
26
+ } from '../override-resolver.js';
27
+ import { promises as fs } from 'node:fs';
28
+ import path from 'node:path';
29
+ import os from 'node:os';
30
+ import { OVERRIDE_SCOPES, BUILTIN_PRESETS, PRESET_NAME_PATTERN } from '../override-manifest-schema.js';
31
+
32
+ // All built-in presets target ijfw-critique in v1.4.0. We hardcode the
33
+ // affected-skills list for the add/remove paths; if a preset later targets
34
+ // additional skills, expand this map.
35
+ const PRESET_TARGET_SKILLS = {
36
+ book: ['ijfw-critique'],
37
+ campaign: ['ijfw-critique'],
38
+ academic: ['ijfw-critique'],
39
+ screenplay: ['ijfw-critique'],
40
+ };
41
+
42
+ /**
43
+ * Validate preset NAME shape at the dispatch boundary. Strings like
44
+ * `book; rm -rf $HOME` would otherwise round-trip into receipts and
45
+ * prelude suggestions.
46
+ */
47
+ function validatePresetName(preset) {
48
+ if (typeof preset !== 'string' || !PRESET_NAME_PATTERN.test(preset)) {
49
+ return 'invalid preset name';
50
+ }
51
+ return null;
52
+ }
53
+
54
+ /**
55
+ * Confirm a preset name refers to a real preset file. Accepts built-ins,
56
+ * `~/.ijfw/overrides/presets/<preset>.md`, and `~/.ijfw/user-overrides/<preset>`.
57
+ */
58
+ async function presetExists(preset) {
59
+ if (BUILTIN_PRESETS.includes(preset)) return true;
60
+ const home = os.homedir();
61
+ const candidates = [
62
+ path.join(home, '.ijfw', 'overrides', 'presets', `${preset}.md`),
63
+ path.join(home, '.ijfw', 'user-overrides', preset),
64
+ ];
65
+ for (const p of candidates) {
66
+ try {
67
+ await fs.stat(p);
68
+ return true;
69
+ } catch {
70
+ /* try next */
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+
76
+ function nowIso() {
77
+ return new Date().toISOString();
78
+ }
79
+
80
+ async function readActiveOverrides(projectRoot) {
81
+ const file = path.join(os.homedir(), '.ijfw/state/active-overrides.json');
82
+ try {
83
+ const raw = await fs.readFile(file, 'utf8');
84
+ const json = JSON.parse(raw);
85
+ return json?.projects?.[projectRoot]?.active_overrides ?? [];
86
+ } catch {
87
+ return [];
88
+ }
89
+ }
90
+
91
+ async function cmdAdd({ args, projectRoot }) {
92
+ const [preset, rawScope] = args.split(/\s+/).filter(Boolean);
93
+ if (!preset) return { ok: false, command: 'add', error: 'missing preset name' };
94
+ const shapeErr = validatePresetName(preset);
95
+ if (shapeErr) return { ok: false, command: 'add', error: shapeErr };
96
+ if (!(await presetExists(preset))) {
97
+ return { ok: false, command: 'add', error: `unknown preset: ${preset}` };
98
+ }
99
+ const scope = OVERRIDE_SCOPES.includes(rawScope) ? rawScope : 'project';
100
+ const override = { preset, scope, applied_at: nowIso() };
101
+ try {
102
+ await recordActiveOverride(projectRoot, override);
103
+ } catch (err) {
104
+ return { ok: false, command: 'add', error: `recordActiveOverride failed: ${err.message}` };
105
+ }
106
+ const affected = PRESET_TARGET_SKILLS[preset] ?? ['ijfw-critique'];
107
+ const deploys = [];
108
+ for (const skill of affected) {
109
+ try {
110
+ deploys.push(await deployResolvedSkill(skill, projectRoot, {}));
111
+ } catch (err) {
112
+ deploys.push({ skill, deployed: [], failed: [{ platform: 'all', error: err.message }] });
113
+ }
114
+ }
115
+ return { ok: true, command: 'add', result: { preset, scope, affected_skills: affected, deploys } };
116
+ }
117
+
118
+ async function cmdRemove({ args, projectRoot }) {
119
+ const [preset] = args.split(/\s+/).filter(Boolean);
120
+ if (!preset) return { ok: false, command: 'remove', error: 'missing preset name' };
121
+ const shapeErr = validatePresetName(preset);
122
+ if (shapeErr) return { ok: false, command: 'remove', error: shapeErr };
123
+ try {
124
+ await removeActiveOverride(projectRoot, preset);
125
+ } catch (err) {
126
+ return { ok: false, command: 'remove', error: `removeActiveOverride failed: ${err.message}` };
127
+ }
128
+ const affected = PRESET_TARGET_SKILLS[preset] ?? ['ijfw-critique'];
129
+ const deploys = [];
130
+ for (const skill of affected) {
131
+ try {
132
+ deploys.push(await deployResolvedSkill(skill, projectRoot, {}));
133
+ } catch (err) {
134
+ deploys.push({ skill, deployed: [], failed: [{ platform: 'all', error: err.message }] });
135
+ }
136
+ }
137
+ return { ok: true, command: 'remove', result: { preset, affected_skills: affected, deploys } };
138
+ }
139
+
140
+ async function cmdList({ projectRoot }) {
141
+ const active = await readActiveOverrides(projectRoot);
142
+ return { ok: true, command: 'list', result: { active, count: active.length } };
143
+ }
144
+
145
+ async function cmdAudit({ projectRoot }) {
146
+ const active = await readActiveOverrides(projectRoot);
147
+ const items = [];
148
+ for (const entry of active) {
149
+ const affected = PRESET_TARGET_SKILLS[entry.preset] ?? ['ijfw-critique'];
150
+ for (const skill of affected) {
151
+ const paths = resolveOverridePaths(skill, projectRoot);
152
+ let sections = 0;
153
+ let extendsChain = [];
154
+ for (const p of paths) {
155
+ const file = await loadOverrideFile(p).catch(() => null);
156
+ if (!file) continue;
157
+ if (Array.isArray(file.manifest?.extends)) extendsChain = file.manifest.extends;
158
+ const matches = file.body?.match(/<!--\s*ijfw-override:/g);
159
+ sections += matches ? matches.length : 0;
160
+ }
161
+ items.push({ preset: entry.preset, scope: entry.scope, skill, sections, extends: extendsChain });
162
+ }
163
+ }
164
+ return { ok: true, command: 'audit', result: { items } };
165
+ }
166
+
167
+ async function cmdPromote({ args, projectRoot }) {
168
+ const [preset] = args.split(/\s+/).filter(Boolean);
169
+ if (!preset) return { ok: false, command: 'promote', error: 'missing preset name' };
170
+ const shapeErr = validatePresetName(preset);
171
+ if (shapeErr) return { ok: false, command: 'promote', error: shapeErr };
172
+ if (!(await presetExists(preset))) {
173
+ return { ok: false, command: 'promote', error: `unknown preset: ${preset}` };
174
+ }
175
+ const affected = PRESET_TARGET_SKILLS[preset] ?? ['ijfw-critique'];
176
+ const promoted = [];
177
+ for (const skill of affected) {
178
+ const src = path.join(projectRoot, '.ijfw/skill-overrides', skill, 'override.md');
179
+ const dst = path.join(os.homedir(), '.ijfw/user-overrides', skill, 'override.md');
180
+ try {
181
+ const data = await fs.readFile(src, 'utf8');
182
+ await fs.mkdir(path.dirname(dst), { recursive: true });
183
+ await fs.writeFile(dst + '.tmp', data, 'utf8');
184
+ await fs.rename(dst + '.tmp', dst);
185
+ promoted.push({ skill, src, dst });
186
+ } catch (err) {
187
+ promoted.push({ skill, src, dst, error: err.message });
188
+ }
189
+ }
190
+ return { ok: true, command: 'promote', result: { preset, promoted } };
191
+ }
192
+
193
+ async function cmdDeploy({ args, projectRoot }) {
194
+ const [skill] = args.split(/\s+/).filter(Boolean);
195
+ if (!skill) return { ok: false, command: 'deploy', error: 'missing skill name' };
196
+ try {
197
+ const merged = await resolveSkill(skill, projectRoot);
198
+ const r = await deployResolvedSkill(skill, projectRoot, {});
199
+ return { ok: true, command: 'deploy', result: { skill, body_length: merged.length, ...r } };
200
+ } catch (err) {
201
+ return { ok: false, command: 'deploy', error: err.message };
202
+ }
203
+ }
204
+
205
+ export async function overrideDispatch({ command, args = '', projectRoot }) {
206
+ const ctx = { command, args: String(args || ''), projectRoot: String(projectRoot || process.cwd()) };
207
+ switch (command) {
208
+ case 'add': return cmdAdd(ctx);
209
+ case 'remove': return cmdRemove(ctx);
210
+ case 'list': return cmdList(ctx);
211
+ case 'audit': return cmdAudit(ctx);
212
+ case 'promote': return cmdPromote(ctx);
213
+ case 'deploy': return cmdDeploy(ctx);
214
+ default:
215
+ return {
216
+ ok: false,
217
+ command,
218
+ error: `unknown override command: ${command}. Supported: add | list | audit | promote | remove | deploy`,
219
+ };
220
+ }
221
+ }
@@ -6,6 +6,7 @@
6
6
  //
7
7
  // Pure + synchronous. ESM. Zero deps. Filesystem only touched by caller.
8
8
 
9
+ // eslint-disable-next-line security/detect-unsafe-regex -- plan markdown is bounded human-authored text; pattern is line-anchored and token-sized.
9
10
  const WAVE_HEADER = /^###\s+Wave\s+([0-9]+[A-Z])(?:-([A-Za-z0-9_+]+))?\b/;
10
11
  // Bullet sub-wave form: `- **11A-mcp**: description`. Parsed as a child of
11
12
  // the most recently seen Wave header.
@@ -106,7 +106,7 @@ log(`start: host=${opts.host}, reason=${opts.reason}, project=${opts.projectRoot
106
106
  // ---------------------------------------------------------------------------
107
107
  //
108
108
  // D1 (Wave 2 Agent E) lands `mcp-server/src/memory/tier-promotion.js`
109
- // with the deterministic promotion rules from D-PILLAR-SPEC.md §1. We
109
+ // with the deterministic promotion rules from D-PILLAR-SPEC.md section 1. We
110
110
  // import-by-URL so a missing module is a soft skip rather than a top-
111
111
  // level ESM resolve failure.
112
112
  //
@@ -210,7 +210,7 @@ async function runTierPromotion() {
210
210
  }
211
211
  // GA real fix-wave F4: Working->Procedural was missing from the
212
212
  // dream-cycle dispatch. The function exists in tier-promotion.js
213
- // (per D-PILLAR-SPEC §1) but the runner only fired We + Es,
213
+ // (per D-PILLAR-SPEC section 1) but the runner only fired We + Es,
214
214
  // leaving the Procedural tier orphaned. Wire it here.
215
215
  //
216
216
  // Source signal per spec: TaskUpdate completed events with duration
@@ -297,7 +297,7 @@ async function runTierPromotion() {
297
297
  // F4 helper: discover TaskUpdate completed events for Procedural promotion.
298
298
  // ---------------------------------------------------------------------------
299
299
  //
300
- // D-PILLAR-SPEC §1 Working->Procedural promotion fires from TaskUpdate
300
+ // D-PILLAR-SPEC section 1 Working->Procedural promotion fires from TaskUpdate
301
301
  // completed events with duration >= 5min and matching git commit window.
302
302
  // The alpha runner does not yet have a dedicated TaskUpdate event source
303
303
  // in the working memory ledger (events arrive via observation bodies but