@apnex/network-adapter 0.1.11 → 0.1.12

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 (47) hide show
  1. package/dist/bin/seed-skills.d.ts +42 -0
  2. package/dist/bin/seed-skills.js +177 -0
  3. package/dist/bin/seed-skills.js.map +1 -0
  4. package/dist/build-info.json +3 -3
  5. package/dist/control-plane/contracts.d.ts +99 -0
  6. package/dist/control-plane/contracts.js +22 -0
  7. package/dist/control-plane/contracts.js.map +1 -0
  8. package/dist/control-plane/index.d.ts +14 -0
  9. package/dist/control-plane/index.js +3 -0
  10. package/dist/control-plane/index.js.map +1 -0
  11. package/dist/control-plane/reconcile-loop.d.ts +50 -0
  12. package/dist/control-plane/reconcile-loop.js +83 -0
  13. package/dist/control-plane/reconcile-loop.js.map +1 -0
  14. package/dist/control-plane/spec-store.d.ts +29 -0
  15. package/dist/control-plane/spec-store.js +41 -0
  16. package/dist/control-plane/spec-store.js.map +1 -0
  17. package/dist/index.d.ts +3 -2
  18. package/dist/index.js +3 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/skills/claude-skill-actuator.d.ts +28 -0
  21. package/dist/skills/claude-skill-actuator.js +147 -0
  22. package/dist/skills/claude-skill-actuator.js.map +1 -0
  23. package/dist/skills/contracts.d.ts +33 -0
  24. package/dist/skills/contracts.js +15 -0
  25. package/dist/skills/contracts.js.map +1 -0
  26. package/dist/skills/file-skill-ledger.d.ts +7 -0
  27. package/dist/skills/file-skill-ledger.js +38 -0
  28. package/dist/skills/file-skill-ledger.js.map +1 -0
  29. package/dist/skills/index.d.ts +15 -0
  30. package/dist/skills/index.js +6 -0
  31. package/dist/skills/index.js.map +1 -0
  32. package/dist/skills/manifest.d.ts +30 -0
  33. package/dist/skills/manifest.js +110 -0
  34. package/dist/skills/manifest.js.map +1 -0
  35. package/dist/skills/role-skill-source.d.ts +29 -0
  36. package/dist/skills/role-skill-source.js +20 -0
  37. package/dist/skills/role-skill-source.js.map +1 -0
  38. package/dist/skills/set-diff-gate.d.ts +20 -0
  39. package/dist/skills/set-diff-gate.js +24 -0
  40. package/dist/skills/set-diff-gate.js.map +1 -0
  41. package/dist/tool-manager/dispatch/dispatch.d.ts +26 -0
  42. package/dist/tool-manager/dispatch/dispatch.js +42 -1
  43. package/dist/tool-manager/dispatch/dispatch.js.map +1 -1
  44. package/dist/tool-manager/orchestrator/dispatcher.d.ts +10 -0
  45. package/dist/tool-manager/orchestrator/dispatcher.js +5 -1
  46. package/dist/tool-manager/orchestrator/dispatcher.js.map +1 -1
  47. package/package.json +4 -1
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ import { type WantedBundles } from "../skills/manifest.js";
3
+ export interface SeedSkillsDeps {
4
+ /** the seat's skills root ($CLAUDE_CONFIG_DIR/skills) — created cold if absent. */
5
+ skillsDir: string;
6
+ /** the durable managed-ledger sidecar path (kept OUTSIDE skillsDir). */
7
+ ledgerPath: string;
8
+ /** the role whose baseline to land (for the set-diff gate + per-role sets). */
9
+ role: string;
10
+ /** the parsed wanted-bundles manifest. */
11
+ manifest: WantedBundles;
12
+ /** the root of the pinned-source CLONE (contains skills/<id>/ + bundles/<b>.yaml). */
13
+ sourceRoot: string;
14
+ /** optional static role→skills OVERRIDES (the future per-role config surface). Slice-1
15
+ * passes none → role-agnostic (role_map = the manifest expansion = mks-delivered). A
16
+ * role override that DROPS an mks skill trips the live set-diff gate below. */
17
+ roleSkills?: ReadonlyMap<string, readonly string[]>;
18
+ log?: (msg: string) => void;
19
+ }
20
+ export interface SeedSkillsResult {
21
+ ok: boolean;
22
+ reason: string;
23
+ /** the mks-delivered skills missing from role_map (non-empty ⇒ gate FAILED). */
24
+ missing: string[];
25
+ landed: string[];
26
+ }
27
+ /**
28
+ * The orchestration core (injected deps; no env/git). Runs: mechanical bundle-expand →
29
+ * LIVE fail-closed set-diff gate (role_map ⊇ mks-delivered) → converge via the neutral
30
+ * loop + ClaudeSkillActuator. Returns ok=false on gate violation OR non-converged
31
+ * (the caller fail-closes the launch).
32
+ */
33
+ export declare function runSeedSkills(deps: SeedSkillsDeps): SeedSkillsResult;
34
+ /**
35
+ * True iff this module is the process entrypoint — robust to SYMLINK invocation
36
+ * (bug-251). The npm bin `ois-seed-skills` is a symlink, so `process.argv[1]` is the
37
+ * symlink path while `import.meta.url` is the real file; a raw `===` mismatches and
38
+ * `main()` silently no-ops (the fleet rollout invokes via that symlink → skill delivery
39
+ * would silently vanish). realpath-normalize BOTH sides so they compare canonical paths.
40
+ * Guarded for a missing/unresolvable argv[1] (e.g. under a test importer).
41
+ */
42
+ export declare function isInvokedAsMain(moduleUrl: string, argv1: string | undefined): boolean;
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * bin/seed-skills.ts — the SEED-TIME headless FAIL-CLOSED skills entrypoint
4
+ * (hcapskills0 build_claude, design §5). A network-adapter bin the ois claude_seed
5
+ * invokes in the `mission_kit_sync` slot: it materializes a role's declared skill
6
+ * baseline into $CLAUDE_CONFIG_DIR/skills BEFORE `exec claude`, headless (no claude
7
+ * runtime), and ABORTS the launch loudly (non-zero) on any converge failure — NEVER
8
+ * `exec claude` with a partial baseline (matching mission_kit_sync's fail-closed cp).
9
+ *
10
+ * THE INTEGRATION POINT: ALL env/ois-layout/git coupling lives HERE (guardrail 2), so
11
+ * the shared skills module (actuator + source) stays fs/env/git-free — the bin reads
12
+ * env, parses the ois manifest, clones the pinned source, and INJECTS concrete paths.
13
+ * `runSeedSkills` is the injected-deps orchestration core (testable without git/env);
14
+ * `main` is the env/git wrapper.
15
+ *
16
+ * COEXIST (design §9): this runs ALONGSIDE the legacy mission_kit_sync (behind the
17
+ * launch flag in ois), never retiring it. The LIVE set-diff gate (below) is what keeps
18
+ * coexist from clobbering an mks skill at runtime under manifest drift.
19
+ */
20
+ import { execFileSync } from "node:child_process";
21
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, } from "node:fs";
22
+ import { tmpdir } from "node:os";
23
+ import { fileURLToPath } from "node:url";
24
+ import { join } from "node:path";
25
+ import { SpecStore } from "../control-plane/spec-store.js";
26
+ import { ReconcileLoop } from "../control-plane/reconcile-loop.js";
27
+ import { ClaudeSkillActuator } from "../skills/claude-skill-actuator.js";
28
+ import { FileSkillLedger } from "../skills/file-skill-ledger.js";
29
+ import { RoleSkillSource } from "../skills/role-skill-source.js";
30
+ import { parseWantedBundles, parseBundleSkills, expandWantedBundles, } from "../skills/manifest.js";
31
+ import { missingFromRoleMap } from "../skills/set-diff-gate.js";
32
+ /**
33
+ * The orchestration core (injected deps; no env/git). Runs: mechanical bundle-expand →
34
+ * LIVE fail-closed set-diff gate (role_map ⊇ mks-delivered) → converge via the neutral
35
+ * loop + ClaudeSkillActuator. Returns ok=false on gate violation OR non-converged
36
+ * (the caller fail-closes the launch).
37
+ */
38
+ export function runSeedSkills(deps) {
39
+ const log = deps.log ?? (() => { });
40
+ const readBundleSkills = (bundle) => {
41
+ const bf = join(deps.sourceRoot, "bundles", `${bundle}.yaml`);
42
+ if (!existsSync(bf)) {
43
+ throw new Error(`bundle '${bundle}' not found (bundles/${bundle}.yaml)`);
44
+ }
45
+ return parseBundleSkills(readFileSync(bf, "utf8"));
46
+ };
47
+ // MECHANICAL expansion — the same derivation mission_kit_sync uses, so mks-delivered
48
+ // and role_map both descend from this manifest (design §5).
49
+ const expanded = expandWantedBundles(deps.manifest, readBundleSkills);
50
+ const source = new RoleSkillSource({
51
+ defaultSkills: expanded, // slice-1: role-agnostic (mirrors mks); per-role generalizes
52
+ roleSkills: deps.roleSkills,
53
+ sourceDirFor: (id) => join(deps.sourceRoot, "skills", id),
54
+ });
55
+ const roleSkills = source.skillIdsForRole(deps.role);
56
+ const mksDelivered = expanded; // what legacy mission_kit_sync would copy for this seat
57
+ // ── LIVE, FAIL-CLOSED set-diff gate (invariant 3b; design §8.2). role_map MUST be a
58
+ // superset of mks-delivered, or the actuator's ledger-scoped removal could unlink
59
+ // an mks skill dropped from role_map. Enforced at RUNTIME (not just CI) so it holds
60
+ // under manifest drift — abort BEFORE any converge/unlink.
61
+ const missing = missingFromRoleMap(roleSkills, mksDelivered);
62
+ if (missing.length > 0) {
63
+ return {
64
+ ok: false,
65
+ reason: `set-diff gate VIOLATED for role '${deps.role}': role_map is missing mission_kit_sync-delivered skills [${missing.join(", ")}] — aborting to protect the coexisting legacy baseline`,
66
+ missing,
67
+ landed: [],
68
+ };
69
+ }
70
+ // Cold-seat root + empty ledger are created here (the watcher precondition: the skills
71
+ // root pre-exists once cp is gone).
72
+ mkdirSync(deps.skillsDir, { recursive: true });
73
+ const ledger = new FileSkillLedger(deps.ledgerPath);
74
+ const actuator = new ClaudeSkillActuator({ skillsDir: deps.skillsDir, ledger, log });
75
+ const store = new SpecStore();
76
+ const loop = new ReconcileLoop({ store, actuator }, { log });
77
+ store.apply(source.specsForRole(deps.role));
78
+ const outcome = loop.sync("seed-skills");
79
+ if (!outcome.converged) {
80
+ return {
81
+ ok: false,
82
+ reason: `skills converge did NOT reach converged (klass=${outcome.klass ?? "?"}${outcome.detail ? `: ${outcome.detail}` : ""}) — refusing to launch with a partial baseline`,
83
+ missing: [],
84
+ landed: [...actuator.observeManaged().observedManaged],
85
+ };
86
+ }
87
+ return {
88
+ ok: true,
89
+ reason: `seeded ${roleSkills.length} skill(s) for role '${deps.role}'`,
90
+ missing: [],
91
+ landed: [...actuator.observeManaged().observedManaged],
92
+ };
93
+ }
94
+ /** env/git wrapper: read env, parse the manifest, clone the pinned source, delegate. */
95
+ function main(argv) {
96
+ const log = (m) => console.error(m);
97
+ const skillsDir = argv[2] ??
98
+ (process.env.CLAUDE_CONFIG_DIR
99
+ ? join(process.env.CLAUDE_CONFIG_DIR, "skills")
100
+ : undefined);
101
+ const manifestPath = process.env.HCAP_SKILLS_MANIFEST;
102
+ const role = process.env.OIS_ROLE ?? process.env.OIS_AGENT_ROLE ?? "unknown";
103
+ const ledgerPath = process.env.HCAP_SKILLS_LEDGER ??
104
+ (process.env.CLAUDE_CONFIG_DIR
105
+ ? join(process.env.CLAUDE_CONFIG_DIR, ".hcap-skills-managed.json")
106
+ : undefined);
107
+ if (!skillsDir) {
108
+ log("[hcap-skills] FATAL: no skills dir (arg1 or CLAUDE_CONFIG_DIR)");
109
+ return 2;
110
+ }
111
+ if (!manifestPath || !existsSync(manifestPath)) {
112
+ log(`[hcap-skills] FATAL: manifest not found (HCAP_SKILLS_MANIFEST=${manifestPath})`);
113
+ return 2;
114
+ }
115
+ if (!ledgerPath) {
116
+ log("[hcap-skills] FATAL: no ledger path (HCAP_SKILLS_LEDGER or CLAUDE_CONFIG_DIR)");
117
+ return 2;
118
+ }
119
+ const manifest = parseWantedBundles(readFileSync(manifestPath, "utf8"));
120
+ const repo = process.env.SKILL_SYNC_REPO ?? manifest.sourceRepo;
121
+ const ref = process.env.SKILL_SYNC_REF ?? manifest.sourceRef;
122
+ if (!repo || !ref) {
123
+ log("[hcap-skills] FATAL: manifest missing source_repo / source_ref (pin required)");
124
+ return 2;
125
+ }
126
+ let clone;
127
+ try {
128
+ clone = mkdtempSync(join(tmpdir(), "hcap-skills-"));
129
+ execFileSync("git", ["clone", "--quiet", repo, clone], { stdio: ["ignore", "ignore", "inherit"] });
130
+ execFileSync("git", ["-C", clone, "checkout", "--quiet", ref], { stdio: ["ignore", "ignore", "inherit"] });
131
+ const result = runSeedSkills({
132
+ skillsDir,
133
+ ledgerPath,
134
+ role,
135
+ manifest,
136
+ sourceRoot: clone,
137
+ log,
138
+ });
139
+ if (!result.ok) {
140
+ log(`[hcap-skills] ABORT (fail-closed): ${result.reason}`);
141
+ return 1;
142
+ }
143
+ log(`[hcap-skills] OK: ${result.reason} from ${repo}@${ref} → ${skillsDir}`);
144
+ return 0;
145
+ }
146
+ catch (err) {
147
+ log(`[hcap-skills] ABORT (fail-closed): ${err?.message ?? String(err)}`);
148
+ return 1;
149
+ }
150
+ finally {
151
+ if (clone)
152
+ rmSync(clone, { recursive: true, force: true });
153
+ }
154
+ }
155
+ /**
156
+ * True iff this module is the process entrypoint — robust to SYMLINK invocation
157
+ * (bug-251). The npm bin `ois-seed-skills` is a symlink, so `process.argv[1]` is the
158
+ * symlink path while `import.meta.url` is the real file; a raw `===` mismatches and
159
+ * `main()` silently no-ops (the fleet rollout invokes via that symlink → skill delivery
160
+ * would silently vanish). realpath-normalize BOTH sides so they compare canonical paths.
161
+ * Guarded for a missing/unresolvable argv[1] (e.g. under a test importer).
162
+ */
163
+ export function isInvokedAsMain(moduleUrl, argv1) {
164
+ if (!argv1)
165
+ return false;
166
+ try {
167
+ return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(argv1);
168
+ }
169
+ catch {
170
+ return false;
171
+ }
172
+ }
173
+ // Run only when invoked as a script (not when imported by a test) — symlink-safe.
174
+ if (isInvokedAsMain(import.meta.url, process.argv[1])) {
175
+ process.exit(main(process.argv));
176
+ }
177
+ //# sourceMappingURL=seed-skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed-skills.js","sourceRoot":"","sources":["../../src/bin/seed-skills.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  {
2
- "commitSha": "7f2e93c",
3
- "dirty": true,
4
- "buildTime": "2026-07-11T08:02:28.261Z",
2
+ "commitSha": "b24120e",
3
+ "dirty": false,
4
+ "buildTime": "2026-07-13T13:44:02.663Z",
5
5
  "branch": "HEAD"
6
6
  }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * control-plane/contracts.ts — the harness-neutral, RESOURCE-GENERIC HCAP consumer
3
+ * core (hcapskills0 build_core; design docs/design/hcapskills-design.md v2).
4
+ *
5
+ * The generalize+invert of the mission-107 PI tool-control-plane: one neutral Controller
6
+ * (SpecStore → DiffEngine → ReconcileLoop) drives ANY resource kind (tools on MCP,
7
+ * skills on filesystem, …) through a `ResourceActuatorPort` that decides HOW to
8
+ * converge. Two inversions vs the pi original (grounding §3, design §2/§3):
9
+ *
10
+ * 1. The port is `converge(desired) → tri-state` + `observeManaged()`, NOT
11
+ * register/setActive/snapshot. The built-in preserve-baseline union and the
12
+ * poison-guard are pi-ACTUATOR internals now — they never appear here (a
13
+ * root-scoped filesystem actuator has no built-ins to preserve).
14
+ * 2. Every observed/desired comparison is MANAGED-SCOPED (`observed ∩ managedNames`
15
+ * vs desired). The neutral Controller never sees the raw running surface, so a host's
16
+ * ever-present built-ins can never read as drift.
17
+ *
18
+ * `definition` is OPAQUE (actuator-interpreted): a ToolDescriptor for pi-tools, a
19
+ * skill/corpus identifier for claude-skills — NEVER molded to a tool shape.
20
+ */
21
+ /** A declared resource — resource-generic. `definition` is opaque to the core. */
22
+ export interface ResourceSpec {
23
+ name: string;
24
+ /** actuator-interpreted payload (registration/materialization input). Opaque here. */
25
+ definition: unknown;
26
+ /** enabled ⇒ member of the desired ACTIVE managed subset; false = declared-inactive. */
27
+ enabled: boolean;
28
+ }
29
+ /**
30
+ * Tri-state convergence outcome from the actuator. The ACTUATOR decides what
31
+ * "not-yet-observable" means for ITS substrate (pi: setActive lands the next agent
32
+ * turn; claude: a filesystem watcher hasn't yet picked up the write) and reports
33
+ * `pending`; the neutral loop counts-but-tolerates it and must NEVER read it as
34
+ * `converged` OR `failed`. The loop counts converge PASSES, never a wall-clock
35
+ * "turn" — the vocabulary here is substrate-agnostic on purpose.
36
+ */
37
+ export type ConvergeStatus = "converged" | "pending" | "failed";
38
+ /** Failure taxonomy for a converge pass (loud escalation after the bound). */
39
+ export type ConvergeFailureClass = "actuate-failed" | "incoherent-plan" | "observe-failed" | "still-diverged";
40
+ /** The actuator's report for one converge pass. */
41
+ export interface ConvergeResult {
42
+ status: ConvergeStatus;
43
+ /** the managed names the actuator drove toward ACTIVE (the loop confirms against this). */
44
+ desiredManaged: string[];
45
+ klass?: ConvergeFailureClass;
46
+ detail?: string;
47
+ }
48
+ /** The MANAGED-scoped observation (`observed ∩ managedNames`) — never the raw surface. */
49
+ export interface ManagedObservation {
50
+ /** names currently observed active WITHIN this plane's managed set. */
51
+ observedManaged: string[];
52
+ /** every name this plane has ever driven (the managed ledger; for status joins). */
53
+ managedNames: string[];
54
+ }
55
+ /**
56
+ * THE harness-neutral actuation seam. Each actuator decides HOW to converge its
57
+ * managed surface toward `desired` (pi: register defs → setActive(builtins ∪
58
+ * enabled) → poison-guard; claude: write/unlink SKILL.md trees). Exposes NO
59
+ * register/setActive/remove primitives — those are actuator-internal.
60
+ */
61
+ export interface ResourceActuatorPort {
62
+ /** converge the managed surface toward `desired`; returns tri-state (never throws
63
+ * for an actuation fault — reports `failed` so the loop can escalate/retry). */
64
+ converge(desired: readonly ResourceSpec[]): ConvergeResult;
65
+ /** observe the MANAGED subset fresh (F1 — no in-memory latch). */
66
+ observeManaged(): ManagedObservation;
67
+ }
68
+ /** U1 custody of the declared spec (neutral). */
69
+ export interface ResourceSpecStorePort {
70
+ list(): readonly ResourceSpec[];
71
+ get(name: string): ResourceSpec | undefined;
72
+ apply(spec: readonly ResourceSpec[]): void;
73
+ create(spec: ResourceSpec): void;
74
+ destroy(name: string): void;
75
+ }
76
+ /** KF4 — the persistence seam (Slice-1 in-memory no-op; disk impl deferred). */
77
+ export interface SpecPersistencePort {
78
+ load(): ResourceSpec[] | null;
79
+ save(spec: readonly ResourceSpec[]): void;
80
+ }
81
+ /** The outcome of one level-triggered reconcile pass (loop → onOutcome). */
82
+ export interface ConvergeOutcome {
83
+ reason: string;
84
+ converged: boolean;
85
+ /** true only for the tolerated interim (`pending`) — actuation accepted, effect not yet observed; not converged, not escalating yet. */
86
+ pending?: boolean;
87
+ klass?: ConvergeFailureClass;
88
+ detail?: string;
89
+ /** consecutive non-converged passes including this one (0 on convergence). */
90
+ consecutiveFailures: number;
91
+ }
92
+ /** Per-resource status, derived from U1 records + the managed observation (never host introspection). */
93
+ export interface RunningResourceStatus {
94
+ name: string;
95
+ declared: boolean;
96
+ enabled: boolean;
97
+ active: boolean;
98
+ managed: boolean;
99
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * control-plane/contracts.ts — the harness-neutral, RESOURCE-GENERIC HCAP consumer
3
+ * core (hcapskills0 build_core; design docs/design/hcapskills-design.md v2).
4
+ *
5
+ * The generalize+invert of the mission-107 PI tool-control-plane: one neutral Controller
6
+ * (SpecStore → DiffEngine → ReconcileLoop) drives ANY resource kind (tools on MCP,
7
+ * skills on filesystem, …) through a `ResourceActuatorPort` that decides HOW to
8
+ * converge. Two inversions vs the pi original (grounding §3, design §2/§3):
9
+ *
10
+ * 1. The port is `converge(desired) → tri-state` + `observeManaged()`, NOT
11
+ * register/setActive/snapshot. The built-in preserve-baseline union and the
12
+ * poison-guard are pi-ACTUATOR internals now — they never appear here (a
13
+ * root-scoped filesystem actuator has no built-ins to preserve).
14
+ * 2. Every observed/desired comparison is MANAGED-SCOPED (`observed ∩ managedNames`
15
+ * vs desired). The neutral Controller never sees the raw running surface, so a host's
16
+ * ever-present built-ins can never read as drift.
17
+ *
18
+ * `definition` is OPAQUE (actuator-interpreted): a ToolDescriptor for pi-tools, a
19
+ * skill/corpus identifier for claude-skills — NEVER molded to a tool shape.
20
+ */
21
+ export {};
22
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../src/control-plane/contracts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * control-plane/ — the harness-neutral, resource-generic HCAP consumer Controller
3
+ * (hcapskills0 build_core; design docs/design/hcapskills-design.md v2). Generalized
4
+ * + inverted from the mission-107 PI tool-control-plane: one core (SpecStore →
5
+ * ReconcileLoop) drives any resource kind through a `ResourceActuatorPort` that
6
+ * decides HOW to converge. Consumed by pi-plugin (tools) and claude-plugin (skills).
7
+ * Naming (ratified, K8s-lineage): the neutral core IS the "Controller" — it reconciles
8
+ * declared specs onto a resource kind, living in this control-plane/. Classes keep their
9
+ * names (SpecStore, ReconcileLoop); the actuators keep "Actuator".
10
+ */
11
+ export type { ResourceSpec, ConvergeStatus, ConvergeFailureClass, ConvergeResult, ManagedObservation, ResourceActuatorPort, ResourceSpecStorePort, SpecPersistencePort, ConvergeOutcome, RunningResourceStatus, } from "./contracts.js";
12
+ export { SpecStore, InMemorySpecPersistence } from "./spec-store.js";
13
+ export { ReconcileLoop } from "./reconcile-loop.js";
14
+ export type { ReconcileCollaborators, ReconcileLoopOptions, } from "./reconcile-loop.js";
@@ -0,0 +1,3 @@
1
+ export { SpecStore, InMemorySpecPersistence } from "./spec-store.js";
2
+ export { ReconcileLoop } from "./reconcile-loop.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/control-plane/index.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * control-plane/reconcile-loop.ts — U4 ReconcileLoop (resource-generic; hcapskills0
3
+ * build_core). Single concern: the level-triggered converge pass. Neutral — closes
4
+ * over the SpecStore + ResourceActuatorPort contracts, ZERO host types.
5
+ *
6
+ * LEVEL = declared spec (U1) vs the MANAGED observed surface (observed ∩ managedNames
7
+ * — never the raw surface, so a host's ever-present built-ins can never read as
8
+ * drift; design v2 §2 blocker fix). The actuator decides HOW to converge and reports
9
+ * a tri-state; the loop owns the escalation classification.
10
+ *
11
+ * Escalation semantics (design v2 §2; ratified ruling): the failure counter is a
12
+ * POISON/termination guard for "genuinely won't converge", NOT a divergence tally.
13
+ * The loop counts converge PASSES, never a wall-clock "turn" — the ACTUATOR reports
14
+ * `pending` (its actuation is accepted but not yet observable; what that means is the
15
+ * actuator's substrate concern: pi setActive lands next agent turn, claude a watcher
16
+ * hasn't yet picked up the write), and the loop TOLERATES it — it does NOT count.
17
+ * Only a CROSS-PASS stall — the actuator reports `pending` again for the SAME managed
18
+ * set on a LATER pass (it never became observable) — counts + escalates at the bound.
19
+ * `pendingSince` tracks the last-actuated-still-pending managed set to make exactly
20
+ * that within-pass-vs-cross-pass distinction. A hard actuation fault (status:"failed",
21
+ * e.g. a registration reject) is a DISTINCT input that counts immediately.
22
+ */
23
+ import type { ConvergeOutcome, ResourceActuatorPort, ResourceSpec } from "./contracts.js";
24
+ export interface ReconcileCollaborators {
25
+ store: {
26
+ list(): readonly ResourceSpec[];
27
+ };
28
+ actuator: Pick<ResourceActuatorPort, "converge">;
29
+ }
30
+ export interface ReconcileLoopOptions {
31
+ onOutcome?: (o: ConvergeOutcome) => void;
32
+ failureBound?: number;
33
+ log?: (msg: string) => void;
34
+ }
35
+ export declare class ReconcileLoop {
36
+ private readonly deps;
37
+ private readonly opts;
38
+ private consecutiveFailures;
39
+ /** the managed desired-set actuated in a prior pass that had NOT converged; a later
40
+ * pass still-pending on the SAME set = a cross-pass stall (counts); a first
41
+ * divergence = a within-pass pending (tolerated, not counted). */
42
+ private pendingSince;
43
+ private readonly failureBound;
44
+ private readonly log;
45
+ constructor(deps: ReconcileCollaborators, opts?: ReconcileLoopOptions);
46
+ getConsecutiveFailures(): number;
47
+ sync(reason: string): ConvergeOutcome;
48
+ private fail;
49
+ private emit;
50
+ }
@@ -0,0 +1,83 @@
1
+ export class ReconcileLoop {
2
+ deps;
3
+ opts;
4
+ consecutiveFailures = 0;
5
+ /** the managed desired-set actuated in a prior pass that had NOT converged; a later
6
+ * pass still-pending on the SAME set = a cross-pass stall (counts); a first
7
+ * divergence = a within-pass pending (tolerated, not counted). */
8
+ pendingSince = null;
9
+ failureBound;
10
+ log;
11
+ constructor(deps, opts = {}) {
12
+ this.deps = deps;
13
+ this.opts = opts;
14
+ this.failureBound = opts.failureBound ?? 3;
15
+ this.log = opts.log ?? (() => { });
16
+ }
17
+ getConsecutiveFailures() {
18
+ return this.consecutiveFailures;
19
+ }
20
+ sync(reason) {
21
+ const desired = this.deps.store.list();
22
+ // The actuator actuates + observes its own managed surface (a fresh read — F1) and
23
+ // reports a tri-state; it NEVER throws for an actuation fault (returns "failed").
24
+ const result = this.deps.actuator.converge(desired);
25
+ if (result.status === "failed") {
26
+ // a genuine actuation fault (registration reject, setActive throw) — counts NOW.
27
+ this.pendingSince = null;
28
+ return this.fail(reason, result.klass ?? "actuate-failed", result.detail);
29
+ }
30
+ if (result.status === "converged") {
31
+ this.consecutiveFailures = 0;
32
+ this.pendingSince = null;
33
+ return this.emit({ reason, converged: true, consecutiveFailures: 0 });
34
+ }
35
+ // status === "pending": actuated, managed surface not yet reflecting it.
36
+ const crossPass = this.pendingSince !== null &&
37
+ sameSet(this.pendingSince, result.desiredManaged);
38
+ this.pendingSince = [...result.desiredManaged];
39
+ if (crossPass) {
40
+ // same managed set reported pending again on a LATER pass, still not observable
41
+ // = genuinely not converging → count + escalate at the bound (termination guard).
42
+ return this.fail(reason, "still-diverged", "managed set still pending across a converge pass");
43
+ }
44
+ // within-pass deferral — TOLERATED, NOT counted (design v2 §2; lily ruling (a)).
45
+ this.log(`[hcap-reconcile] ${reason}: pending (managed actuation accepted, effect not yet observed) — not counted`);
46
+ return this.emit({
47
+ reason,
48
+ converged: false,
49
+ pending: true,
50
+ klass: "still-diverged",
51
+ consecutiveFailures: this.consecutiveFailures,
52
+ });
53
+ }
54
+ fail(reason, klass, detail) {
55
+ this.consecutiveFailures += 1;
56
+ this.log(`[hcap-reconcile] ${reason}: NOT converged (${klass}${detail ? `: ${detail}` : ""}) — consecutive=${this.consecutiveFailures}`);
57
+ if (this.consecutiveFailures >= this.failureBound) {
58
+ this.log(`[hcap-reconcile] ${reason}: ESCALATION — ${this.consecutiveFailures} consecutive cross-pass/failed converges (>= ${this.failureBound}, ${klass}); surface cannot converge, operator/architect intervention required`);
59
+ }
60
+ return this.emit({
61
+ reason,
62
+ converged: false,
63
+ klass,
64
+ detail,
65
+ consecutiveFailures: this.consecutiveFailures,
66
+ });
67
+ }
68
+ emit(o) {
69
+ this.opts.onOutcome?.(o);
70
+ return o;
71
+ }
72
+ }
73
+ /** Order-independent set equality over string name lists. */
74
+ function sameSet(a, b) {
75
+ if (a.length !== b.length)
76
+ return false;
77
+ const sa = new Set(a);
78
+ for (const x of b)
79
+ if (!sa.has(x))
80
+ return false;
81
+ return true;
82
+ }
83
+ //# sourceMappingURL=reconcile-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile-loop.js","sourceRoot":"","sources":["../../src/control-plane/reconcile-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * control-plane/spec-store.ts — U1 SpecStore (resource-generic; hcapskills0
3
+ * build_core). Custody of the declared `ResourceSpec[]`: the SOLE writer + the SOLE
4
+ * authority for enabled-vs-removed truth (a host's introspection cannot distinguish
5
+ * declared-inactive from removed). Neutral — imports zero host types. A verbatim
6
+ * generalization (tool→resource) of the mission-107 pi SpecStore.
7
+ *
8
+ * KF4 — owns the persistence SEAM. Slice-1 uses the in-memory no-op; the disk impl
9
+ * is deferred (Earned-Exposure). The cold-start rehydrate lives here so the held
10
+ * spec surviving independent of any controller has a home in U1.
11
+ */
12
+ import type { ResourceSpec, ResourceSpecStorePort, SpecPersistencePort } from "./contracts.js";
13
+ /** The Slice-1 in-memory no-op impl behind the KF4 persistence seam. */
14
+ export declare class InMemorySpecPersistence implements SpecPersistencePort {
15
+ load(): ResourceSpec[] | null;
16
+ save(_spec: readonly ResourceSpec[]): void;
17
+ }
18
+ export declare class SpecStore implements ResourceSpecStorePort {
19
+ private readonly persistence;
20
+ /** the declared spec, keyed by name; the array view preserves insertion order. */
21
+ private byName;
22
+ constructor(persistence?: SpecPersistencePort);
23
+ list(): readonly ResourceSpec[];
24
+ get(name: string): ResourceSpec | undefined;
25
+ /** AUTHORITATIVE REPLACE of the whole declared set — absent names ⇒ removed. */
26
+ apply(spec: readonly ResourceSpec[]): void;
27
+ create(spec: ResourceSpec): void;
28
+ destroy(name: string): void;
29
+ }
@@ -0,0 +1,41 @@
1
+ /** The Slice-1 in-memory no-op impl behind the KF4 persistence seam. */
2
+ export class InMemorySpecPersistence {
3
+ load() {
4
+ return null;
5
+ }
6
+ save(_spec) {
7
+ /* no-op — the disk-backed impl is a deferred Earned-Exposure item. */
8
+ }
9
+ }
10
+ export class SpecStore {
11
+ persistence;
12
+ /** the declared spec, keyed by name; the array view preserves insertion order. */
13
+ byName = new Map();
14
+ constructor(persistence = new InMemorySpecPersistence()) {
15
+ this.persistence = persistence;
16
+ const loaded = this.persistence.load();
17
+ if (loaded)
18
+ for (const s of loaded)
19
+ this.byName.set(s.name, s);
20
+ }
21
+ list() {
22
+ return [...this.byName.values()];
23
+ }
24
+ get(name) {
25
+ return this.byName.get(name);
26
+ }
27
+ /** AUTHORITATIVE REPLACE of the whole declared set — absent names ⇒ removed. */
28
+ apply(spec) {
29
+ this.byName = new Map(spec.map((s) => [s.name, s]));
30
+ this.persistence.save(this.list());
31
+ }
32
+ create(spec) {
33
+ this.byName.set(spec.name, spec);
34
+ this.persistence.save(this.list());
35
+ }
36
+ destroy(name) {
37
+ this.byName.delete(name);
38
+ this.persistence.save(this.list());
39
+ }
40
+ }
41
+ //# sourceMappingURL=spec-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-store.js","sourceRoot":"","sources":["../../src/control-plane/spec-store.ts"],"names":[],"mappings":"AAiBA,wEAAwE;AACxE,MAAM,OAAO,uBAAuB;IAClC,IAAI;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,KAA8B;QACjC,sEAAsE;IACxE,CAAC;CACF;AAED,MAAM,OAAO,SAAS;IAKD;IAJnB,kFAAkF;IAC1E,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;IAEjD,YACmB,cAAmC,IAAI,uBAAuB,EAAE;QAAhE,gBAAW,GAAX,WAAW,CAAqD;QAEjF,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QACvC,IAAI,MAAM;YAAE,KAAK,MAAM,CAAC,IAAI,MAAM;gBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,gFAAgF;IAChF,KAAK,CAAC,IAA6B;QACjC,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,CAAC,IAAkB;QACvB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACzB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;CACF"}
package/dist/index.d.ts CHANGED
@@ -30,8 +30,8 @@ export type { LivenessLostSignalPayload } from "./kernel/liveness-signal.js";
30
30
  export { parseHarnessManifest, loadHarnessManifest, serverCapabilitiesFromManifest, } from "./kernel/harness-manifest.js";
31
31
  export type { HarnessManifest, HarnessCapability } from "./kernel/harness-manifest.js";
32
32
  export type { ToolDescriptor, ToolDispatchCallOptions, ToolDispatchResult, IToolDispatchAgent, IToolManager, } from "./tool-manager/contracts.js";
33
- export { runToolDispatch } from "./tool-manager/dispatch/dispatch.js";
34
- export type { ToolDispatchContext, McpToolCallResult, } from "./tool-manager/dispatch/dispatch.js";
33
+ export { runToolDispatch, DEFAULT_TRANSIENT_DROP_RETRY, } from "./tool-manager/dispatch/dispatch.js";
34
+ export type { ToolDispatchContext, McpToolCallResult, TransientDropRetryConfig, } from "./tool-manager/dispatch/dispatch.js";
35
35
  export { createSharedDispatcher, pendingKey, injectQueueItemId, assertHostWiringComplete, } from "./tool-manager/orchestrator/dispatcher.js";
36
36
  export type { DispatcherClientInfo, DispatcherNotificationHooks, SharedDispatcherOptions, SharedDispatcher, } from "./tool-manager/orchestrator/dispatcher.js";
37
37
  export { CATALOG_SCHEMA_VERSION, cachePathFor, readCache, writeCache, isCacheValid, } from "./tool-manager/catalog/tool-catalog-cache.js";
@@ -52,3 +52,4 @@ export { appendNotification, buildPendingTaskNotification } from "./notification
52
52
  export type { NotificationLogEntry, NotificationLogOptions, } from "./notification-log.js";
53
53
  export { CognitivePipeline, CognitiveTelemetry, CircuitBreaker, HubUnavailableError, WriteCallDedup, DedupTimeoutError, ToolResultCache, FlushAllOnWriteStrategy, ToolDescriptionEnricher, ErrorNormalizer, NormalizedError, ResponseSummarizer, summarizeResult, buildPaginationHint, } from "@apnex/cognitive-layer";
54
54
  export type { CognitiveMiddleware, ToolCallContext, ListToolsContext, ToolErrorContext, Tool as CognitiveTool, StandardPipelineConfig, CognitiveTelemetryConfig, TelemetryEvent, TelemetryEventKind, CircuitBreakerConfig, CircuitState, CircuitStateChange, WriteCallDedupConfig, ToolResultCacheConfig, InvalidationStrategy, InvalidationDirective, CacheKey, ToolDescriptionEnricherConfig, ToolHints, ErrorNormalizerConfig, ErrorRule, CascadeDriftRule, ResponseSummarizerConfig, } from "@apnex/cognitive-layer";
55
+ export * from "./control-plane/index.js";