@tablation/crew 0.48.0 → 0.49.1

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.
Files changed (2) hide show
  1. package/dist/cli.js +50 -17
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -7470,6 +7470,17 @@ import { join as join13 } from "path";
7470
7470
  import { parse as parseYaml } from "yaml";
7471
7471
  import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError8, TablationApiError as TablationApiError2 } from "@tablation/client";
7472
7472
 
7473
+ // src/prompt-additions.ts
7474
+ var PROMPT_ADDITIONS_SEPARATOR = "---\n\nAdditional instructions from this workspace. Where they conflict with anything above, these take precedence.";
7475
+ function appendAdditions(text, additions) {
7476
+ if (typeof additions !== "string" || additions.trim() === "") return text;
7477
+ return `${text}
7478
+
7479
+ ${PROMPT_ADDITIONS_SEPARATOR}
7480
+
7481
+ ${additions}`;
7482
+ }
7483
+
7473
7484
  // src/agents.ts
7474
7485
  import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
7475
7486
  import { join as join12 } from "path";
@@ -7510,9 +7521,9 @@ async function resolveAgentId(route, role, opts) {
7510
7521
  return void 0;
7511
7522
  }
7512
7523
  }
7513
- async function fetchDivergedPrompt(route, role, opts) {
7524
+ async function fetchPersonaPrompt(route, role, opts) {
7514
7525
  const agentId = await resolveAgentId(route, role, opts);
7515
- if (!agentId || !route.resolved) return void 0;
7526
+ if (!agentId || !route.resolved) return {};
7516
7527
  const client2 = new TablationClient2({
7517
7528
  baseUrl: `${route.baseUrl}/api`,
7518
7529
  apiKey: resolveApiKey(route),
@@ -7525,8 +7536,17 @@ async function fetchDivergedPrompt(route, role, opts) {
7525
7536
  agentsModelId = agentsModel.id;
7526
7537
  row = await client2.records.get(agentsModelId, agentId);
7527
7538
  } catch {
7528
- return void 0;
7539
+ return {};
7529
7540
  }
7541
+ const additions = typeof row.prompt_additions === "string" && row.prompt_additions.trim() !== "" ? row.prompt_additions : void 0;
7542
+ const divergedPrompt = await divergedRowPrompt(route, role, row, agentId, agentsModelId, opts);
7543
+ return { divergedPrompt, additions };
7544
+ }
7545
+ function personaPromptOptions(p) {
7546
+ return { divergedPrompt: p.divergedPrompt, personaAdditions: p.additions };
7547
+ }
7548
+ async function divergedRowPrompt(route, role, row, agentId, agentsModelId, opts) {
7549
+ if (!route.resolved) return void 0;
7530
7550
  const defaultPrompt = personaDefaultPrompt(route.promptsDir, role);
7531
7551
  if (row.prompt === defaultPrompt) return void 0;
7532
7552
  const cached = route.resolved.agentPersonas?.[role];
@@ -7572,8 +7592,8 @@ async function fetchSeatPersona(route, role, opts) {
7572
7592
  }
7573
7593
  }
7574
7594
  async function currentPersonaPrompt(route, role, opts) {
7575
- const diverged = await fetchDivergedPrompt(route, role, opts);
7576
- return diverged ?? personaDefaultPrompt(route.promptsDir, role);
7595
+ const { divergedPrompt, additions } = await fetchPersonaPrompt(route, role, opts);
7596
+ return appendAdditions(divergedPrompt ?? personaDefaultPrompt(route.promptsDir, role), additions);
7577
7597
  }
7578
7598
  async function fetchHistoryEntries(route, agentsModelId, recordId, userAgent, limit = 50) {
7579
7599
  const url = `${route.baseUrl.replace(/\/+$/, "")}/api/data-models/${agentsModelId}/records/${recordId}/history?limit=${limit}`;
@@ -8031,14 +8051,18 @@ async function fetchSkillOverrides(route, opts) {
8031
8051
  const out = {};
8032
8052
  for (const row of rows) {
8033
8053
  const name = row.name;
8034
- if (row.prompt === local.get(name)?.prompt) continue;
8035
- if (row.prompt_customized !== true && row.prompt === row.prompt_baseline) continue;
8036
- const cached = cache[name];
8037
- if (cached?.skillId === row.id && cached.lastSyncedUpdatedAt === row.updated_at) continue;
8038
- out[name] = row.prompt;
8054
+ const edited = isEditedSkillRow(row, local.get(name)?.prompt, cache[name]);
8055
+ const base = edited ? row.prompt : local.get(name)?.prompt ?? row.prompt;
8056
+ const composed = appendAdditions(base, row.prompt_additions);
8057
+ if (edited || composed !== base) out[name] = composed;
8039
8058
  }
8040
8059
  return out;
8041
8060
  }
8061
+ function isEditedSkillRow(row, localPrompt, cached) {
8062
+ if (row.prompt === localPrompt) return false;
8063
+ if (row.prompt_customized !== true && row.prompt === row.prompt_baseline) return false;
8064
+ return !(cached?.skillId === row.id && cached.lastSyncedUpdatedAt === row.updated_at);
8065
+ }
8042
8066
  function skillMarkerWarnings(promptsDir, personaPrompts) {
8043
8067
  const shipped = new Set(listLocalSkills(promptsDir).map((s) => s.name));
8044
8068
  const out = [];
@@ -8122,7 +8146,7 @@ function planAgentRun(o) {
8122
8146
  const common = usesCommon ? readFileSync12(commonPath, "utf8") : "";
8123
8147
  const brief = readFileSync12(briefPath, "utf8");
8124
8148
  const effectiveCommon = o.divergedPrompt ? "" : common;
8125
- const effectiveBrief = o.divergedPrompt ?? brief;
8149
+ const effectiveBrief = appendAdditions(o.divergedPrompt ?? brief, o.personaAdditions);
8126
8150
  const localSkills = new Map(listLocalSkills(o.route.promptsDir).map((s) => [s.name, s.prompt]));
8127
8151
  let expanded;
8128
8152
  try {
@@ -8667,8 +8691,13 @@ ${v.reason[0].toUpperCase()}${v.reason.slice(1)}. Set back to \`in_progress\`, s
8667
8691
  async function enforceHandoff(i) {
8668
8692
  try {
8669
8693
  const t = await i.writer.ticket(i.ticketId);
8670
- if (t.status !== i.contract.statuses.handoff) return { kind: "not-handed-off" };
8694
+ const st = i.contract.statuses;
8671
8695
  const log = { ticket: t.issue_id, step: "handoff" };
8696
+ const claimed = [st.verifying, st.verified, st.deployed].some((s) => s && t.status === s);
8697
+ if (t.status !== st.handoff && !claimed) {
8698
+ i.log.emit(`${t.issue_id} is at ${t.status}, not handed off \u2014 nothing to check`, log);
8699
+ return { kind: "not-handed-off" };
8700
+ }
8672
8701
  const v = inspectHandoff(i.dir, i.cfg, t, i.role);
8673
8702
  if (v.kind === "unverified") {
8674
8703
  i.log.warn(`${t.issue_id} is at ${t.status} but the push was not checked: ${v.why}`, log);
@@ -8679,6 +8708,10 @@ async function enforceHandoff(i) {
8679
8708
  i.log.emit(`${t.issue_id} pushed at ${v.sha.slice(0, 7)} \u2014 handed on to QA`, log);
8680
8709
  return { kind: "pushed", sha: v.sha };
8681
8710
  }
8711
+ if (claimed) {
8712
+ i.log.warn(`${t.issue_id} is at ${t.status} but its branch is not on the remote at HEAD: ${v.reason}`, log);
8713
+ return { kind: "failed", why: v.reason };
8714
+ }
8682
8715
  const heldBy = i.hold ? t[i.hold.column] : void 0;
8683
8716
  await i.writer.updateTicket(t.id, {
8684
8717
  status: i.contract.statuses.building,
@@ -11196,7 +11229,7 @@ async function showLogEntry(route, entryId, opts = {}) {
11196
11229
  }
11197
11230
  };
11198
11231
  }
11199
- const prompt = match.prompt ?? "";
11232
+ const prompt = appendAdditions(match.prompt ?? "", match.prompt_additions);
11200
11233
  const actualSha = createHash3("sha256").update(prompt).digest("hex");
11201
11234
  const exact = !!entry.prompt_sha && actualSha === entry.prompt_sha;
11202
11235
  return {
@@ -12665,7 +12698,7 @@ async function runRoleAgent(role, decision, ctx) {
12665
12698
  cycle: emit.cycle,
12666
12699
  ticket: ticketHint,
12667
12700
  repo: ticketRepo,
12668
- divergedPrompt: await fetchDivergedPrompt(route, role, { userAgent: ship.userAgent }),
12701
+ ...personaPromptOptions(await fetchPersonaPrompt(route, role, { userAgent: ship.userAgent })),
12669
12702
  skillOverrides: await fetchSkillOverrides(route, { userAgent: ship.userAgent }),
12670
12703
  agentPersona: await fetchSeatPersona(route, role, { userAgent: ship.userAgent }),
12671
12704
  resolvedAgentId: await resolveAgentId(route, role, { userAgent: ship.userAgent })
@@ -13844,7 +13877,7 @@ ${changed2.map((c) => ` - ${c}
13844
13877
  cycle: wemit.cycle,
13845
13878
  ticket: w.decision.actionable.top?.issue_id,
13846
13879
  repo: repoTargetFor(w.route, w.decision.actionable.top?.repo_id)?.name,
13847
- divergedPrompt: await fetchDivergedPrompt(w.route, w.role, { userAgent: cfg.ship.userAgent }),
13880
+ ...personaPromptOptions(await fetchPersonaPrompt(w.route, w.role, { userAgent: cfg.ship.userAgent })),
13848
13881
  skillOverrides: await fetchSkillOverrides(w.route, { userAgent: cfg.ship.userAgent }),
13849
13882
  agentPersona: await fetchSeatPersona(w.route, w.role, { userAgent: cfg.ship.userAgent }),
13850
13883
  resolvedAgentId: await resolveAgentId(w.route, w.role, { userAgent: cfg.ship.userAgent })
@@ -13941,7 +13974,7 @@ ${changed2.map((c) => ` - ${c}
13941
13974
  cycle: wemit.cycle,
13942
13975
  ticket: fleetTicketHint,
13943
13976
  repo: fleetTicketRepo,
13944
- divergedPrompt: await fetchDivergedPrompt(w.route, w.role, { userAgent: cfg.ship.userAgent }),
13977
+ ...personaPromptOptions(await fetchPersonaPrompt(w.route, w.role, { userAgent: cfg.ship.userAgent })),
13945
13978
  skillOverrides: await fetchSkillOverrides(w.route, { userAgent: cfg.ship.userAgent }),
13946
13979
  agentPersona: await fetchSeatPersona(w.route, w.role, { userAgent: cfg.ship.userAgent }),
13947
13980
  resolvedAgentId: await resolveAgentId(w.route, w.role, { userAgent: cfg.ship.userAgent })
@@ -14150,7 +14183,7 @@ ${changed2.map((c) => ` - ${c}
14150
14183
  cycle: emit.cycle,
14151
14184
  ticket: ticketHint,
14152
14185
  repo: ticketRepo,
14153
- divergedPrompt: await fetchDivergedPrompt(route, current, { userAgent: cfg.ship.userAgent }),
14186
+ ...personaPromptOptions(await fetchPersonaPrompt(route, current, { userAgent: cfg.ship.userAgent })),
14154
14187
  skillOverrides: await fetchSkillOverrides(route, { userAgent: cfg.ship.userAgent }),
14155
14188
  agentPersona: await fetchSeatPersona(route, current, { userAgent: cfg.ship.userAgent }),
14156
14189
  resolvedAgentId: await resolveAgentId(route, current, { userAgent: cfg.ship.userAgent })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.48.0",
3
+ "version": "0.49.1",
4
4
  "description": "A standing team of headless agents that picks work off a Tablation board, does it on a machine you control, and reports back.",
5
5
  "type": "module",
6
6
  "license": "MIT",