@cohortapp/agent-sdk 2.3.2 → 2.4.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/framework-features.json +30 -0
- package/lib/backlog.mjs +136 -0
- package/lib/cadences.mjs +63 -2
- package/lib/cadences.test.mjs +105 -0
- package/lib/capability/inventory.mjs +542 -0
- package/lib/capability/inventory.test.mjs +232 -0
- package/lib/capability/probe.mjs +255 -0
- package/lib/channels/contract.mjs +37 -1
- package/lib/channels/contract.test.mjs +25 -1
- package/lib/claude-bin.mjs +37 -3
- package/lib/claude-bin.test.mjs +42 -8
- package/lib/execution/disposition.mjs +501 -0
- package/lib/execution/disposition.test.mjs +482 -0
- package/lib/execution/drive.mjs +352 -0
- package/lib/execution/drive.test.mjs +270 -0
- package/lib/execution/effects.mjs +340 -0
- package/lib/execution/effects.test.mjs +193 -0
- package/lib/execution/index.mjs +152 -0
- package/lib/execution/intake.mjs +581 -0
- package/lib/execution/intake.test.mjs +343 -0
- package/lib/execution/journal.mjs +374 -0
- package/lib/execution/journal.test.mjs +261 -0
- package/lib/execution/match.mjs +331 -0
- package/lib/execution/match.test.mjs +235 -0
- package/lib/execution/pipeline.mjs +341 -0
- package/lib/execution/pipeline.test.mjs +389 -0
- package/lib/execution/route.mjs +332 -0
- package/lib/execution/route.test.mjs +186 -0
- package/lib/execution/surface-policy.mjs +446 -0
- package/lib/execution/surface-policy.test.mjs +162 -0
- package/lib/goals/admission.mjs +209 -0
- package/lib/goals/admission.test.mjs +139 -0
- package/lib/goals/classify.mjs +206 -0
- package/lib/goals/classify.test.mjs +109 -0
- package/lib/goals/collaborate.mjs +415 -0
- package/lib/goals/collaborate.test.mjs +324 -0
- package/lib/goals/gaps.mjs +111 -0
- package/lib/goals/gaps.test.mjs +284 -0
- package/lib/goals/loop.mjs +537 -0
- package/lib/goals/loop.test.mjs +719 -0
- package/lib/identity/persona.mjs +247 -0
- package/lib/identity/persona.test.mjs +117 -0
- package/lib/kpi.mjs +469 -0
- package/lib/kpi.test.mjs +244 -0
- package/lib/mandate/audit.mjs +168 -0
- package/lib/mandate/audit.test.mjs +195 -0
- package/lib/mandate/cache.mjs +162 -0
- package/lib/mandate/derive.mjs +317 -0
- package/lib/mandate/derive.test.mjs +224 -0
- package/lib/mandate/model.mjs +352 -0
- package/lib/mandate/model.test.mjs +145 -0
- package/lib/mandate/refresh.mjs +187 -0
- package/lib/mandate/refresh.test.mjs +293 -0
- package/lib/mcp/server.test.mjs +4 -4
- package/lib/org/approvals.mjs +14 -2
- package/lib/org/client.mjs +58 -22
- package/lib/org/client.test.mjs +3 -1
- package/lib/org/inbound/directedness.mjs +720 -0
- package/lib/org/inbound/directedness.test.mjs +543 -0
- package/lib/org/inbound/facts.mjs +501 -0
- package/lib/org/inbound/facts.test.mjs +375 -0
- package/lib/org/inbound/hydrate.mjs +535 -0
- package/lib/org/inbound/hydrate.test.mjs +326 -0
- package/lib/org/inbound/index.mjs +233 -0
- package/lib/org/inbound/index.test.mjs +324 -0
- package/lib/org/inbound/io.mjs +141 -0
- package/lib/org/inbound/project.mjs +201 -0
- package/lib/org/inbound/project.test.mjs +287 -0
- package/lib/org/inbound/surfaces.mjs +257 -0
- package/lib/org/knowledge.mjs +10 -1
- package/lib/org/knowledge.test.mjs +8 -1
- package/lib/org/leases.mjs +5 -0
- package/lib/org/mesh.mjs +17 -2
- package/lib/org/messaging.mjs +40 -4
- package/lib/org/messaging.test.mjs +40 -0
- package/lib/org/param-contract.mjs +694 -0
- package/lib/org/param-contract.test.mjs +451 -0
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +8 -0
- package/lib/org/protocol.test.mjs +5 -1
- package/lib/org/push.mjs +1025 -0
- package/lib/org/push.test.mjs +690 -0
- package/lib/org/tool-surface.mjs +138 -38
- package/lib/org/tool-surface.test.mjs +13 -8
- package/lib/org/typing.mjs +341 -0
- package/lib/org/typing.test.mjs +291 -0
- package/lib/plan/compile.mjs +510 -0
- package/lib/plan/compile.test.mjs +286 -0
- package/lib/plan/emit.mjs +256 -0
- package/lib/plan/emit.test.mjs +246 -0
- package/lib/plan/explain.mjs +226 -0
- package/lib/plan/explain.test.mjs +188 -0
- package/lib/plan/schema.mjs +140 -0
- package/lib/resource-governor.mjs +47 -1
- package/lib/resource-governor.test.mjs +21 -1
- package/lib/setup/enroll-from-cohort.mjs +84 -16
- package/lib/setup/enroll-from-cohort.test.mjs +43 -1
- package/lib/setup/sections/identity.mjs +15 -4
- package/lib/setup/sections/identity.test.mjs +94 -0
- package/lib/setup/sections/inventory.mjs +178 -0
- package/lib/setup/sections/inventory.test.mjs +198 -0
- package/lib/setup/sections/mandate.mjs +392 -0
- package/lib/setup/sections/mandate.test.mjs +373 -0
- package/lib/setup/sections/subagents.mjs +427 -0
- package/lib/setup/sections/subagents.test.mjs +429 -0
- package/lib/setup/sections/verify.mjs +121 -0
- package/lib/setup/sections/verify.test.mjs +175 -0
- package/lib/setup/sot.mjs +2 -0
- package/lib/subagents/cli.mjs +463 -0
- package/lib/subagents/cli.test.mjs +389 -0
- package/lib/subagents/client.mjs +373 -0
- package/lib/subagents/client.test.mjs +309 -0
- package/lib/subagents/gap.mjs +268 -0
- package/lib/subagents/gap.test.mjs +234 -0
- package/lib/subagents/lock.mjs +296 -0
- package/lib/subagents/lock.test.mjs +248 -0
- package/lib/subagents/manifest.mjs +224 -0
- package/lib/subagents/manifest.test.mjs +175 -0
- package/lib/subagents/refs.mjs +274 -0
- package/lib/subagents/refs.test.mjs +204 -0
- package/lib/subagents/resolve.mjs +455 -0
- package/lib/subagents/resolve.test.mjs +422 -0
- package/lib/subagents/schema.mjs +467 -0
- package/lib/subagents/schema.test.mjs +306 -0
- package/package.json +8 -3
- package/plugins/maestro-skills/.claude-plugin/marketplace.json +16 -0
- package/policies/ai-disclosure.yaml +42 -2
- package/scaffold/CLAUDE.md +16 -2
- package/schedules/triggers/goal-steward.md +79 -0
- package/scripts/ci/conformance-org-api.mjs +792 -0
- package/scripts/ci/conformance-org-api.test.mjs +417 -0
- package/scripts/daemon/agent-daemon.mjs +36 -4
- package/scripts/daemon/cadence-handlers.mjs +145 -1
- package/scripts/daemon/goal-steward-cadence.test.mjs +243 -0
- package/scripts/daemon/inbox-deferral.mjs +45 -2
- package/scripts/daemon/inbox-deferral.test.mjs +56 -0
- package/scripts/daemon/inbox-wake.mjs +282 -0
- package/scripts/daemon/inbox-wake.test.mjs +199 -0
- package/scripts/daemon/prompt-builder.mjs +41 -1
- package/scripts/daemon/typing-registry.mjs +55 -2
- package/scripts/daemon/typing-registry.test.mjs +25 -0
- package/scripts/local-triggers/generate-plists.test.mjs +5 -5
- package/scripts/setup/gen-subagent-manifest.mjs +95 -0
- package/scripts/setup/gen-subagent-manifest.test.mjs +124 -0
- package/scripts/setup/generate-plan.mjs +108 -0
- package/scripts/setup/init-capability-manifest.mjs +70 -0
- package/scripts/setup/init-skill-marketplace.mjs +155 -0
- package/scripts/setup/init-skill-marketplace.test.mjs +193 -0
package/lib/org/messaging.mjs
CHANGED
|
@@ -431,8 +431,28 @@ export async function pullInbound(o = {}) {
|
|
|
431
431
|
maxSeq = Math.max(Number(maxSeq) || 0, Number(ev.seq));
|
|
432
432
|
}
|
|
433
433
|
const directed = isDirectedAtAgent(ev, me, { myChannelIds });
|
|
434
|
-
if (
|
|
435
|
-
|
|
434
|
+
if (directed) {
|
|
435
|
+
events.push(toMessageEvent(ev, me));
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
// MENTION CANDIDATE. The org-wide feed says `mentionCount` — a NUMBER —
|
|
439
|
+
// and deliberately does NOT name who was mentioned: it is readable by every
|
|
440
|
+
// agent holding `org.read`, so listing member ids there would leak the
|
|
441
|
+
// association graph of private rooms (the payload is a REDACTED audit row
|
|
442
|
+
// by design). That redaction is right, and it is also why this filter alone
|
|
443
|
+
// is deaf to an @mention in a space.
|
|
444
|
+
//
|
|
445
|
+
// So resolve it the ACL-safe way: mark it a candidate here and confirm it
|
|
446
|
+
// below against `messaging.history`, which hq gates PER MEMBER. If this
|
|
447
|
+
// agent cannot read that room, it gets nothing back and the candidate is
|
|
448
|
+
// dropped — fail-closed, with no widening of what the shared feed says.
|
|
449
|
+
const p = ev.payload || ev;
|
|
450
|
+
const count = Number(p.mentionCount || 0);
|
|
451
|
+
if (me && count > 0) {
|
|
452
|
+
const cand = toMessageEvent(ev, me);
|
|
453
|
+
cand._mentionCandidate = true;
|
|
454
|
+
events.push(cand);
|
|
455
|
+
}
|
|
436
456
|
}
|
|
437
457
|
|
|
438
458
|
// HYDRATE THE BODY. The org event feed is redacted by design — it carries
|
|
@@ -448,7 +468,7 @@ export async function pullInbound(o = {}) {
|
|
|
448
468
|
//
|
|
449
469
|
// Fail-open: a message we cannot hydrate is dropped rather than delivered
|
|
450
470
|
// blank, because a blank inbound produces a nonsense reply in a real room.
|
|
451
|
-
const needBody = events.filter((e) => !e.text && e.kind !== "call");
|
|
471
|
+
const needBody = events.filter((e) => (!e.text || e._mentionCandidate) && e.kind !== "call");
|
|
452
472
|
if (needBody.length) {
|
|
453
473
|
const byChannel = new Map();
|
|
454
474
|
for (const e of needBody) {
|
|
@@ -471,6 +491,10 @@ export async function pullInbound(o = {}) {
|
|
|
471
491
|
const hit = byId.get(String(e.message_id));
|
|
472
492
|
if (!hit) continue;
|
|
473
493
|
e.text = String(hit.body ?? hit.text ?? "");
|
|
494
|
+
// The ACL'd read is the ONLY place mention identities are trusted.
|
|
495
|
+
e._mentions = Array.isArray(hit.mentions)
|
|
496
|
+
? hit.mentions.map((m) => String((m && (m.memberId || m.id)) ?? m))
|
|
497
|
+
: [];
|
|
474
498
|
if (!e.from.name || e.from.name === e.from.id) {
|
|
475
499
|
e.from.name = String(hit.authorName || hit.author?.displayName || e.from.name || "");
|
|
476
500
|
}
|
|
@@ -481,7 +505,19 @@ export async function pullInbound(o = {}) {
|
|
|
481
505
|
}
|
|
482
506
|
}
|
|
483
507
|
|
|
484
|
-
|
|
508
|
+
// Confirm the mention candidates against what history actually returned. A
|
|
509
|
+
// candidate survives ONLY if this agent's id is in that message's `mentions`
|
|
510
|
+
// — proven by an ACL'd read, never inferred from the shared feed.
|
|
511
|
+
const confirmed = events.filter((e) => {
|
|
512
|
+
if (!e._mentionCandidate) return true;
|
|
513
|
+
const ok = Array.isArray(e._mentions) && e._mentions.map(String).includes(String(me));
|
|
514
|
+
if (!ok) return false;
|
|
515
|
+
e.reason = "mention";
|
|
516
|
+
return true;
|
|
517
|
+
});
|
|
518
|
+
for (const e of confirmed) { delete e._mentionCandidate; delete e._mentions; }
|
|
519
|
+
|
|
520
|
+
const delivered = confirmed.filter((e) => e.kind === "call" || (e.text && e.text.trim()));
|
|
485
521
|
return { events: delivered, nextCursor: raw.length ? maxSeq : cursor };
|
|
486
522
|
}
|
|
487
523
|
|
|
@@ -313,3 +313,43 @@ test("isDirectedAtAgent: a call event in my room is an invite", () => {
|
|
|
313
313
|
payload: { channelId: "room-1", topic: "standup" } };
|
|
314
314
|
assert.equal(isDirectedAtAgent(ev, "agent-self", { myChannelIds: mine }), true);
|
|
315
315
|
});
|
|
316
|
+
|
|
317
|
+
test("isDirectedAtAgent: the SHARED feed never names who was mentioned", () => {
|
|
318
|
+
// hq's chain payload is a REDACTED audit row read by every agent holding
|
|
319
|
+
// `org.read`, so it carries `mentionCount` and deliberately NOT the member
|
|
320
|
+
// ids — listing them would leak the association graph of private rooms. This
|
|
321
|
+
// filter therefore cannot resolve a space @mention on its own, BY DESIGN;
|
|
322
|
+
// pullInbound confirms candidates against the per-member `messaging.history`
|
|
323
|
+
// read instead. Asserted so nobody "fixes" the deafness by widening the feed.
|
|
324
|
+
const shared = {
|
|
325
|
+
family: "messaging", kind: "send", entityId: "msg_1", actor: "human-1",
|
|
326
|
+
payload: { channelId: "space-general", channelKind: "PUBLIC", mentionCount: 1 },
|
|
327
|
+
};
|
|
328
|
+
assert.equal(isDirectedAtAgent(shared, "agent-self"), false);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("isDirectedAtAgent: an explicit mentions[] still wins when a producer supplies one", () => {
|
|
332
|
+
// hq's chain payload used to carry `mentionCount` — a NUMBER — so an agent
|
|
333
|
+
// tailing the feed could see that a mention happened but not that it was the
|
|
334
|
+
// one mentioned. Combined with hq deferring to live daemons, an @mention in a
|
|
335
|
+
// public space reached nobody: the server stayed quiet and the agent was deaf.
|
|
336
|
+
// hq now emits `mentions: [memberId…]`, which this filter already understood.
|
|
337
|
+
const ev = {
|
|
338
|
+
seq: 10500,
|
|
339
|
+
family: "messaging",
|
|
340
|
+
kind: "send",
|
|
341
|
+
entityId: "msg_1",
|
|
342
|
+
actor: "human-1",
|
|
343
|
+
payload: {
|
|
344
|
+
channelId: "space-general",
|
|
345
|
+
channelKind: "PUBLIC",
|
|
346
|
+
mentions: ["agent-self"],
|
|
347
|
+
mentionCount: 1,
|
|
348
|
+
recipientCount: 12,
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
// Membership is NOT required for a mention — being named is the invitation.
|
|
352
|
+
assert.equal(isDirectedAtAgent(ev, "agent-self"), true);
|
|
353
|
+
// …and someone else's mention in the same space is still not ours.
|
|
354
|
+
assert.equal(isDirectedAtAgent(ev, "agent-other"), false);
|
|
355
|
+
});
|