@tacuchi/agent-workflow-cli 21.0.0 → 21.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/application/capability/compose.js +161 -0
- package/dist/application/capability/compose.js.map +1 -0
- package/dist/application/capability/descriptor-loader.js +109 -0
- package/dist/application/capability/descriptor-loader.js.map +1 -0
- package/dist/application/capability/design-handler.js +299 -0
- package/dist/application/capability/design-handler.js.map +1 -0
- package/dist/application/capability/dispatcher.js +312 -0
- package/dist/application/capability/dispatcher.js.map +1 -0
- package/dist/application/capability/durable-effect.js +220 -0
- package/dist/application/capability/durable-effect.js.map +1 -0
- package/dist/application/capability/installed-inventory.js +168 -0
- package/dist/application/capability/installed-inventory.js.map +1 -0
- package/dist/application/capability/readiness.js +204 -0
- package/dist/application/capability/readiness.js.map +1 -0
- package/dist/application/capability/resolution.js +264 -0
- package/dist/application/capability/resolution.js.map +1 -0
- package/dist/application/capability/wrapper.js +187 -0
- package/dist/application/capability/wrapper.js.map +1 -0
- package/dist/application/context/manifest.js +11 -5
- package/dist/application/context/manifest.js.map +1 -1
- package/dist/application/context/measure.js +5 -2
- package/dist/application/context/measure.js.map +1 -1
- package/dist/application/context/plan-service.js +6 -6
- package/dist/application/context/plan-service.js.map +1 -1
- package/dist/application/design/design-bundle-service.js +148 -0
- package/dist/application/design/design-bundle-service.js.map +1 -0
- package/dist/application/design/design-proposal-service.js +69 -0
- package/dist/application/design/design-proposal-service.js.map +1 -0
- package/dist/application/design/design-publish-service.js +275 -7
- package/dist/application/design/design-publish-service.js.map +1 -1
- package/dist/application/design/design-record-service.js +100 -0
- package/dist/application/design/design-record-service.js.map +1 -0
- package/dist/application/flow/advance.js +533 -0
- package/dist/application/flow/advance.js.map +1 -0
- package/dist/application/flow/flow-service.js +65 -0
- package/dist/application/flow/flow-service.js.map +1 -0
- package/dist/application/flow/run-projection.js +80 -0
- package/dist/application/flow/run-projection.js.map +1 -0
- package/dist/application/flow/run-state-service.js +107 -0
- package/dist/application/flow/run-state-service.js.map +1 -0
- package/dist/application/flow/submit.js +423 -0
- package/dist/application/flow/submit.js.map +1 -0
- package/dist/application/resume-service.js +23 -10
- package/dist/application/resume-service.js.map +1 -1
- package/dist/application/self/install-skill.js +11 -0
- package/dist/application/self/install-skill.js.map +1 -1
- package/dist/application/self/uninstall.js +21 -0
- package/dist/application/self/uninstall.js.map +1 -1
- package/dist/application/skills-resolver-service.js +8 -2
- package/dist/application/skills-resolver-service.js.map +1 -1
- package/dist/application/status-service.js +4 -1
- package/dist/application/status-service.js.map +1 -1
- package/dist/application/workspace-init-service.js +13 -1
- package/dist/application/workspace-init-service.js.map +1 -1
- package/dist/cli/commands/capability.js +182 -0
- package/dist/cli/commands/capability.js.map +1 -0
- package/dist/cli/commands/flow.js +71 -0
- package/dist/cli/commands/flow.js.map +1 -0
- package/dist/cli/commands/index.js +4 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/skills.js +55 -2
- package/dist/cli/commands/skills.js.map +1 -1
- package/dist/cli/commands/status.js +5 -0
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/help-groups.js +6 -0
- package/dist/cli/help-groups.js.map +1 -1
- package/dist/cli/parser.js +6 -0
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/tui/data/workflow-content.js +5 -0
- package/dist/cli/tui/data/workflow-content.js.map +1 -1
- package/dist/cli/tui/tabs/workflow-tab.js +5 -1
- package/dist/cli/tui/tabs/workflow-tab.js.map +1 -1
- package/dist/domain/capability/descriptor.js +553 -0
- package/dist/domain/capability/descriptor.js.map +1 -0
- package/dist/domain/capability/effects.js +90 -0
- package/dist/domain/capability/effects.js.map +1 -0
- package/dist/domain/capability/protocol.js +494 -0
- package/dist/domain/capability/protocol.js.map +1 -0
- package/dist/domain/contract-reader.js +84 -0
- package/dist/domain/contract-reader.js.map +1 -0
- package/dist/domain/design/adapter.js +161 -0
- package/dist/domain/design/adapter.js.map +1 -0
- package/dist/domain/design/artifact.js +148 -4
- package/dist/domain/design/artifact.js.map +1 -1
- package/dist/domain/design/capability.js +263 -4
- package/dist/domain/design/capability.js.map +1 -1
- package/dist/domain/design/direct.js +108 -0
- package/dist/domain/design/direct.js.map +1 -0
- package/dist/domain/design/external-send.js +65 -0
- package/dist/domain/design/external-send.js.map +1 -0
- package/dist/domain/design/maturity.js +71 -0
- package/dist/domain/design/maturity.js.map +1 -1
- package/dist/domain/design/offline.js +92 -0
- package/dist/domain/design/offline.js.map +1 -0
- package/dist/domain/design/profiles.js +212 -0
- package/dist/domain/design/profiles.js.map +1 -0
- package/dist/domain/design/proposal.js +234 -0
- package/dist/domain/design/proposal.js.map +1 -0
- package/dist/domain/design/render-bundle.js +0 -0
- package/dist/domain/design/render-bundle.js.map +1 -0
- package/dist/domain/design/rendition.js +472 -0
- package/dist/domain/design/rendition.js.map +1 -0
- package/dist/domain/design/sources.js +180 -0
- package/dist/domain/design/sources.js.map +1 -0
- package/dist/domain/design/validation.js +12 -68
- package/dist/domain/design/validation.js.map +1 -1
- package/dist/domain/design/visual-evidence.js +155 -0
- package/dist/domain/design/visual-evidence.js.map +1 -0
- package/dist/domain/flow/answer.js +459 -0
- package/dist/domain/flow/answer.js.map +1 -0
- package/dist/domain/flow/authority.js +2276 -0
- package/dist/domain/flow/authority.js.map +1 -0
- package/dist/domain/flow/authorization.js +62 -0
- package/dist/domain/flow/authorization.js.map +1 -0
- package/dist/domain/flow/directive.js +421 -0
- package/dist/domain/flow/directive.js.map +1 -0
- package/dist/domain/flow/rules.js +140 -0
- package/dist/domain/flow/rules.js.map +1 -0
- package/dist/domain/flow/run-state.js +317 -0
- package/dist/domain/flow/run-state.js.map +1 -0
- package/dist/domain/harnesses.js +21 -0
- package/dist/domain/harnesses.js.map +1 -1
- package/dist/domain/skills.js +13 -14
- package/dist/domain/skills.js.map +1 -1
- package/package.json +1 -1
- package/skills/w/SKILL.md +5 -5
- package/skills/w/context/MANIFEST.json +38 -1
- package/skills/w/harness/HARNESS.md +20 -0
- package/skills/w/loops/CHASSIS.md +11 -20
- package/skills/w/loops/CODE-POLICIES.md +9 -15
- package/skills/w/loops/plan-exec-loop/LOOP.md +11 -13
- package/skills/w/loops/plan-new-loop/LOOP.md +6 -4
- package/skills/w/loops/plan-refine-loop/LOOP.md +5 -3
- package/skills/w/loops/quick-loop/LOOP.md +18 -26
- package/skills/w/loops/spec-refine-loop/LOOP.md +14 -18
- package/skills/w/modules/COMPACTION.md +2 -6
- package/skills/w/modules/DB-SCRIPTS-ONLY.md +2 -0
- package/skills/w/modules/DESIGN-REFERENCES.md +15 -11
- package/skills/w/modules/IDEATION-GATE.md +3 -3
- package/skills/w/modules/PLAN-EXECUTION-BATCHES.md +17 -36
- package/skills/w/modules/PLAN-INPUT.md +5 -3
- package/skills/w/modules/PLAN-REFINE-SPLIT.md +3 -1
- package/skills/w/modules/PLAN-SPLIT-GATE.md +7 -5
- package/skills/w/modules/PROMPT-CONTINUITY.md +3 -1
- package/skills/w/modules/SPEC-CHANGE-SHAPE.md +4 -4
- package/skills/w/roles/README.md +2 -0
- package/skills/w/roles/design/CONTRACT.md +80 -0
- package/skills/w/roles/design/ROLE.md +7 -0
- package/skills/w/schemas/capability-descriptor.schema.json +260 -0
- package/skills/w/schemas/design/design-render-bundle.v1.schema.json +191 -0
- package/skills/w/schemas/design/design-rendition.v1.schema.json +232 -0
- package/skills/w/schemas/design/ui-screen.v1.schema.json +29 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What is ACTUALLY installed, as exact instances.
|
|
3
|
+
*
|
|
4
|
+
* The advisory check this replaces compared names. A name is not a thing that
|
|
5
|
+
* runs: two directories can both be called `design`, and picking one by scan
|
|
6
|
+
* order is a silent choice nobody made. So the unit here is an instance —
|
|
7
|
+
* name, scope, the locator of its `SKILL.md`, the locator of its descriptor, its
|
|
8
|
+
* version and a digest over its bytes — and the two failure modes are kept
|
|
9
|
+
* apart:
|
|
10
|
+
*
|
|
11
|
+
* - **Same name, same digest, several roots.** Equivalent replicas. Every
|
|
12
|
+
* locator is kept, because "which copy answered" is still a real question.
|
|
13
|
+
* - **Same name, different bytes.** `misconfigured`. Not a precedence puzzle to
|
|
14
|
+
* resolve — two different things are wearing one name, and no scan order makes
|
|
15
|
+
* that safe.
|
|
16
|
+
*
|
|
17
|
+
* Only skills that DECLARE a descriptor enter this inventory. An ambient skill —
|
|
18
|
+
* a linter, a writing convention — is installed and invisible here, which is the
|
|
19
|
+
* whole content of the opt-in.
|
|
20
|
+
*/
|
|
21
|
+
import { createHash } from "node:crypto";
|
|
22
|
+
import { RETIRED_SKILL_IDENTITIES } from "../../domain/skills.js";
|
|
23
|
+
import { semanticDigest } from "../semantic-operation/protocol.js";
|
|
24
|
+
import { skillRoots } from "../skills-resolver-service.js";
|
|
25
|
+
import { loadDescriptor, readSkillHead } from "./descriptor-loader.js";
|
|
26
|
+
export async function buildCapabilityInventory(fs, env) {
|
|
27
|
+
const roots = skillRoots(env);
|
|
28
|
+
const candidates = [];
|
|
29
|
+
for (const root of roots) {
|
|
30
|
+
candidates.push(...(await scanRoot(fs, root)));
|
|
31
|
+
}
|
|
32
|
+
return { roots, capabilities: group(candidates) };
|
|
33
|
+
}
|
|
34
|
+
async function scanRoot(fs, root) {
|
|
35
|
+
if (!(await fs.exists(root)))
|
|
36
|
+
return [];
|
|
37
|
+
let entries;
|
|
38
|
+
try {
|
|
39
|
+
entries = await fs.list(root);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
const found = [];
|
|
45
|
+
for (const entry of entries) {
|
|
46
|
+
if (entry.type !== "dir")
|
|
47
|
+
continue;
|
|
48
|
+
const head = await readSkillHead(fs, entry.path, entry.name);
|
|
49
|
+
// No readable head, or no claim at all: an ambient skill. It never becomes a
|
|
50
|
+
// capability by being installed, so it never enters the inventory.
|
|
51
|
+
if (head === null || head.locatorValue === null)
|
|
52
|
+
continue;
|
|
53
|
+
found.push(await candidateFor(fs, root, entry.path, head));
|
|
54
|
+
}
|
|
55
|
+
return found;
|
|
56
|
+
}
|
|
57
|
+
async function candidateFor(fs, root, skillDir, head) {
|
|
58
|
+
const location = {
|
|
59
|
+
scope: root,
|
|
60
|
+
skillDir,
|
|
61
|
+
skillMd: head.skillMd,
|
|
62
|
+
descriptorPath: null,
|
|
63
|
+
};
|
|
64
|
+
const base = { name: head.name, version: head.version, location, descriptor: null };
|
|
65
|
+
// A directory wearing a retired name is refused before its descriptor is even
|
|
66
|
+
// read: the name IS the identity, and honoring it would resurrect the alias
|
|
67
|
+
// the contract exists to close.
|
|
68
|
+
const retired = RETIRED_SKILL_IDENTITIES.get(head.name.toLowerCase());
|
|
69
|
+
if (retired !== undefined) {
|
|
70
|
+
return {
|
|
71
|
+
...base,
|
|
72
|
+
digest: null,
|
|
73
|
+
failure: {
|
|
74
|
+
code: "CAPABILITY_NAME_RETIRED",
|
|
75
|
+
message: `'${head.name}' está retirado — ${retired}`,
|
|
76
|
+
action: "renombrá la skill al nombre vigente de la capacidad; no hay alias",
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const load = await loadDescriptor(fs, skillDir, head);
|
|
81
|
+
if (load.state !== "loaded") {
|
|
82
|
+
return {
|
|
83
|
+
...base,
|
|
84
|
+
digest: null,
|
|
85
|
+
failure: load.state === "invalid" ? load.failure : null,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
name: head.name,
|
|
90
|
+
version: head.version,
|
|
91
|
+
digest: await instanceDigest(fs, head.skillMd, load.loaded.digest),
|
|
92
|
+
location: { ...location, descriptorPath: load.loaded.path },
|
|
93
|
+
descriptor: load.loaded.descriptor,
|
|
94
|
+
failure: null,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The identity of the bytes that would run: the instruction file AND the
|
|
99
|
+
* contract it points at. Sealing only the descriptor would call two skills
|
|
100
|
+
* identical while their `SKILL.md` told the host to do different things.
|
|
101
|
+
*/
|
|
102
|
+
async function instanceDigest(fs, skillMd, descriptorDigest) {
|
|
103
|
+
try {
|
|
104
|
+
const text = await fs.readText(skillMd);
|
|
105
|
+
return semanticDigest({
|
|
106
|
+
skill_md: createHash("sha256").update(text, "utf8").digest("hex"),
|
|
107
|
+
descriptor: descriptorDigest,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function group(candidates) {
|
|
115
|
+
const byName = new Map();
|
|
116
|
+
for (const candidate of candidates) {
|
|
117
|
+
byName.set(candidate.name, [...(byName.get(candidate.name) ?? []), candidate]);
|
|
118
|
+
}
|
|
119
|
+
const out = [];
|
|
120
|
+
for (const [name, copies] of byName) {
|
|
121
|
+
const locations = copies.map((c) => c.location);
|
|
122
|
+
const broken = copies.find((c) => c.failure !== null);
|
|
123
|
+
if (broken?.failure != null) {
|
|
124
|
+
out.push({
|
|
125
|
+
name,
|
|
126
|
+
version: broken.version,
|
|
127
|
+
digest: null,
|
|
128
|
+
locations,
|
|
129
|
+
descriptor: null,
|
|
130
|
+
state: "misconfigured",
|
|
131
|
+
failure: broken.failure,
|
|
132
|
+
});
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const digests = [...new Set(copies.map((c) => c.digest))];
|
|
136
|
+
const first = copies[0];
|
|
137
|
+
if (digests.length > 1) {
|
|
138
|
+
out.push({
|
|
139
|
+
name,
|
|
140
|
+
version: first.version,
|
|
141
|
+
digest: null,
|
|
142
|
+
locations,
|
|
143
|
+
descriptor: null,
|
|
144
|
+
state: "misconfigured",
|
|
145
|
+
failure: {
|
|
146
|
+
code: "CAPABILITY_INSTANCE_COLLISION",
|
|
147
|
+
message: `hay ${copies.length} skills llamadas '${name}' con bytes distintos: ${locations.map((l) => l.skillDir).join(", ")}`,
|
|
148
|
+
action: "dejá una sola instalación de ese nombre, o renombrá la que no corresponde: no hay precedencia entre roots",
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
out.push({
|
|
154
|
+
name,
|
|
155
|
+
version: first.version,
|
|
156
|
+
digest: first.digest,
|
|
157
|
+
locations,
|
|
158
|
+
descriptor: first.descriptor,
|
|
159
|
+
state: "ready",
|
|
160
|
+
failure: null,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return out.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
164
|
+
}
|
|
165
|
+
export function findInstalled(inventory, name) {
|
|
166
|
+
return inventory.capabilities.find((c) => c.name === name) ?? null;
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=installed-inventory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installed-inventory.js","sourceRoot":"","sources":["../../../src/application/capability/installed-inventory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What can be invoked here, in what state, and what to do about it.
|
|
3
|
+
*
|
|
4
|
+
* The check this replaces compared names and emitted advice. Readiness answers
|
|
5
|
+
* the question a person actually has — "can I use this right now, and if not,
|
|
6
|
+
* why?" — and every state carries its evidence and one next action, because a
|
|
7
|
+
* status with neither is a puzzle rather than a diagnosis.
|
|
8
|
+
*
|
|
9
|
+
* Two things are deliberately kept apart:
|
|
10
|
+
*
|
|
11
|
+
* - **Per exposure.** The direct entrypoint and the composed route fail
|
|
12
|
+
* independently. A foreign skill squatting the wrapper's name breaks `direct`
|
|
13
|
+
* and leaves `compose` running on the floor; collapsing them into one state
|
|
14
|
+
* would hide half of that.
|
|
15
|
+
* - **Per operation.** `off` blocks four operations of `design` and keeps
|
|
16
|
+
* `validate` alive. One state for the capability cannot express that.
|
|
17
|
+
*
|
|
18
|
+
* The invocation form is read from `HARNESSES` and never inferred. A host with
|
|
19
|
+
* no verified form says so instead of borrowing another host's syntax.
|
|
20
|
+
*/
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { HARNESSES } from "../../domain/harnesses.js";
|
|
23
|
+
import { classifyCapabilityBinding } from "../../domain/skills.js";
|
|
24
|
+
import { resolveSkills } from "../skills-resolver-service.js";
|
|
25
|
+
import { capabilityHandler, registeredCapabilities } from "./dispatcher.js";
|
|
26
|
+
import { buildCapabilityInventory } from "./installed-inventory.js";
|
|
27
|
+
import { resolveCapability } from "./resolution.js";
|
|
28
|
+
import { inspectCapabilityDirVia } from "./wrapper.js";
|
|
29
|
+
export async function capabilityReadiness(input) {
|
|
30
|
+
const skills = await resolveSkills(input.fs, input.paths);
|
|
31
|
+
const inventory = await buildCapabilityInventory(input.fs, input.env);
|
|
32
|
+
const reports = [];
|
|
33
|
+
for (const name of registeredCapabilities()) {
|
|
34
|
+
const handler = capabilityHandler(name);
|
|
35
|
+
if (handler === null)
|
|
36
|
+
continue;
|
|
37
|
+
const slot = skills.skills[name];
|
|
38
|
+
const binding = slot === undefined
|
|
39
|
+
? { state: "floor_and_improvements", reason: null, action: null }
|
|
40
|
+
: classifyCapabilityBinding(slot, name);
|
|
41
|
+
const resolution = resolveCapability({ descriptor: handler.descriptor, binding, inventory });
|
|
42
|
+
reports.push(await reportFor(input, handler.descriptor, resolution, inventory, {
|
|
43
|
+
state: resolution.state,
|
|
44
|
+
reason: resolution.reason,
|
|
45
|
+
action: resolution.action,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
return reports;
|
|
49
|
+
}
|
|
50
|
+
async function reportFor(input, descriptor, resolution, inventory, verdict) {
|
|
51
|
+
const direct = await directVerdict(input, descriptor, verdict);
|
|
52
|
+
return {
|
|
53
|
+
capability: descriptor.name,
|
|
54
|
+
contract_version: descriptor.contract_version,
|
|
55
|
+
purpose: descriptor.purpose,
|
|
56
|
+
exposure: descriptor.exposure,
|
|
57
|
+
...verdict,
|
|
58
|
+
floor: {
|
|
59
|
+
builtin: descriptor.floor.builtin,
|
|
60
|
+
kind: descriptor.floor.kind,
|
|
61
|
+
running: resolution.floor,
|
|
62
|
+
},
|
|
63
|
+
instance: resolution.selection[0] === undefined
|
|
64
|
+
? null
|
|
65
|
+
: {
|
|
66
|
+
name: resolution.selection[0].name,
|
|
67
|
+
scope: resolution.selection[0].scope,
|
|
68
|
+
locator: resolution.selection[0].locator,
|
|
69
|
+
version: resolution.selection[0].version,
|
|
70
|
+
digest: resolution.selection[0].digest,
|
|
71
|
+
},
|
|
72
|
+
invocation: invocationFor(input.host, descriptor.name),
|
|
73
|
+
exposures: {
|
|
74
|
+
direct: descriptor.exposure.includes("direct")
|
|
75
|
+
? direct
|
|
76
|
+
: { state: "unavailable", reason: "la capacidad no expone la ruta directa", action: null },
|
|
77
|
+
compose: descriptor.exposure.includes("compose")
|
|
78
|
+
? verdict
|
|
79
|
+
: {
|
|
80
|
+
state: "unavailable",
|
|
81
|
+
reason: "la capacidad no expone la ruta compuesta",
|
|
82
|
+
action: null,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
operations: descriptor.operations.map((op) => {
|
|
86
|
+
const availability = resolution.operations.find((o) => o.operation === op.name);
|
|
87
|
+
const blocked = availability !== undefined && !availability.available;
|
|
88
|
+
return {
|
|
89
|
+
operation: op.name,
|
|
90
|
+
exposure: op.exposure,
|
|
91
|
+
workspace: op.workspace,
|
|
92
|
+
effects: op.effects.map((e) => e.class),
|
|
93
|
+
// The capability being `disabled` does NOT make the operation `off`
|
|
94
|
+
// spared disabled too: saying so would erase exactly the consumption
|
|
95
|
+
// `off` is required to preserve.
|
|
96
|
+
state: blocked ? "disabled" : availableState(verdict, resolution.floor),
|
|
97
|
+
reason: blocked ? availability.reason : verdict.reason,
|
|
98
|
+
action: blocked ? (resolution.action ?? null) : verdict.action,
|
|
99
|
+
};
|
|
100
|
+
}),
|
|
101
|
+
improvements: inventory.capabilities
|
|
102
|
+
.filter((c) => c.name !== descriptor.name)
|
|
103
|
+
.map((c) => eligibility(c, descriptor)),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The direct route lives or dies by a real directory on disk.
|
|
108
|
+
*
|
|
109
|
+
* A capability can be perfectly resolved and still be unreachable directly
|
|
110
|
+
* because nobody installed the wrapper — or because someone else's skill holds
|
|
111
|
+
* the name. Both are states of the ENTRYPOINT, not of the capability, so they
|
|
112
|
+
* are reported here and nowhere else.
|
|
113
|
+
*/
|
|
114
|
+
async function directVerdict(input, descriptor, verdict) {
|
|
115
|
+
const harness = HARNESSES.find((h) => h.id === input.host);
|
|
116
|
+
if (harness === undefined || harness.invocation === null) {
|
|
117
|
+
return {
|
|
118
|
+
state: "unavailable",
|
|
119
|
+
reason: `no hay una forma de invocación verificada para el host '${input.host}'`,
|
|
120
|
+
action: "usá la ruta compuesta desde un flow, o instalá en un host soportado",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const root = join(input.env.homeDir(), ...(harness.skillsDirs[0]?.split("/") ?? []));
|
|
124
|
+
const ownership = await inspectCapabilityDirVia(input.fs, join(root, descriptor.name));
|
|
125
|
+
if (ownership.state === "foreign") {
|
|
126
|
+
return {
|
|
127
|
+
state: "misconfigured",
|
|
128
|
+
reason: ownership.why,
|
|
129
|
+
action: `renombrá o quitá esa skill y reinstalá con 'aw self install-skill'`,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (ownership.state === "absent") {
|
|
133
|
+
return {
|
|
134
|
+
state: "unavailable",
|
|
135
|
+
reason: `no hay un wrapper '${descriptor.name}' instalado en ${root}`,
|
|
136
|
+
action: "instalalo con 'aw self install-skill'",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return verdict;
|
|
140
|
+
}
|
|
141
|
+
/** What an AVAILABLE operation's state is when the capability itself is off. */
|
|
142
|
+
function availableState(verdict, floorRunning) {
|
|
143
|
+
if (verdict.state !== "disabled")
|
|
144
|
+
return verdict.state;
|
|
145
|
+
return floorRunning ? "ready" : "unavailable";
|
|
146
|
+
}
|
|
147
|
+
function invocationFor(host, name) {
|
|
148
|
+
const harness = HARNESSES.find((h) => h.id === host);
|
|
149
|
+
const invocation = harness?.invocation ?? null;
|
|
150
|
+
if (invocation === null) {
|
|
151
|
+
return {
|
|
152
|
+
host,
|
|
153
|
+
kind: "unavailable",
|
|
154
|
+
form: null,
|
|
155
|
+
// Saying "unavailable" is the honest answer. Printing another host's
|
|
156
|
+
// syntax would send a person to type something that does nothing.
|
|
157
|
+
note: `no hay una forma de invocación verificada para '${host}'`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
host,
|
|
162
|
+
kind: invocation.kind,
|
|
163
|
+
form: invocation.template.replace("<name>", name),
|
|
164
|
+
note: invocation.note,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function eligibility(installed, descriptor) {
|
|
168
|
+
const improves = installed.descriptor?.compatibility.improves ?? null;
|
|
169
|
+
if (installed.state === "misconfigured") {
|
|
170
|
+
return {
|
|
171
|
+
name: installed.name,
|
|
172
|
+
digest: null,
|
|
173
|
+
improves: null,
|
|
174
|
+
eligible: false,
|
|
175
|
+
why: installed.failure?.message ?? "instancia no identificable",
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (improves === null || improves.capability !== descriptor.name) {
|
|
179
|
+
return {
|
|
180
|
+
name: installed.name,
|
|
181
|
+
digest: installed.digest,
|
|
182
|
+
improves: improves?.capability ?? null,
|
|
183
|
+
eligible: false,
|
|
184
|
+
why: `no declara mejorar '${descriptor.name}'`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (improves.contract_version !== descriptor.contract_version) {
|
|
188
|
+
return {
|
|
189
|
+
name: installed.name,
|
|
190
|
+
digest: installed.digest,
|
|
191
|
+
improves: improves.capability,
|
|
192
|
+
eligible: false,
|
|
193
|
+
why: `habla la versión de contrato ${improves.contract_version}`,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
name: installed.name,
|
|
198
|
+
digest: installed.digest,
|
|
199
|
+
improves: improves.capability,
|
|
200
|
+
eligible: true,
|
|
201
|
+
why: null,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.js","sourceRoot":"","sources":["../../../src/application/capability/readiness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What will actually run, and who gets credit for it.
|
|
3
|
+
*
|
|
4
|
+
* The resolution answers three questions that used to have no answer at all:
|
|
5
|
+
* whether the capability is available, whether an improvement contributes, and —
|
|
6
|
+
* when one does — WHICH exact instance, pinned so it cannot change mid-
|
|
7
|
+
* conversation. Everything here is built so that the honest answer is always
|
|
8
|
+
* reachable:
|
|
9
|
+
*
|
|
10
|
+
* - **No improvement is the ordinary case, not a degradation.** A machine with
|
|
11
|
+
* nothing extra installed runs the floor and reports `ready`. Calling that
|
|
12
|
+
* "degraded" would make the normal state look broken and hide the real one.
|
|
13
|
+
* - **A degradation names its cause and its loss, and attributes nothing.** When
|
|
14
|
+
* a selection cannot be identified, the run does not guess who helped; it says
|
|
15
|
+
* what it could not verify.
|
|
16
|
+
* - **A floor is what keeps a missing skill from blocking a flow.** A capability
|
|
17
|
+
* a core gate needs declares `floor.builtin`; one that does not may be
|
|
18
|
+
* `unavailable`, and then it blocks ITS operations and nothing else.
|
|
19
|
+
*/
|
|
20
|
+
import { findInstalled } from "./installed-inventory.js";
|
|
21
|
+
export function resolveCapability(input) {
|
|
22
|
+
const { descriptor, binding } = input;
|
|
23
|
+
const operations = availability(descriptor, binding);
|
|
24
|
+
if (binding.state === "off") {
|
|
25
|
+
return {
|
|
26
|
+
capability: descriptor.name,
|
|
27
|
+
state: "disabled",
|
|
28
|
+
// The operations `off` leaves alive still run on the floor. Reporting
|
|
29
|
+
// `floor: false` here would say nothing implements `validate`, which is
|
|
30
|
+
// exactly the consumption `off` is required to preserve.
|
|
31
|
+
floor: descriptor.floor.builtin && operations.some((o) => o.available),
|
|
32
|
+
selection: [],
|
|
33
|
+
degradations: [],
|
|
34
|
+
operations,
|
|
35
|
+
reason: binding.reason,
|
|
36
|
+
action: `para reactivarla, quitá el binding o ponelo en '${descriptor.name}'`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (binding.state === "misconfigured") {
|
|
40
|
+
return degradeToFloor(input, operations, {
|
|
41
|
+
cause: "invalid_binding",
|
|
42
|
+
loss: binding.reason ?? "el binding no selecciona una instancia identificable",
|
|
43
|
+
action: binding.action,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const contributors = input.hostSelection?.contributors ?? [];
|
|
47
|
+
if (descriptor.floor.improvements === "none" || contributors.length === 0) {
|
|
48
|
+
// Nothing extra selected. Not a loss — there was nothing to lose.
|
|
49
|
+
return floorOnly(descriptor, operations);
|
|
50
|
+
}
|
|
51
|
+
const verified = verifyContributors(input, contributors);
|
|
52
|
+
if (verified.degradation !== null) {
|
|
53
|
+
return degradeToFloor(input, operations, {
|
|
54
|
+
cause: verified.degradation.cause,
|
|
55
|
+
loss: verified.degradation.loss,
|
|
56
|
+
action: verified.degradation.action,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
capability: descriptor.name,
|
|
61
|
+
state: "ready",
|
|
62
|
+
floor: false,
|
|
63
|
+
selection: verified.selection,
|
|
64
|
+
degradations: [],
|
|
65
|
+
operations,
|
|
66
|
+
reason: null,
|
|
67
|
+
action: null,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function availability(descriptor, binding) {
|
|
71
|
+
return descriptor.operations.map((op) => operationState(op, binding));
|
|
72
|
+
}
|
|
73
|
+
function operationState(op, binding) {
|
|
74
|
+
if (binding.state === "off" && op.off === "blocked") {
|
|
75
|
+
return {
|
|
76
|
+
operation: op.name,
|
|
77
|
+
available: false,
|
|
78
|
+
// `off` is not reverted by a host, a wrapper or a legacy name; the only
|
|
79
|
+
// way back is the binding itself.
|
|
80
|
+
reason: "la capacidad está en off y esta operación queda bloqueada",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return { operation: op.name, available: true, reason: null };
|
|
84
|
+
}
|
|
85
|
+
function floorOnly(descriptor, operations) {
|
|
86
|
+
if (descriptor.floor.builtin) {
|
|
87
|
+
return {
|
|
88
|
+
capability: descriptor.name,
|
|
89
|
+
state: "ready",
|
|
90
|
+
floor: true,
|
|
91
|
+
selection: [],
|
|
92
|
+
degradations: [],
|
|
93
|
+
operations,
|
|
94
|
+
reason: null,
|
|
95
|
+
action: null,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// Feature-only with nothing installed. It blocks ITS operations — the flows
|
|
99
|
+
// that never asked for it keep working, which is the whole isolation rule.
|
|
100
|
+
return {
|
|
101
|
+
capability: descriptor.name,
|
|
102
|
+
state: "unavailable",
|
|
103
|
+
floor: false,
|
|
104
|
+
selection: [],
|
|
105
|
+
degradations: [],
|
|
106
|
+
operations: operations.map((o) => ({
|
|
107
|
+
operation: o.operation,
|
|
108
|
+
available: false,
|
|
109
|
+
reason: `'${descriptor.name}' no tiene floor incorporado y no hay ninguna instancia conformante`,
|
|
110
|
+
})),
|
|
111
|
+
reason: `'${descriptor.name}' es una capacidad sin floor y no hay implementación instalada`,
|
|
112
|
+
action: `instalá una skill conformante con '${descriptor.name}' o dejá de invocar esa feature`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Fall back — but only where falling back is safe.
|
|
117
|
+
*
|
|
118
|
+
* With a built-in floor the run continues and the receipt carries the cause and
|
|
119
|
+
* the loss. Without one there is nothing safe to fall to, so the resolution says
|
|
120
|
+
* `misconfigured` instead of quietly producing a lesser result under the same
|
|
121
|
+
* name.
|
|
122
|
+
*/
|
|
123
|
+
function degradeToFloor(input, operations, seed) {
|
|
124
|
+
const { descriptor } = input;
|
|
125
|
+
const degradations = [{ cause: seed.cause, loss: seed.loss }];
|
|
126
|
+
const declared = descriptor.degradations.some((d) => d.cause === seed.cause);
|
|
127
|
+
if (!descriptor.floor.builtin || !declared) {
|
|
128
|
+
return {
|
|
129
|
+
capability: descriptor.name,
|
|
130
|
+
state: "misconfigured",
|
|
131
|
+
floor: false,
|
|
132
|
+
selection: [],
|
|
133
|
+
degradations,
|
|
134
|
+
operations: operations.map((o) => ({ ...o, available: false, reason: seed.loss })),
|
|
135
|
+
reason: seed.loss,
|
|
136
|
+
action: seed.action ?? `revisá la configuración de '${descriptor.name}'`,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
capability: descriptor.name,
|
|
141
|
+
state: "degraded",
|
|
142
|
+
floor: true,
|
|
143
|
+
// Empty on purpose: the whole point of the degradation is that nothing
|
|
144
|
+
// verifiable contributed, so nothing gets credit.
|
|
145
|
+
selection: [],
|
|
146
|
+
degradations,
|
|
147
|
+
operations,
|
|
148
|
+
reason: seed.loss,
|
|
149
|
+
action: seed.action ?? "corré la operación con el floor o corregí la selección",
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function verifyContributors(input, contributors) {
|
|
153
|
+
const selection = [];
|
|
154
|
+
const ordered = [...contributors].sort((a, b) => a.order - b.order);
|
|
155
|
+
for (const contributor of ordered) {
|
|
156
|
+
const installed = findInstalled(input.inventory, contributor.name);
|
|
157
|
+
const problem = identify(input, contributor, installed);
|
|
158
|
+
if (problem !== null)
|
|
159
|
+
return { selection: [], degradation: problem };
|
|
160
|
+
const instance = installed;
|
|
161
|
+
const location = instance.locations.find((l) => l.skillDir === contributor.locator) ?? instance.locations[0];
|
|
162
|
+
selection.push({
|
|
163
|
+
name: instance.name,
|
|
164
|
+
scope: location?.scope ?? "",
|
|
165
|
+
locator: location?.skillDir ?? "",
|
|
166
|
+
version: instance.version,
|
|
167
|
+
digest: instance.digest,
|
|
168
|
+
order: contributor.order,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return { selection, degradation: null };
|
|
172
|
+
}
|
|
173
|
+
/** Everything that can stop a declared contributor from being a real one. */
|
|
174
|
+
function identify(input, contributor, installed) {
|
|
175
|
+
if (installed === null) {
|
|
176
|
+
return {
|
|
177
|
+
cause: "opaque_selection",
|
|
178
|
+
loss: `el host declaró '${contributor.name}' y no hay ninguna instancia instalada con ese nombre`,
|
|
179
|
+
action: "instalá la skill declarada, o quitala de la selección del host",
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (installed.state === "misconfigured" || installed.digest === null) {
|
|
183
|
+
return {
|
|
184
|
+
cause: "opaque_selection",
|
|
185
|
+
loss: installed.failure?.message ?? `'${contributor.name}' no identifica una instancia exacta`,
|
|
186
|
+
action: installed.failure?.action ?? "dejá una sola instalación de ese nombre",
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// Equivalent replicas are the same BYTES, not the same install. A name-only
|
|
190
|
+
// selection among several roots still cannot say which copy answered, and
|
|
191
|
+
// picking the first would be exactly the silent precedence there must not be.
|
|
192
|
+
const locators = installed.locations.map((l) => l.skillDir);
|
|
193
|
+
if (locators.length > 1 && !locators.includes(contributor.locator ?? "")) {
|
|
194
|
+
return {
|
|
195
|
+
cause: "opaque_selection",
|
|
196
|
+
loss: `'${contributor.name}' está instalada en ${locators.length} raíces y la selección no dice cuál contribuyó`,
|
|
197
|
+
action: `declará el locator de la instancia (${locators.join(", ")}), o dejá una sola instalación`,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
if (contributor.digest != null && contributor.digest !== installed.digest) {
|
|
201
|
+
return {
|
|
202
|
+
cause: "digest_changed",
|
|
203
|
+
loss: `'${contributor.name}' cambió de bytes entre la selección del host y el inventario`,
|
|
204
|
+
action: "volvé a resolver la capacidad: la instancia dejó de ser la que se fijó",
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return compatible(input, contributor, installed);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Installed is not compatible. The claim has to come from the improvement's own
|
|
211
|
+
* descriptor and has to match the capability being resolved — the operation
|
|
212
|
+
* included, because improving `render` says nothing about `create`.
|
|
213
|
+
*/
|
|
214
|
+
function compatible(input, contributor, installed) {
|
|
215
|
+
const improves = installed.descriptor?.compatibility.improves ?? null;
|
|
216
|
+
const reject = (why) => ({
|
|
217
|
+
cause: "incompatible_improvement",
|
|
218
|
+
loss: `'${contributor.name}' ${why}`,
|
|
219
|
+
action: `corregí 'compatibility.improves' de '${contributor.name}', o sacala de la selección`,
|
|
220
|
+
});
|
|
221
|
+
if (improves === null) {
|
|
222
|
+
return reject(`no declara mejorar a '${input.descriptor.name}'`);
|
|
223
|
+
}
|
|
224
|
+
if (improves.capability !== input.descriptor.name) {
|
|
225
|
+
return reject(`declara mejorar a '${improves.capability}', no a '${input.descriptor.name}'`);
|
|
226
|
+
}
|
|
227
|
+
if (improves.contract_version !== input.descriptor.contract_version) {
|
|
228
|
+
return reject(`habla la versión de contrato ${improves.contract_version} y esta capacidad es la ${input.descriptor.contract_version}`);
|
|
229
|
+
}
|
|
230
|
+
const operation = input.operation ?? null;
|
|
231
|
+
if (operation !== null && !improves.operations.includes(operation)) {
|
|
232
|
+
return reject(`no declara mejorar la operación '${operation}'`);
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
export function pinSelection(resolution, descriptor) {
|
|
237
|
+
return {
|
|
238
|
+
capability: resolution.capability,
|
|
239
|
+
contract_version: descriptor.contract_version,
|
|
240
|
+
floor: resolution.floor,
|
|
241
|
+
instances: resolution.selection.map((s) => ({
|
|
242
|
+
name: s.name,
|
|
243
|
+
digest: s.digest,
|
|
244
|
+
order: s.order,
|
|
245
|
+
})),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
export function checkPin(pin, inventory) {
|
|
249
|
+
for (const instance of pin.instances) {
|
|
250
|
+
const installed = findInstalled(inventory, instance.name);
|
|
251
|
+
if (installed?.digest === instance.digest)
|
|
252
|
+
continue;
|
|
253
|
+
return {
|
|
254
|
+
ok: false,
|
|
255
|
+
degradation: {
|
|
256
|
+
cause: "digest_changed",
|
|
257
|
+
loss: `'${instance.name}' ya no son los bytes que se fijaron al empezar la invocación`,
|
|
258
|
+
},
|
|
259
|
+
action: "volvé a empezar la invocación: la selección fijada dejó de existir tal cual",
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return { ok: true };
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=resolution.js.map
|