@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.
- package/dist/bin/seed-skills.d.ts +42 -0
- package/dist/bin/seed-skills.js +177 -0
- package/dist/bin/seed-skills.js.map +1 -0
- package/dist/build-info.json +3 -3
- package/dist/control-plane/contracts.d.ts +99 -0
- package/dist/control-plane/contracts.js +22 -0
- package/dist/control-plane/contracts.js.map +1 -0
- package/dist/control-plane/index.d.ts +14 -0
- package/dist/control-plane/index.js +3 -0
- package/dist/control-plane/index.js.map +1 -0
- package/dist/control-plane/reconcile-loop.d.ts +50 -0
- package/dist/control-plane/reconcile-loop.js +83 -0
- package/dist/control-plane/reconcile-loop.js.map +1 -0
- package/dist/control-plane/spec-store.d.ts +29 -0
- package/dist/control-plane/spec-store.js +41 -0
- package/dist/control-plane/spec-store.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/skills/claude-skill-actuator.d.ts +28 -0
- package/dist/skills/claude-skill-actuator.js +147 -0
- package/dist/skills/claude-skill-actuator.js.map +1 -0
- package/dist/skills/contracts.d.ts +33 -0
- package/dist/skills/contracts.js +15 -0
- package/dist/skills/contracts.js.map +1 -0
- package/dist/skills/file-skill-ledger.d.ts +7 -0
- package/dist/skills/file-skill-ledger.js +38 -0
- package/dist/skills/file-skill-ledger.js.map +1 -0
- package/dist/skills/index.d.ts +15 -0
- package/dist/skills/index.js +6 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/manifest.d.ts +30 -0
- package/dist/skills/manifest.js +110 -0
- package/dist/skills/manifest.js.map +1 -0
- package/dist/skills/role-skill-source.d.ts +29 -0
- package/dist/skills/role-skill-source.js +20 -0
- package/dist/skills/role-skill-source.js.map +1 -0
- package/dist/skills/set-diff-gate.d.ts +20 -0
- package/dist/skills/set-diff-gate.js +24 -0
- package/dist/skills/set-diff-gate.js.map +1 -0
- package/dist/tool-manager/dispatch/dispatch.d.ts +26 -0
- package/dist/tool-manager/dispatch/dispatch.js +42 -1
- package/dist/tool-manager/dispatch/dispatch.js.map +1 -1
- package/dist/tool-manager/orchestrator/dispatcher.d.ts +10 -0
- package/dist/tool-manager/orchestrator/dispatcher.js +5 -1
- package/dist/tool-manager/orchestrator/dispatcher.js.map +1 -1
- 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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"}
|
package/dist/build-info.json
CHANGED
|
@@ -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 @@
|
|
|
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";
|