@tablation/crew 0.46.1 → 0.47.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/dist/cli.js +53 -7
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2256,6 +2256,7 @@ function parseResolved(raw, m, where) {
|
|
|
2256
2256
|
holds: holds2,
|
|
2257
2257
|
contract: raw.contract ?? void 0,
|
|
2258
2258
|
...typeof raw.releaseSeat === "string" && raw.releaseSeat ? { releaseSeat: raw.releaseSeat } : {},
|
|
2259
|
+
...typeof raw.releaseAgentId === "string" && raw.releaseAgentId ? { releaseAgentId: raw.releaseAgentId } : {},
|
|
2259
2260
|
...Object.keys(agentPersonas).length > 0 ? { agentPersonas } : {},
|
|
2260
2261
|
...Object.keys(agentSkills).length > 0 ? { agentSkills } : {}
|
|
2261
2262
|
};
|
|
@@ -7548,6 +7549,8 @@ function needsBaselineBackfill(model, row) {
|
|
|
7548
7549
|
function isPromptGateRejection(e) {
|
|
7549
7550
|
return e instanceof TablationApiError && e.status === 422;
|
|
7550
7551
|
}
|
|
7552
|
+
var RELEASE_AGENT_NAME = "Release agent";
|
|
7553
|
+
var RELEASE_AGENT_PROMPT = "Principal for the crew release phase (merge, deploy, release comments). It runs no model; this row only gives those tracker writes an agent identity.";
|
|
7551
7554
|
async function syncPersonas(route, opts) {
|
|
7552
7555
|
if (!route.resolved) {
|
|
7553
7556
|
throw new AgentsSyncError(`route "${route.route}" has no resolved ids \u2014 run \`crew connect\` first`);
|
|
@@ -7679,7 +7682,24 @@ async function syncPersonas(route, opts) {
|
|
|
7679
7682
|
}
|
|
7680
7683
|
}
|
|
7681
7684
|
const crewLinks = opts.dryRun ? [] : await linkCrewSeats(route, client2, agentsModel.id, agentPersonas, opts.userAgent);
|
|
7682
|
-
|
|
7685
|
+
const releaseAgentId = opts.dryRun ? route.resolved.releaseAgentId : await linkReleaseSeat(route, client2, agentsModel, opts.userAgent);
|
|
7686
|
+
return { outcomes, agentPersonas, crewLinks, ...releaseAgentId ? { releaseAgentId } : {} };
|
|
7687
|
+
}
|
|
7688
|
+
async function linkReleaseSeat(route, client2, agentsModel, userAgent) {
|
|
7689
|
+
const seatId = route.resolved.releaseSeat;
|
|
7690
|
+
if (!seatId) return void 0;
|
|
7691
|
+
const rows = await client2.records.list(agentsModel.id, { limit: 200 });
|
|
7692
|
+
const agentId = rows.find((r) => r.name === RELEASE_AGENT_NAME)?.id ?? (await client2.records.create(agentsModel.id, {
|
|
7693
|
+
name: RELEASE_AGENT_NAME,
|
|
7694
|
+
prompt: RELEASE_AGENT_PROMPT,
|
|
7695
|
+
...promptSourceFields(agentsModel, RELEASE_AGENT_PROMPT)
|
|
7696
|
+
})).id;
|
|
7697
|
+
const crewModelId = route.resolved.models.crew;
|
|
7698
|
+
const crewModel = await client2.dataModels.get(crewModelId);
|
|
7699
|
+
const columnName = await ensureCrewAgentField(route, client2, crewModel, agentsModel.id, userAgent);
|
|
7700
|
+
const seatRow = await client2.records.get(crewModelId, seatId);
|
|
7701
|
+
if (seatRow[CREW_AGENT_COLUMN] !== agentId) await client2.records.update(crewModelId, seatId, { [columnName]: agentId });
|
|
7702
|
+
return agentId;
|
|
7683
7703
|
}
|
|
7684
7704
|
async function linkCrewSeats(route, client2, agentsModelId, agentPersonas, userAgent) {
|
|
7685
7705
|
const seats2 = route.resolved.seats;
|
|
@@ -10870,6 +10890,18 @@ function slugFor(route, agentId) {
|
|
|
10870
10890
|
function seatAgentId(route, role) {
|
|
10871
10891
|
return route.resolved?.agentPersonas?.[role]?.agentId;
|
|
10872
10892
|
}
|
|
10893
|
+
async function resolveReleaseKey(route, getStore = getSessionStore2) {
|
|
10894
|
+
const ship = (reason) => ({ kind: "ship", apiKey: resolveApiKey(route), reason });
|
|
10895
|
+
const agentId = route.resolved?.releaseAgentId;
|
|
10896
|
+
if (!agentId) return ship("release seat has no linked Agents row (run `crew agents sync`)");
|
|
10897
|
+
try {
|
|
10898
|
+
const session = await getStore().get(hostFromUrl2(route.baseUrl), slugFor(route, agentId));
|
|
10899
|
+
if (session?.apiKey) return { kind: "agent", apiKey: session.apiKey, agentId };
|
|
10900
|
+
return ship("no agent key stored for the release seat (run `crew agents sync`)");
|
|
10901
|
+
} catch {
|
|
10902
|
+
return ship("OS keychain unavailable");
|
|
10903
|
+
}
|
|
10904
|
+
}
|
|
10873
10905
|
async function resolveSeatKey(route, role, getStore = getSessionStore2) {
|
|
10874
10906
|
const ship = (reason) => ({ kind: "ship", apiKey: resolveApiKey(route), reason });
|
|
10875
10907
|
if (!SEAT_KEY_ROLES.includes(role)) return ship(`${role} always writes as the ship`);
|
|
@@ -10888,12 +10920,15 @@ async function mintMissingSeatKeys(route, o) {
|
|
|
10888
10920
|
if (!route.resolved) return out;
|
|
10889
10921
|
const resolved = route.resolved;
|
|
10890
10922
|
const doFetch = o.fetchImpl ?? fetch;
|
|
10891
|
-
const
|
|
10923
|
+
const targets = [
|
|
10924
|
+
...SEAT_KEY_ROLES.filter((r) => resolved.seats[r]).map((role) => ({ role, agentId: seatAgentId(route, role) })),
|
|
10925
|
+
...resolved.releaseSeat ? [{ role: "release", agentId: resolved.releaseAgentId }] : []
|
|
10926
|
+
];
|
|
10892
10927
|
let store;
|
|
10893
10928
|
try {
|
|
10894
10929
|
store = (o.getStore ?? getSessionStore2)();
|
|
10895
10930
|
} catch (e) {
|
|
10896
|
-
return
|
|
10931
|
+
return targets.map(({ role }) => ({
|
|
10897
10932
|
role,
|
|
10898
10933
|
action: "failed",
|
|
10899
10934
|
detail: `OS keychain unavailable: ${e.message}`
|
|
@@ -10901,8 +10936,7 @@ async function mintMissingSeatKeys(route, o) {
|
|
|
10901
10936
|
}
|
|
10902
10937
|
const host = hostFromUrl2(route.baseUrl);
|
|
10903
10938
|
const base = route.baseUrl.replace(/\/+$/, "");
|
|
10904
|
-
for (const role of
|
|
10905
|
-
const agentId = seatAgentId(route, role);
|
|
10939
|
+
for (const { role, agentId } of targets) {
|
|
10906
10940
|
if (!agentId) {
|
|
10907
10941
|
out.push({ role, action: "no_agent" });
|
|
10908
10942
|
continue;
|
|
@@ -13277,7 +13311,9 @@ ${changed2.map((c) => ` - ${c}
|
|
|
13277
13311
|
return { scope, tests: "skipped", outcome: "skipped", detail: note };
|
|
13278
13312
|
}
|
|
13279
13313
|
if (!dryRun && !c.enabled) return { scope, tests: "skipped", outcome: "skipped", detail: "route not enabled" };
|
|
13280
|
-
const
|
|
13314
|
+
const releaseIdentity = await resolveReleaseKey(c);
|
|
13315
|
+
if (releaseIdentity.kind === "ship") remit.warn(`release writes as the ship, not its own agent: ${releaseIdentity.reason}`, { step: "release" });
|
|
13316
|
+
const tracker = new Tracker(c, cfg.ship, { apiKey: releaseIdentity.apiKey });
|
|
13281
13317
|
if (!dryRun) {
|
|
13282
13318
|
const force2 = command === "deploy" || flag("force");
|
|
13283
13319
|
let seen = precheckTickets.get(c.route);
|
|
@@ -14237,12 +14273,14 @@ ${changed2.map((c) => ` - ${c}
|
|
|
14237
14273
|
const raw = existsSync24(resolvedPath) ? JSON.parse(readFileSync18(resolvedPath, "utf8")) : {};
|
|
14238
14274
|
raw.agentPersonas = personaResult.agentPersonas;
|
|
14239
14275
|
raw.agentSkills = skillResult.agentSkills;
|
|
14276
|
+
if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
|
|
14240
14277
|
mkdirSync13(dirOf(resolvedPath), { recursive: true });
|
|
14241
14278
|
writeFileSync14(resolvedPath, `${JSON.stringify(raw, null, 2)}
|
|
14242
14279
|
`);
|
|
14243
14280
|
}
|
|
14244
14281
|
if (!dryRun) {
|
|
14245
14282
|
target.resolved.agentPersonas = personaResult.agentPersonas;
|
|
14283
|
+
if (personaResult.releaseAgentId) target.resolved.releaseAgentId = personaResult.releaseAgentId;
|
|
14246
14284
|
}
|
|
14247
14285
|
for (const o of await mintMissingSeatKeys(target, { userAgent: cfg.ship.userAgent, dryRun, shipName: cfg.ship.name })) {
|
|
14248
14286
|
process.stdout.write(`${describeMintOutcome(o)}
|
|
@@ -14708,7 +14746,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
14708
14746
|
// role as "diverged by definition" on the next sync even though
|
|
14709
14747
|
// nothing about its prompt actually changed.
|
|
14710
14748
|
...previous?.agentPersonas ? { agentPersonas: previous.agentPersonas } : {},
|
|
14711
|
-
...previous?.agentSkills ? { agentSkills: previous.agentSkills } : {}
|
|
14749
|
+
...previous?.agentSkills ? { agentSkills: previous.agentSkills } : {},
|
|
14750
|
+
...previous?.releaseAgentId ? { releaseAgentId: previous.releaseAgentId } : {}
|
|
14712
14751
|
};
|
|
14713
14752
|
if (dryRun) {
|
|
14714
14753
|
process.stderr.write(`(dry run) would write resolved ids to ${resolvedPath}
|
|
@@ -15613,6 +15652,13 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
15613
15652
|
const id = await resolveSeatKey(route, role);
|
|
15614
15653
|
process.stdout.write(
|
|
15615
15654
|
`seat identity: ${role} writes as ${id.kind === "agent" ? `its own agent (${id.agentId})` : `the ship (${id.reason})`}
|
|
15655
|
+
`
|
|
15656
|
+
);
|
|
15657
|
+
}
|
|
15658
|
+
if (route.resolved?.releaseSeat) {
|
|
15659
|
+
const id = await resolveReleaseKey(route);
|
|
15660
|
+
process.stdout.write(
|
|
15661
|
+
`seat identity: release writes as ${id.kind === "agent" ? `its own agent (${id.agentId})` : `the ship (${id.reason})`}
|
|
15616
15662
|
`
|
|
15617
15663
|
);
|
|
15618
15664
|
}
|
package/package.json
CHANGED