@cohortapp/agent-sdk 2.4.1 → 2.5.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/bin/maestro.mjs +9 -0
- package/lib/backlog.mjs +35 -0
- package/lib/backlog.test.mjs +36 -0
- package/lib/channels/contract.mjs +1 -0
- package/lib/channels/contract.test.mjs +2 -1
- package/lib/channels/inbox-item.mjs +54 -0
- package/lib/comms/send-gate.mjs +56 -1
- package/lib/comms/send-gate.test.mjs +56 -0
- package/lib/execution/disposition.mjs +62 -2
- package/lib/execution/disposition.test.mjs +54 -0
- package/lib/execution/drive.mjs +1 -1
- package/lib/execution/effects.mjs +282 -24
- package/lib/execution/effects.test.mjs +112 -0
- package/lib/execution/index.mjs +1 -0
- package/lib/execution/intake.mjs +43 -9
- package/lib/execution/intake.test.mjs +46 -0
- package/lib/execution/pipeline.mjs +5 -0
- package/lib/execution/surface-policy.mjs +80 -30
- package/lib/goals/classify.mjs +49 -5
- package/lib/goals/classify.test.mjs +58 -0
- package/lib/goals/collaborate.mjs +131 -17
- package/lib/goals/collaborate.test.mjs +16 -4
- package/lib/goals/loop.mjs +160 -9
- package/lib/goals/loop.test.mjs +129 -3
- package/lib/kpi-sensors.mjs +666 -0
- package/lib/kpi-sensors.test.mjs +275 -0
- package/lib/kpi.mjs +23 -0
- package/lib/mandate/audit.mjs +3 -0
- package/lib/mandate/contract.mjs +277 -0
- package/lib/mandate/contract.test.mjs +185 -0
- package/lib/mandate/derive.mjs +49 -5
- package/lib/mandate/derive.test.mjs +7 -1
- package/lib/mandate/model.mjs +10 -1
- package/lib/mandate/model.test.mjs +22 -3
- package/lib/mandate/refresh.mjs +53 -5
- package/lib/mandate/refresh.test.mjs +83 -1
- package/lib/org/doctor.mjs +66 -0
- package/lib/org/doctor.test.mjs +73 -1
- package/lib/org/inbound/directedness.mjs +119 -1
- package/lib/org/inbound/directedness.test.mjs +67 -0
- package/lib/org/inbound/facts.mjs +132 -9
- package/lib/org/inbound/facts.test.mjs +96 -0
- package/lib/org/inbound/hydrate.mjs +40 -0
- package/lib/org/inbound/index.test.mjs +83 -0
- package/lib/org/inbound/project.mjs +8 -0
- package/lib/org/inbound/surfaces.mjs +20 -0
- package/lib/org/param-contract.mjs +16 -2
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +214 -2
- package/lib/org/protocol.test.mjs +11 -2
- package/lib/org/push.mjs +213 -49
- package/lib/org/push.test.mjs +112 -10
- package/lib/plan/compile.mjs +85 -8
- package/lib/plan/compile.test.mjs +82 -0
- package/lib/plan/emit.test.mjs +6 -1
- package/lib/setup/sections/mandate.mjs +43 -1
- package/lib/subagents/schema.mjs +14 -2
- package/lib/subagents/schema.test.mjs +22 -0
- package/package.json +1 -1
- package/scripts/ci/check-subagent-frontmatter.mjs +139 -0
- package/scripts/ci/check-subagent-frontmatter.test.mjs +124 -0
- package/scripts/ci/check.mjs +3 -0
- package/scripts/ci/conformance-org-api.mjs +16 -0
- package/scripts/ci/journey-approval-escalation.mjs +341 -0
- package/scripts/daemon/agent-daemon.mjs +582 -28
- package/scripts/daemon/cadence-handlers.mjs +273 -17
- package/scripts/daemon/cadence-handlers.test.mjs +101 -0
- package/scripts/daemon/execution-ladder.test.mjs +430 -0
- package/scripts/daemon/goal-steward-cadence.test.mjs +69 -0
- package/scripts/daemon/maestro-daemon.mjs +53 -0
- package/scripts/daemon/prompt-builder.mjs +47 -0
- package/scripts/daemon/responder.mjs +70 -3
- package/scripts/poller/imap-client.mjs +20 -1
- package/scripts/poller/inbox-scan-poller.mjs +15 -0
- package/scripts/poller/utils.mjs +51 -0
- package/scripts/setup/generate-capability.mjs +120 -11
- package/scripts/setup/generate-capability.test.mjs +134 -0
- package/scripts/setup/generate-plan.mjs +6 -1
- package/scripts/setup/repair-subagent-frontmatter.mjs +231 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* repair-subagent-frontmatter.mjs — bring every `agents/<slug>/agent.md` back to
|
|
4
|
+
* a state that `lib/subagents/schema.validateAgentMd()` accepts.
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS EXISTS. `renderAgentMd()` in generate-capability.mjs shipped two bugs
|
|
7
|
+
* that every repo scaffolded before 2026-08-11 inherited across its whole role
|
|
8
|
+
* roster:
|
|
9
|
+
*
|
|
10
|
+
* 1. it emitted no `tools:` key, so EVERY generated sub-agent failed
|
|
11
|
+
* validation ("frontmatter.tools is required") — 57 of 70 in this repo; and
|
|
12
|
+
* 2. it interpolated identity at GENERATION time rather than leaving
|
|
13
|
+
* `{{agent.*}}` tokens for lib/render.mjs, so a repo generated before
|
|
14
|
+
* config/agent.json was populated baked the empty values in permanently:
|
|
15
|
+
* "You are the Chief of Staff sub-agent for ( at UNCONFIGURED)".
|
|
16
|
+
*
|
|
17
|
+
* Both are fixed at source in generate-capability.mjs. This pass repairs the
|
|
18
|
+
* files that were already written, and it does so by calling that SAME exported
|
|
19
|
+
* renderAgentMd() — a second implementation here would be free to drift from the
|
|
20
|
+
* generator, which is the whole failure mode we are cleaning up.
|
|
21
|
+
*
|
|
22
|
+
* SAFETY. Dry-run by default; `--write` applies. It only rewrites files that
|
|
23
|
+
* FAIL validation and whose slug is still present in the capability pack, and it
|
|
24
|
+
* refuses to touch a file whose body has been edited away from the generator's
|
|
25
|
+
* output unless `--force` is passed — a hand-tailored mandate is real work and
|
|
26
|
+
* regenerating it silently would destroy it.
|
|
27
|
+
*
|
|
28
|
+
* Usage:
|
|
29
|
+
* node scripts/setup/repair-subagent-frontmatter.mjs # report only
|
|
30
|
+
* node scripts/setup/repair-subagent-frontmatter.mjs --write # apply
|
|
31
|
+
*
|
|
32
|
+
* @module scripts/setup/repair-subagent-frontmatter
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
"use strict";
|
|
36
|
+
|
|
37
|
+
import { readFileSync, writeFileSync, existsSync, readdirSync } from "node:fs";
|
|
38
|
+
import { join, dirname, resolve } from "node:path";
|
|
39
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
40
|
+
|
|
41
|
+
import {
|
|
42
|
+
checkAgentFile,
|
|
43
|
+
splitFrontmatter,
|
|
44
|
+
} from "../../lib/subagents/schema.mjs";
|
|
45
|
+
import { renderAgentMd } from "./generate-capability.mjs";
|
|
46
|
+
|
|
47
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
48
|
+
const MAESTRO_ROOT =
|
|
49
|
+
process.env.MAESTRO_ROOT &&
|
|
50
|
+
existsSync(join(process.env.MAESTRO_ROOT, "lib", "capability.mjs"))
|
|
51
|
+
? process.env.MAESTRO_ROOT
|
|
52
|
+
: resolve(here, "..", "..");
|
|
53
|
+
const AGENT_ROOT = process.env.AGENT_ROOT || process.cwd();
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The `{function, altitude}` the on-disk roster was generated from.
|
|
57
|
+
*
|
|
58
|
+
* Deliberately read from config/agent-roster.yaml, NOT config/agent.json: the
|
|
59
|
+
* roster records what was actually written to agents/, and the two can disagree
|
|
60
|
+
* (this repo's agent.json says technical-leader × svp while every file on disk is
|
|
61
|
+
* executive-operator × c-suite). Repairing against agent.json would look up the
|
|
62
|
+
* wrong pack and "repair" nothing.
|
|
63
|
+
*
|
|
64
|
+
* @param {object} yaml js-yaml module
|
|
65
|
+
* @returns {{function:string, altitude:string}|null}
|
|
66
|
+
*/
|
|
67
|
+
export function rosterArchetype(yaml, agentRoot = AGENT_ROOT) {
|
|
68
|
+
const p = join(agentRoot, "config", "agent-roster.yaml");
|
|
69
|
+
if (!existsSync(p)) return null;
|
|
70
|
+
const doc = yaml.load(readFileSync(p, "utf-8")) || {};
|
|
71
|
+
const m = String(doc.archetype || "").match(/^([a-z-]+)@([a-z-]+)$/);
|
|
72
|
+
return m ? { function: m[1], altitude: m[2] } : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Pull the prose under a `## <heading>` out of an agent.md body, whitespace
|
|
77
|
+
* normalised. Returns "" when the heading is absent.
|
|
78
|
+
*
|
|
79
|
+
* Normalising whitespace is not cosmetic here: the machine-wide prettier hook
|
|
80
|
+
* inserts a blank line after every markdown heading, so the on-disk files differ
|
|
81
|
+
* from the generator's own output by whitespace alone in three places.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} text full agent.md @param {string} heading e.g. "Mandate"
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
export function sectionText(text, heading) {
|
|
87
|
+
const body = splitFrontmatter(text).body;
|
|
88
|
+
const m = body.match(
|
|
89
|
+
new RegExp(`^##\\s+${heading}\\s*$([\\s\\S]*?)(?=^##\\s|\\Z)`, "m"),
|
|
90
|
+
);
|
|
91
|
+
return m ? m[1].replace(/\s+/g, " ").trim() : "";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Has this file's BODY been tailored away from the pack's own content?
|
|
96
|
+
*
|
|
97
|
+
* Deliberately NOT a diff against a regenerated file. The corrupt files differ
|
|
98
|
+
* from a token-bearing regeneration in exactly the identity spans, and trying to
|
|
99
|
+
* regex those spans back out is guesswork that fails open (an over-eager pattern
|
|
100
|
+
* silently overwrites someone's tailored mandate) or closed (a shy one repairs
|
|
101
|
+
* nothing — the first cut of this function flagged all 57 files as hand-edited).
|
|
102
|
+
*
|
|
103
|
+
* So compare the only two things a human tailoring an agent would actually
|
|
104
|
+
* change, against the pack they came from: the mandate prose and the towers
|
|
105
|
+
* list. Everything else in the template is boilerplate the generator owns. If
|
|
106
|
+
* both still match the pack verbatim, nothing has been authored here and
|
|
107
|
+
* regenerating destroys nothing.
|
|
108
|
+
*
|
|
109
|
+
* @param {string} currentText @param {object} packEntry @returns {boolean}
|
|
110
|
+
*/
|
|
111
|
+
export function bodyWasHandEdited(currentText, packEntry) {
|
|
112
|
+
const mandate = String((packEntry && packEntry.mandate) || "")
|
|
113
|
+
.replace(/\s+/g, " ")
|
|
114
|
+
.trim();
|
|
115
|
+
const towers = (packEntry && packEntry.towers) || [];
|
|
116
|
+
const expectedTowers = towers.length
|
|
117
|
+
? `- ${towers.join(", ")}`
|
|
118
|
+
: "- (general)";
|
|
119
|
+
return (
|
|
120
|
+
sectionText(currentText, "Mandate") !== mandate ||
|
|
121
|
+
sectionText(currentText, "Towers served") !== expectedTowers
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function main(argv = process.argv.slice(2)) {
|
|
126
|
+
const write = argv.includes("--write");
|
|
127
|
+
const force = argv.includes("--force");
|
|
128
|
+
|
|
129
|
+
const yamlMod = await import("js-yaml");
|
|
130
|
+
const yaml = yamlMod && yamlMod.default ? yamlMod.default : yamlMod;
|
|
131
|
+
const { resolveArchetype } = await import(
|
|
132
|
+
pathToFileURL(join(MAESTRO_ROOT, "lib", "archetype.mjs")).href
|
|
133
|
+
);
|
|
134
|
+
const { resolveCapabilitySurface } = await import(
|
|
135
|
+
pathToFileURL(join(MAESTRO_ROOT, "lib", "capability.mjs")).href
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const sel = rosterArchetype(yaml);
|
|
139
|
+
if (!sel) {
|
|
140
|
+
console.error(
|
|
141
|
+
"[repair-subagent-frontmatter] config/agent-roster.yaml has no `archetype: <function>@<altitude>` — nothing to repair against.",
|
|
142
|
+
);
|
|
143
|
+
return 1;
|
|
144
|
+
}
|
|
145
|
+
const profile = await resolveArchetype(sel);
|
|
146
|
+
const surface = await resolveCapabilitySurface(sel);
|
|
147
|
+
const byId = new Map(surface.roleAgents.map((a) => [a.id, a]));
|
|
148
|
+
|
|
149
|
+
const agentsDir = join(AGENT_ROOT, "agents");
|
|
150
|
+
const slugs = existsSync(agentsDir)
|
|
151
|
+
? readdirSync(agentsDir, { withFileTypes: true })
|
|
152
|
+
.filter((d) => d.isDirectory())
|
|
153
|
+
.map((d) => d.name)
|
|
154
|
+
.sort()
|
|
155
|
+
: [];
|
|
156
|
+
|
|
157
|
+
const repaired = [];
|
|
158
|
+
const skippedValid = [];
|
|
159
|
+
const unknown = [];
|
|
160
|
+
const handEdited = [];
|
|
161
|
+
|
|
162
|
+
for (const slug of slugs) {
|
|
163
|
+
const p = join(agentsDir, slug, "agent.md");
|
|
164
|
+
if (!existsSync(p)) {
|
|
165
|
+
unknown.push(`${slug} (no agent.md)`);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
const text = readFileSync(p, "utf-8");
|
|
169
|
+
if (checkAgentFile(text, slug).ok) {
|
|
170
|
+
skippedValid.push(slug);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const packEntry = byId.get(slug);
|
|
174
|
+
if (!packEntry) {
|
|
175
|
+
unknown.push(`${slug} (invalid, but not in the ${sel.function} pack)`);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (!force && bodyWasHandEdited(text, packEntry)) {
|
|
179
|
+
handEdited.push(slug);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const fresh = renderAgentMd(packEntry, profile);
|
|
183
|
+
const after = checkAgentFile(fresh, slug);
|
|
184
|
+
if (!after.ok) {
|
|
185
|
+
unknown.push(
|
|
186
|
+
`${slug} (regenerated but STILL invalid: ${after.errors.join("; ")})`,
|
|
187
|
+
);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (write) writeFileSync(p, fresh, "utf-8");
|
|
191
|
+
repaired.push(slug);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const verb = write ? "repaired" : "would repair";
|
|
195
|
+
console.log(
|
|
196
|
+
`[repair-subagent-frontmatter] ${sel.function} × ${sel.altitude}: ${slugs.length} agent dir(s)`,
|
|
197
|
+
);
|
|
198
|
+
console.log(
|
|
199
|
+
`[repair-subagent-frontmatter] already valid: ${skippedValid.length}`,
|
|
200
|
+
);
|
|
201
|
+
console.log(`[repair-subagent-frontmatter] ${verb}: ${repaired.length}`);
|
|
202
|
+
if (handEdited.length)
|
|
203
|
+
console.log(
|
|
204
|
+
`[repair-subagent-frontmatter] SKIPPED (body hand-edited, pass --force to overwrite): ${handEdited.join(", ")}`,
|
|
205
|
+
);
|
|
206
|
+
if (unknown.length)
|
|
207
|
+
console.log(
|
|
208
|
+
`[repair-subagent-frontmatter] NOT repairable: ${unknown.join(", ")}`,
|
|
209
|
+
);
|
|
210
|
+
if (!write && repaired.length)
|
|
211
|
+
console.log(
|
|
212
|
+
"[repair-subagent-frontmatter] dry run — re-run with --write to apply.",
|
|
213
|
+
);
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (
|
|
218
|
+
process.argv[1] &&
|
|
219
|
+
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
220
|
+
) {
|
|
221
|
+
main()
|
|
222
|
+
.then((c) => process.exit(c))
|
|
223
|
+
.catch((e) => {
|
|
224
|
+
console.error(
|
|
225
|
+
`[repair-subagent-frontmatter] FAILED: ${e && e.message ? e.message : e}`,
|
|
226
|
+
);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export { main };
|