@tablation/crew 0.29.0 → 0.30.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.
package/dist/cli.js CHANGED
@@ -200,11 +200,11 @@ function extractResult(line) {
200
200
  return {
201
201
  sessionId: obj.session_id,
202
202
  numTurns: obj.num_turns,
203
- totalCostUsd: obj.total_cost_usd,
203
+ costUsd: obj.total_cost_usd,
204
204
  inputTokens: obj.usage?.input_tokens,
205
205
  outputTokens: obj.usage?.output_tokens,
206
206
  cacheReadTokens: obj.usage?.cache_read_input_tokens,
207
- cacheCreationTokens: obj.usage?.cache_creation_input_tokens
207
+ cacheWriteTokens: obj.usage?.cache_creation_input_tokens
208
208
  };
209
209
  }
210
210
 
@@ -376,6 +376,7 @@ var claudeAdapter = {
376
376
  ...spec.env
377
377
  };
378
378
  },
379
+ provider: () => "anthropic",
379
380
  unsetEnv: BILLING_VARS_TO_UNSET,
380
381
  mapStreamLine,
381
382
  extractResult,
@@ -6131,6 +6132,13 @@ function renderEnvironment(i) {
6131
6132
  }
6132
6133
 
6133
6134
  // src/agent-log.ts
6135
+ var AgentLogHttpError = class extends Error {
6136
+ status;
6137
+ constructor(message, status2) {
6138
+ super(message);
6139
+ this.status = status2;
6140
+ }
6141
+ };
6134
6142
  async function post(target, path, body) {
6135
6143
  const url = `${target.baseUrl.replace(/\/+$/, "")}/api/workspaces/${target.workspaceId}${path}`;
6136
6144
  const res = await fetch(url, {
@@ -6143,11 +6151,11 @@ async function post(target, path, body) {
6143
6151
  },
6144
6152
  body: JSON.stringify(body)
6145
6153
  });
6146
- if (!res.ok) throw new Error(`${path}: ${res.status} ${res.statusText}`);
6154
+ if (!res.ok) throw new AgentLogHttpError(`${path}: ${res.status} ${res.statusText}`, res.status);
6147
6155
  return await res.json();
6148
6156
  }
6149
6157
  async function reportAgentRun(target, run2) {
6150
- const entry = await post(target, "/agents/log", {
6158
+ const body = {
6151
6159
  agentId: target.agentId,
6152
6160
  ticketReference: run2.ticketReference,
6153
6161
  outcome: run2.outcome,
@@ -6163,7 +6171,14 @@ async function reportAgentRun(target, run2) {
6163
6171
  costUsd: run2.costUsd,
6164
6172
  promptVersion: run2.promptVersion,
6165
6173
  promptSha: run2.promptSha
6166
- });
6174
+ };
6175
+ let entry;
6176
+ try {
6177
+ entry = await post(target, "/agents/log", { ...body, harness: run2.harness, provider: run2.provider });
6178
+ } catch (e) {
6179
+ if (!(e instanceof AgentLogHttpError) || e.status !== 400 || !run2.harness && !run2.provider) throw e;
6180
+ entry = await post(target, "/agents/log", body);
6181
+ }
6167
6182
  for (const cycle of run2.cycles) {
6168
6183
  await post(target, `/agents/log/${entry.id}/cycles`, {
6169
6184
  cycleIndex: cycle.cycleIndex,
@@ -6212,6 +6227,7 @@ var TicketAttributionTracker = class {
6212
6227
  // src/agent.ts
6213
6228
  var DIGEST_MAX_AGE_SECONDS = 600;
6214
6229
  var LEGACY_TOOLCHAIN_HARNESS = "claude";
6230
+ var LEGACY_TOOLCHAIN_PROVIDER = "anthropic";
6215
6231
  function scrubbedEnv(env = process.env, harness = LEGACY_TOOLCHAIN_HARNESS) {
6216
6232
  const out = { ...env };
6217
6233
  for (const v of adapterFor(harness).unsetEnv) delete out[v];
@@ -6288,6 +6304,7 @@ function planAgentRun(o) {
6288
6304
  args: invocation.args,
6289
6305
  toolchain: toolchain.name,
6290
6306
  harness: toolchain.harness,
6307
+ provider: adapter.provider(toolchain),
6291
6308
  resolution: { toolchain: seat.toolchainWhy, model: seat.modelWhy },
6292
6309
  warnings: seat.warnings,
6293
6310
  prompt,
@@ -6440,6 +6457,11 @@ function spawnAgent(plan, emit) {
6440
6457
  await Promise.all([endSink(rawSink), endSink(eventsSink)]);
6441
6458
  if (spawnFailed) return;
6442
6459
  const attributedTicket = attribution.winner(plan.ticket);
6460
+ const identity = {
6461
+ toolchain: plan.toolchain ?? LEGACY_TOOLCHAIN_HARNESS,
6462
+ harness: adapter.harness,
6463
+ provider: plan.provider ?? LEGACY_TOOLCHAIN_PROVIDER
6464
+ };
6443
6465
  try {
6444
6466
  writeFileSync4(`${plan.streamPath}.meta.json`, JSON.stringify({
6445
6467
  route: plan.route,
@@ -6449,9 +6471,10 @@ function spawnAgent(plan, emit) {
6449
6471
  startedAt,
6450
6472
  exitCode: code ?? 1,
6451
6473
  model: plan.model,
6474
+ ...identity,
6452
6475
  sessionId: result?.sessionId ?? null,
6453
6476
  numTurns: result?.numTurns ?? null,
6454
- totalCostUsd: result?.totalCostUsd ?? null
6477
+ totalCostUsd: result?.costUsd ?? null
6455
6478
  }));
6456
6479
  } catch {
6457
6480
  }
@@ -6460,9 +6483,10 @@ function spawnAgent(plan, emit) {
6460
6483
  data: {
6461
6484
  ms: ms2,
6462
6485
  code: code ?? 1,
6486
+ ...identity,
6463
6487
  ...result?.sessionId ? { sessionId: result.sessionId } : {},
6464
6488
  ...result?.numTurns !== void 0 ? { numTurns: result.numTurns } : {},
6465
- ...result?.totalCostUsd !== void 0 ? { totalCostUsd: result.totalCostUsd } : {}
6489
+ ...result?.costUsd !== void 0 ? { totalCostUsd: result.costUsd } : {}
6466
6490
  }
6467
6491
  });
6468
6492
  if (plan.agentLog) {
@@ -6477,8 +6501,10 @@ function spawnAgent(plan, emit) {
6477
6501
  tokensIn: result?.inputTokens,
6478
6502
  tokensOut: result?.outputTokens,
6479
6503
  cacheReadTokens: result?.cacheReadTokens,
6480
- cacheWriteTokens: result?.cacheCreationTokens,
6481
- costUsd: result?.totalCostUsd,
6504
+ cacheWriteTokens: result?.cacheWriteTokens,
6505
+ costUsd: result?.costUsd,
6506
+ harness: identity.harness,
6507
+ provider: identity.provider,
6482
6508
  promptVersion: plan.promptVersion,
6483
6509
  promptSha: plan.promptSha
6484
6510
  });
@@ -12697,7 +12723,7 @@ ${changed2.map((c) => ` - ${c}
12697
12723
  }
12698
12724
  for (const e of entries) {
12699
12725
  process.stdout.write(
12700
- `${e.id} ${e.started_at ?? "?"} ${e.outcome ?? "?"}${e.ticket_reference ? ` ${e.ticket_reference}` : ""}${e.model ? ` ${e.model}` : ""}
12726
+ `${e.id} ${e.started_at ?? "?"} ${e.outcome ?? "?"}${e.ticket_reference ? ` ${e.ticket_reference}` : ""}${e.model ? ` ${e.model}` : ""}${e.harness || e.provider ? ` (${[e.harness, e.provider].filter(Boolean).join(" \xB7 ")})` : ""}
12701
12727
  `
12702
12728
  );
12703
12729
  }
@@ -12713,6 +12739,8 @@ finished: ${entry.finished_at ?? "?"}
12713
12739
  outcome: ${entry.outcome ?? "?"}
12714
12740
  ticket: ${entry.ticket_reference ?? "(none)"}
12715
12741
  model: ${entry.model ?? "?"}
12742
+ harness: ${entry.harness ?? "?"}
12743
+ provider: ${entry.provider ?? "?"}
12716
12744
  `
12717
12745
  );
12718
12746
  if (flag("prompt")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.29.0",
3
+ "version": "0.30.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",
@@ -131,7 +131,7 @@ Don't reopen it, don't re-verify it, don't merge it.
131
131
  **A ticket assigned to any row the roster lists as a hold is an active
132
132
  hold: skip it entirely, do not read its worktree, do not `cd` into it, do
133
133
  not touch its branch.** That is a person, directly or through an
134
- interactive Claude session, actively iterating on it right now; assignee_id
134
+ interactive session, actively iterating on it right now; assignee_id
135
135
  is the live hand-off signal, checked *before* anything else in this step.
136
136
  This is not the same thing as "nothing new to act on" below — an
137
137
  actively-held ticket isn't yours to evaluate at all this run. **A held
@@ -483,7 +483,7 @@ if `report_type` is either of those.
483
483
  See `CHANGELOG.md`'s own "How this file is maintained" header for the
484
484
  full mechanics of how the release phase turns these into the entry.
485
485
  **User-facing documentation.** If the repository's own instructions (its
486
- CLAUDE.md or equivalent) name a user-facing documentation location, a
486
+ CLAUDE.md, AGENTS.md, GEMINI.md or equivalent) name a user-facing documentation location, a
487
487
  change to a user-facing surface updates the relevant documentation there
488
488
  **in the same commit as the change**. If the repository names no such
489
489
  location, this rule is a no-op — never invent a docs location of your own.
@@ -44,10 +44,13 @@ stopping for this run, do the following instead:
44
44
  - **3.4a — Design before pixels.** Work the surface out per the designer
45
45
  brief: read the components already rendering that area, settle the
46
46
  interaction model in words (default / empty / loading / error / narrow
47
- width / keyboard path / destructive path), then make it visual. Load the
48
- `design` skill for anything with real layout to settle — a new screen,
47
+ width / keyboard path / destructive path), then make it visual. If your harness provides a
48
+ `design` skill and a canvas (Claude Code's `Skill` and `Artifact` tools),
49
+ load the skill for anything with real layout to settle — a new screen,
49
50
  panel, or flow, or a redesign; skip the canvas for a single-component
50
- tweak and mock it statically instead. If it genuinely helps to try an
51
+ tweak and mock it statically instead. Without them (Codex, Gemini and
52
+ other harnesses), work in the edit profile: no canvas, mock statically
53
+ and rely on the PNG in 3.4b. If it genuinely helps to try an
51
54
  interaction out in code first — a real worktree, in whatever stack this
52
55
  repo actually uses — that's fine as a spike to inform the comp, but it is
53
56
  scratch work: it never gets committed as the fix, and this ticket never
@@ -60,9 +63,9 @@ stopping for this run, do the following instead:
60
63
  repo's own front-end stack, then upload it with the recipe under
61
64
  "Attaching a file to a comment" in the Environment section. The PNG
62
65
  upload is a required part of a finished design pass; a text wireframe
63
- alone does not satisfy this step. If a canvas Artifact published, include
64
- its URL in the comment body too, but never *only* the URL — the Artifact
65
- canvas is optional, the PNG is not. If the upload itself fails, post the
66
+ alone does not satisfy this step. If a canvas Artifact published (only where your harness provides
67
+ one), include its URL in the comment body too, but never *only* the URL — the canvas
68
+ is optional, the PNG is not. If the upload itself fails, post the
66
69
  HTTP status and response body in the comment rather than a sentence
67
70
  saying uploads are impossible.
68
71
  - **3.4c — Hand it to the operator, always, one of two ways.** Every design
@@ -20,7 +20,8 @@ interactive session editing files there does too, and it has no cycle
20
20
  boundary to make the collision visible until something breaks. If you
21
21
  already edited a tracked file directly in the primary checkout before
22
22
  realizing this, stash it, create the worktree, and re-apply the stash there
23
- — don't just leave the change on `main`. Read your own project's CLAUDE.md
23
+ — don't just leave the change on `main`. Read your own project's agent
24
+ instructions file (CLAUDE.md, AGENTS.md, GEMINI.md or equivalent)
24
25
  and any repo-specific instructions the person has already given you in this
25
26
  conversation; where they conflict with the general conventions below, they
26
27
  win.
@@ -111,7 +111,7 @@ human review, so "probably fine" is not a pass.
111
111
  comment. Red text in a run that exits 0 with every test passing is not a
112
112
  failure.
113
113
  7. **Check the documentation, when the repository names a place for it.** If
114
- the repository's own instructions (its CLAUDE.md or equivalent) name a
114
+ the repository's own instructions (its CLAUDE.md, AGENTS.md, GEMINI.md or equivalent) name a
115
115
  user-facing documentation location, a ticket that changes a user-facing
116
116
  surface must come with a matching update there, in the same branch.
117
117
  Missing or stale docs are a reason to bounce the ticket back to the dev
@@ -129,7 +129,7 @@ Don't reopen it, don't re-verify it, don't merge it.
129
129
  **A ticket assigned to any row the roster lists as a hold is an active
130
130
  hold: skip it entirely, do not read its worktree, do not `cd` into it, do
131
131
  not touch its branch.** That is a person, directly or through an
132
- interactive Claude session, actively iterating on it right now; assignee_id
132
+ interactive session, actively iterating on it right now; assignee_id
133
133
  is the live hand-off signal, checked *before* anything else in this step.
134
134
  This is not the same thing as "nothing new to act on" below — an
135
135
  actively-held ticket isn't yours to evaluate at all this run. **A held
@@ -465,7 +465,7 @@ if `report_type` is either of those.
465
465
  See `CHANGELOG.md`'s own "How this file is maintained" header for the
466
466
  full mechanics of how the release phase turns these into the entry.
467
467
  **User-facing documentation.** If the repository's own instructions (its
468
- CLAUDE.md or equivalent) name a user-facing documentation location, a
468
+ CLAUDE.md, AGENTS.md, GEMINI.md or equivalent) name a user-facing documentation location, a
469
469
  change to a user-facing surface updates the relevant documentation there
470
470
  **in the same commit as the change**. If the repository names no such
471
471
  location, this rule is a no-op — never invent a docs location of your own.
@@ -20,7 +20,8 @@ interactive session editing files there does too, and it has no cycle
20
20
  boundary to make the collision visible until something breaks. If you
21
21
  already edited a tracked file directly in the primary checkout before
22
22
  realizing this, stash it, create the worktree, and re-apply the stash there
23
- — don't just leave the change on `main`. Read your own project's CLAUDE.md
23
+ — don't just leave the change on `main`. Read your own project's agent
24
+ instructions file (CLAUDE.md, AGENTS.md, GEMINI.md or equivalent)
24
25
  and any repo-specific instructions the person has already given you in this
25
26
  conversation; where they conflict with the general conventions below, they
26
27
  win.
@@ -98,7 +98,7 @@ human review, so "probably fine" is not a pass.
98
98
  the ticket untouched; an undecided ticket at `qa` just gets re-picked
99
99
  next cycle and re-run from scratch.
100
100
  7. **Check the documentation, when the repository names a place for it.** If
101
- the repository's own instructions (its CLAUDE.md or equivalent) name a
101
+ the repository's own instructions (its CLAUDE.md, AGENTS.md, GEMINI.md or equivalent) name a
102
102
  user-facing documentation location, a ticket that changes a user-facing
103
103
  surface must come with a matching update there, in the same branch.
104
104
  Missing or stale docs are a reason to bounce the ticket back to the dev