@agfpd/iapeer-memory 0.2.5 → 0.2.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agfpd/iapeer-memory",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "iapeer-memory — peer memory for the iapeer ecosystem: vault, memoryd (index/search/MCP-http), layer-5 context fragments, role doctrines. The package IS the system; the claude/codex plugins are thin session sockets (docs/10-distribution.md, ADR-009).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,7 +27,7 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@agfpd/iapeer-memory-core": "0.2.5"
30
+ "@agfpd/iapeer-memory-core": "0.2.7"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/bun": "^1.2.0",
package/src/cli.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * verify found problems, 2 usage error or not-yet-implemented stage.
14
14
  */
15
15
 
16
- import { isUnderProdAnchor, sandboxEnvArmed } from "@agfpd/iapeer-memory-core";
16
+ import { sandboxBlocksProdRead } from "@agfpd/iapeer-memory-core";
17
17
  import { loadConfigFile } from "./config-env.js";
18
18
  import { liveEgress } from "./egress.js";
19
19
  import { memoryPaths } from "./paths.js";
@@ -92,7 +92,7 @@ export async function main(argv: string[]): Promise<number> {
92
92
  // process. A test must pass ITS OWN IAPEER_MEMORY_CONFIG_FILE.
93
93
  if (cmd && !["help", "--help", "-h", "version", "--version", "-V"].includes(cmd)) {
94
94
  const configFile = memoryPaths().configFile;
95
- if (sandboxEnvArmed() && isUnderProdAnchor(configFile)) {
95
+ if (sandboxBlocksProdRead(configFile)) {
96
96
  console.error(
97
97
  `iapeer-memory: live config.env skipped under the test sandbox (${configFile}) — pass IAPEER_MEMORY_CONFIG_FILE`,
98
98
  );
@@ -14,8 +14,8 @@
14
14
  *
15
15
  * Step order: deps → vault → config → binary → templates → role peers +
16
16
  * doctrines + roles manifest → fleet map → watcher registration → direct
17
- * session surfaces sweep (ADR-009 v1.2) → legacy v1.1 plugin off (while the
18
- * old declaration is still readable) → slot declaration (v1.2 provision
17
+ * session surfaces sweep (ADR-009 v1.2) → legacy v1.1 manual hint (the
18
+ * plugin channel is removed, ADR-017) → slot declaration (v1.2 provision
19
19
  * command) → native-memory sweep (core verb, soft-skip on old cores) →
20
20
  * host-wide guide fragment. Ecosystem steps are skippable (--skip-ecosystem)
21
21
  * for sandboxed runs; the binary compile is skippable (--skip-binary) for
@@ -36,7 +36,7 @@ import { IAPEER_BIN, type Egress } from "../egress.js";
36
36
  import { memoryPaths } from "../paths.js";
37
37
  import { provisionVault, writeDefaultConfig } from "../provision.js";
38
38
  import { writeRolesManifest, type RoleEntry } from "../roles.js";
39
- import { applyMemoryPlugin, readSlot, writeSlot, SLOT_PROVIDER } from "../slot.js";
39
+ import { readSlot, writeSlot, SLOT_PROVIDER } from "../slot.js";
40
40
  import { readFleetMap, writeFleetMap } from "../fleet.js";
41
41
  import { withProvisionLock } from "../surfaces/lock.js";
42
42
  import { sweepProvision } from "../surfaces/sweep.js";
@@ -470,27 +470,25 @@ export async function cmdInit(argv: string[], egress: Egress): Promise<number> {
470
470
  }
471
471
  }
472
472
 
473
- // 8c. v1.1 → v1.2 migration: sweep the legacy session plugin off the
474
- // fleet while the old declaration is STILL readable and ONLY after
475
- // the direct surfaces landed cleanly.
473
+ // 8c. v1.1 → v1.2 migration: the plugin channel is REMOVED (ADR-017) —
474
+ // the package no longer shells the core verb; a v1.1 host gets the
475
+ // manual recipe and the slot migrates ONLY after the direct surfaces
476
+ // landed cleanly (never strand a host with neither channel).
476
477
  let migrationBlocked = false;
477
478
  if (!flags.skipEcosystem && existingSlot?.plugin) {
478
479
  if (!surfacesOk) {
479
480
  migrationBlocked = true;
480
481
  step(
481
482
  "plugin-off",
482
- "POSTPONED: direct surfaces did not land cleanly — legacy plugin and v1.1 slot kept (re-run init after fixing)",
483
+ "POSTPONED: direct surfaces did not land cleanly — v1.1 slot kept (re-run init after fixing)",
483
484
  false,
484
485
  );
485
486
  } else {
486
- const off = applyMemoryPlugin(egress, { mode: "off", iapeerBin: flags.iapeerBin });
487
487
  step(
488
488
  "plugin-off",
489
- off.suppressed
490
- ? "skipped (test sandbox core calls suppressed)"
491
- : off.ok
492
- ? "legacy v1.1 session plugin swept off the fleet (memory-plugin off --all)"
493
- : `legacy plugin off failed (${off.detail.slice(0, 120)}) — manual: iapeer memory-plugin off --all (or per peer: claude plugin uninstall iapeer-memory@agfpd --scope project)`,
489
+ "legacy v1.1 session plugin is NOT auto-removed (channel removed, ADR-017) — manual, per claude peer: " +
490
+ "`claude plugin uninstall iapeer-memory@agfpd --scope project` from its cwd; codex (host-global): " +
491
+ "`codex plugin remove iapeer-memory@agfpd`. Until then it stamps in parallel (idempotent).",
494
492
  );
495
493
  }
496
494
  }
@@ -21,7 +21,7 @@ import type { Egress } from "../egress.js";
21
21
  import { memoryPaths } from "../paths.js";
22
22
  import { removeBinary } from "../binary.js";
23
23
  import { readFleetMap } from "../fleet.js";
24
- import { applyMemoryPlugin, readSlot, removeSlot, SLOT_PROVIDER } from "../slot.js";
24
+ import { readSlot, removeSlot, SLOT_PROVIDER } from "../slot.js";
25
25
  import { withProvisionLock } from "../surfaces/lock.js";
26
26
  import { sweepUnprovision } from "../surfaces/sweep.js";
27
27
  import { guardedUnlinkSync } from "@agfpd/iapeer-memory-core";
@@ -130,19 +130,14 @@ export function cmdUninstall(argv: string[], egress: Egress): number {
130
130
  }
131
131
  }
132
132
 
133
- // Legacy v1.1 path: the slot still carries a plugin block sweep the
134
- // session plugin off via the core verb WHILE the declaration is alive
135
- // (it derives the identity from it; agreed order, auto-removal).
133
+ // Legacy v1.1 path: the slot still carries a plugin block. The plugin
134
+ // channel is REMOVED (ADR-017) no core verb is shelled; the manual
135
+ // recipe works without the slot.
136
136
  if (declared.plugin) {
137
- const off = applyMemoryPlugin(egress, { mode: "off", iapeerBin });
138
137
  console.log(
139
- `plugin : ${
140
- off.suppressed
141
- ? "skipped (test sandbox core calls suppressed)"
142
- : off.ok
143
- ? "legacy session plugin removed across the fleet (memory-plugin off --all; codex side is host-global)"
144
- : `off not applied (${off.detail.slice(0, 160)}) — manual fallback (works without the slot): per claude peer \`claude plugin uninstall iapeer-memory@agfpd --scope project\` from its cwd; codex (host-global): \`codex plugin remove iapeer-memory@agfpd\``
145
- }`,
138
+ "plugin : legacy v1.1 session plugin is NOT auto-removed (channel removed, ADR-017) — manual: " +
139
+ "per claude peer `claude plugin uninstall iapeer-memory@agfpd --scope project` from its cwd; " +
140
+ "codex (host-global): `codex plugin remove iapeer-memory@agfpd`",
146
141
  );
147
142
  }
148
143
  }
@@ -16,10 +16,10 @@
16
16
  * 5. surfaces — direct per-peer session surfaces sweep over the map
17
17
  * (ADR-009 v1.2: the «всё на местах у подключённых пиров»
18
18
  * duty — both runtimes, idempotent, repairs drift);
19
- * 6. plugin-off — v1.1→v1.2 migration: when the on-disk slot still
20
- * carries a plugin block, sweep the legacy plugin off the
21
- * fleet (while the old declaration is STILL readable by
22
- * the core verb) — only after surfaces landed cleanly;
19
+ * 6. plugin-off — v1.1→v1.2 migration: a slot still carrying a plugin
20
+ * block gets the MANUAL removal recipe (the plugin
21
+ * channel is removed, ADR-017) the slot migrates only
22
+ * after surfaces landed cleanly;
23
23
  * 7. slot — re-declare in the v1.2 form (provision command blocks,
24
24
  * new version — contract obligation);
25
25
  * 8. launcher + triggers + guide — regenerate;
@@ -44,7 +44,7 @@ import type { Egress } from "../egress.js";
44
44
  import { readFleetMap, writeFleetMap } from "../fleet.js";
45
45
  import { memoryPaths } from "../paths.js";
46
46
  import { readRolesManifest } from "../roles.js";
47
- import { applyMemoryPlugin, readSlot, writeSlot, SLOT_PROVIDER } from "../slot.js";
47
+ import { readSlot, writeSlot, SLOT_PROVIDER } from "../slot.js";
48
48
  import { withProvisionLock } from "../surfaces/lock.js";
49
49
  import { sweepProvision } from "../surfaces/sweep.js";
50
50
  import { mcpPort } from "./provision-peer.js";
@@ -187,28 +187,25 @@ export function cmdUpdate(argv: string[], egress: Egress): number {
187
187
  }
188
188
  }
189
189
 
190
- // 6. v1.1 → v1.2 migration (one-shot per host): the on-disk slot still
191
- // carries a plugin block sweep the legacy plugin off the fleet WHILE the
192
- // old declaration is still readable (the core verb derives the identity
193
- // from it), and ONLY after the direct surfaces landed cleanly.
190
+ // 6. v1.1 → v1.2 migration (one-shot per host): the plugin channel is
191
+ // REMOVED (ADR-017) no core verb is shelled; a v1.1 host gets the
192
+ // manual recipe and the slot migrates ONLY after the direct surfaces
193
+ // landed cleanly (never strand a host with neither channel).
194
194
  let migrationBlocked = false;
195
195
  if (!slotForeign && existingSlot?.plugin) {
196
196
  if (!surfacesOk) {
197
197
  migrationBlocked = true;
198
198
  step(
199
199
  "plugin-off",
200
- "POSTPONED: direct surfaces did not land cleanly — legacy plugin and v1.1 slot kept (fix and re-run update)",
200
+ "POSTPONED: direct surfaces did not land cleanly — v1.1 slot kept (fix and re-run update)",
201
201
  false,
202
202
  );
203
203
  } else {
204
- const off = applyMemoryPlugin(egress, { mode: "off" });
205
204
  step(
206
205
  "plugin-off",
207
- off.suppressed
208
- ? "skipped (test sandbox core calls suppressed)"
209
- : off.ok
210
- ? "legacy v1.1 session plugin swept off the fleet (memory-plugin off --all)"
211
- : `legacy plugin off failed (${off.detail.slice(0, 120)}) — manual: iapeer memory-plugin off --all`,
206
+ "legacy v1.1 session plugin is NOT auto-removed (channel removed, ADR-017) — manual, per claude peer: " +
207
+ "`claude plugin uninstall iapeer-memory@agfpd --scope project` from its cwd; codex (host-global): " +
208
+ "`codex plugin remove iapeer-memory@agfpd`. Until then it stamps in parallel (idempotent).",
212
209
  );
213
210
  }
214
211
  }
package/src/fleet.ts CHANGED
@@ -21,7 +21,7 @@
21
21
  import fs from "node:fs";
22
22
  import path from "node:path";
23
23
  import { IAPEER_BIN, type Egress } from "./egress.js";
24
- import { guardedWriteFileSync } from "@agfpd/iapeer-memory-core";
24
+ import { guardedWriteFileSync, sandboxBlocksProdRead } from "@agfpd/iapeer-memory-core";
25
25
 
26
26
  export type FleetMapResult = {
27
27
  action: "written" | "failed";
@@ -48,6 +48,15 @@ export type FleetPeer = { personality: string; cwd: string; runtimes: string[] }
48
48
  * (pre-v1.2 maps) read as `runtimes: []` — the sweep skips them until the
49
49
  * next map re-write (init/update/verify --repair). */
50
50
  export function readFleetMap(fleetMapPath: string): FleetPeer[] | null {
51
+ // Read-as-egress (И4 parity): the prod fleet map NAMES live cwds — a
52
+ // sandboxed process must not learn them. Null = «map unreadable», every
53
+ // caller already reports that honestly instead of sweeping.
54
+ if (sandboxBlocksProdRead(fleetMapPath)) {
55
+ console.error(
56
+ `iapeer-memory: live fleet map skipped under the test sandbox (${fleetMapPath}) — set IAPEER_MEMORY_STATE_DIR/IAPEER_ROOT`,
57
+ );
58
+ return null;
59
+ }
51
60
  try {
52
61
  const raw = JSON.parse(fs.readFileSync(fleetMapPath, "utf-8")) as {
53
62
  peers?: Array<{ personality?: unknown; cwd?: unknown; runtimes?: unknown }>;
package/src/slot.ts CHANGED
@@ -31,8 +31,11 @@
31
31
 
32
32
  import fs from "node:fs";
33
33
  import path from "node:path";
34
- import { IAPEER_BIN, type Egress } from "./egress.js";
35
- import { guardedWriteFileSync, guardedUnlinkSync } from "@agfpd/iapeer-memory-core";
34
+ import {
35
+ guardedWriteFileSync,
36
+ guardedUnlinkSync,
37
+ sandboxBlocksProdRead,
38
+ } from "@agfpd/iapeer-memory-core";
36
39
 
37
40
  export const SLOT_PROVIDER = "iapeer-memory";
38
41
  export const SLOT_PACKAGE = "@agfpd/iapeer-memory";
@@ -100,6 +103,10 @@ export type MemoryProviderSlot = {
100
103
 
101
104
  /** Never throws: missing / unreadable / malformed → null (empty slot). */
102
105
  export function readSlot(slotPath: string): MemoryProviderSlot | null {
106
+ // Read-as-egress (И4 parity): the PROD slot gates fleet-wide sweeps
107
+ // («slot is ours» is TRUE on the live host) — a sandboxed process must
108
+ // read it as absent and refuse/skip honestly.
109
+ if (sandboxBlocksProdRead(slotPath)) return null;
103
110
  try {
104
111
  const parsed = JSON.parse(fs.readFileSync(slotPath, "utf-8")) as MemoryProviderSlot;
105
112
  if (!parsed || typeof parsed.provider !== "string") return null;
@@ -165,54 +172,7 @@ export function removeSlot(slotPath: string): SlotRemoveResult {
165
172
  return "removed";
166
173
  }
167
174
 
168
- export type MemoryPluginApplyResult = {
169
- ok: boolean;
170
- detail: string;
171
- /** True when the test-sandbox fuse blocked the call callers report a
172
- * SKIP, not a failure (same contract as watcher.ts iapSend). */
173
- suppressed?: boolean;
174
- };
175
-
176
- /**
177
- * Fleet-wide install/remove of the slot-declared session plugin via the core
178
- * verb `iapeer memory-plugin <on|off> --all` (iapeer ≥0.2.25; marketplace
179
- * ensure + stale-cache retry live INSIDE the verb). The verb derives the
180
- * plugin identity from the slot declaration — so `on` runs AFTER writeSlot
181
- * and `off` runs BEFORE removeSlot (agreed order, auto-removal: a dead
182
- * provider's plugin must not keep injecting).
183
- *
184
- * The hard fuse of incident 10.06 (`on --all` mutates the HOST fleet — no
185
- * sandbox env contains it) lives in the egress constructor now
186
- * (deny-by-default §4): a refusing handle blocks the spawn here.
187
- */
188
- export function applyMemoryPlugin(
189
- egress: Egress,
190
- opts: {
191
- mode: "on" | "off";
192
- iapeerBin?: string;
193
- },
194
- ): MemoryPluginApplyResult {
195
- const bin = opts.iapeerBin ?? IAPEER_BIN;
196
- const proc = egress.spawnSync([bin, "memory-plugin", opts.mode, "--all"], {
197
- explicitBin: opts.iapeerBin !== undefined,
198
- });
199
- if (proc.refused) {
200
- return {
201
- ok: false,
202
- suppressed: true,
203
- detail: "memory-plugin call suppressed (test sandbox)",
204
- };
205
- }
206
- if (proc.spawnError) {
207
- return { ok: false, detail: `${bin} unavailable: ${proc.spawnError}` };
208
- }
209
- if (proc.exitCode !== 0) {
210
- return {
211
- ok: false,
212
- detail:
213
- (proc.stderr.trim() || proc.stdout.trim() || "").slice(0, 200) ||
214
- `iapeer memory-plugin exited ${proc.exitCode}`,
215
- };
216
- }
217
- return { ok: true, detail: proc.stdout.trim() };
218
- }
175
+ // applyMemoryPlugin (the core verb `iapeer memory-plugin <on|off> --all`)
176
+ // was REMOVED with the plugin channel (ADR-017): v1.1 hosts get a manual
177
+ // recipe in init/update/uninstall instead of an auto-sweep. The `plugin`
178
+ // field on the slot type stays it is the v1.1 READ marker.
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Manifest version sync (docs/10 §Версионная синхронизация): propagate the
3
3
  * facade `package/package.json` version into every other manifest of the
4
- * monorepo — `core/package.json` + `adapters/{claude,codex}` plugin
5
- * manifests. Wired into the npm `version` lifecycle, runnable standalone:
4
+ * monorepo — `core/package.json` (the adapter plugin manifests left with
5
+ * the plugin channel, ADR-017). Wired into the npm `version` lifecycle,
6
+ * runnable standalone:
6
7
  *
7
8
  * bun src/sync-versions.ts
8
9
  *
9
- * Missing manifests are reported and skipped (the adapters land in P2/P5 —
10
- * the script must not fail before they exist). The reference
10
+ * Missing manifests are reported and skipped. The reference
11
11
  * sync-plugin-version pattern, generalised to N manifests.
12
12
  */
13
13
 
@@ -18,11 +18,7 @@ import { guardedWriteFileSync } from "@agfpd/iapeer-memory-core";
18
18
  export type SyncOutcome = { file: string; action: "updated" | "identical" | "missing" };
19
19
 
20
20
  /** Relative (to the monorepo root) manifests that must carry one version. */
21
- export const SYNC_TARGETS = [
22
- "core/package.json",
23
- "adapters/claude/.claude-plugin/plugin.json",
24
- "adapters/codex/.codex-plugin/plugin.json",
25
- ] as const;
21
+ export const SYNC_TARGETS = ["core/package.json"] as const;
26
22
 
27
23
  export function syncVersions(opts: {
28
24
  rootDir: string;
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Embedded skill files — the DIRECT-surface form of the four session skills
3
3
  * (ADR-009 v1.2: direct per-peer surfaces instead of the plugin socket).
4
- * Bodies are the boris-accepted plugin skills (adapters/claude/skills, spot-
4
+ * Bodies are the boris-accepted plugin-era skills (historical provenance:
5
+ * adapters/claude/skills, removed with the plugin channel — ADR-017; spot-
5
6
  * checked against the live CLI 10.06) with exactly two deltas:
6
7
  *
7
8
  * 1. names are namespaced `iapeer-memory-*` (boris design input: direct