@cardor/agent-harness-kit 2.3.1 → 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/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  resolveGlobalStorageDir,
13
13
  resolveSqlitePath,
14
14
  resolveSqlitePathForScope
15
- } from "./chunk-JTACLEGM.js";
15
+ } from "./chunk-TWG3KVZQ.js";
16
16
 
17
17
  // src/cli.ts
18
18
  import { Command, InvalidArgumentError } from "commander";
@@ -2735,7 +2735,7 @@ async function runMigrateStorage(cwd2, opts, homeDir = homedir3()) {
2735
2735
  }
2736
2736
  async function probeTaskCount(dbPath) {
2737
2737
  if (!existsSync15(dbPath)) return 0;
2738
- const { SQLiteDriver } = await import("./sqlite-5OWKTUUZ.js");
2738
+ const { SQLiteDriver } = await import("./sqlite-UDXCFIEN.js");
2739
2739
  const driver = new SQLiteDriver(dbPath);
2740
2740
  try {
2741
2741
  await driver.ensureSchema();
@@ -2756,7 +2756,7 @@ async function migrateScopeOnly(cwd2, config, homeDir, fromScope, toScope, opts)
2756
2756
  }
2757
2757
  const destExists = existsSync15(destDb);
2758
2758
  if (destExists) {
2759
- const { SQLiteDriver } = await import("./sqlite-5OWKTUUZ.js");
2759
+ const { SQLiteDriver } = await import("./sqlite-UDXCFIEN.js");
2760
2760
  const destDriver = new SQLiteDriver(destDb);
2761
2761
  let destEmpty;
2762
2762
  try {
@@ -2771,12 +2771,12 @@ async function migrateScopeOnly(cwd2, config, homeDir, fromScope, toScope, opts)
2771
2771
  );
2772
2772
  }
2773
2773
  if (!destEmpty && opts.force) {
2774
- const { SQLiteDriver: Driver } = await import("./sqlite-5OWKTUUZ.js");
2774
+ const { SQLiteDriver: Driver } = await import("./sqlite-UDXCFIEN.js");
2775
2775
  const backupDriver = new Driver(destDb);
2776
2776
  let data;
2777
2777
  try {
2778
2778
  await backupDriver.ensureSchema();
2779
- const { HarnessDB } = await import("./db-L3AADJF5.js");
2779
+ const { HarnessDB } = await import("./db-3B2W2EBA.js");
2780
2780
  const tmpDb = new HarnessDB(backupDriver, config, homeDir);
2781
2781
  data = await tmpDb.exportJson();
2782
2782
  } finally {
@@ -2815,14 +2815,14 @@ async function migrateAcrossDbType(cwd2, config, homeDir, sourceScope, opts) {
2815
2815
  if (!existsSync15(srcPath)) {
2816
2816
  fail(`Source sqlite database not found at ${srcPath} (expected ${sourceScope} scope) \u2014 nothing to migrate.`);
2817
2817
  }
2818
- const { SQLiteDriver } = await import("./sqlite-5OWKTUUZ.js");
2818
+ const { SQLiteDriver } = await import("./sqlite-UDXCFIEN.js");
2819
2819
  const srcDriver = new SQLiteDriver(srcPath);
2820
2820
  let sourceData;
2821
2821
  let sourceCounts;
2822
2822
  try {
2823
2823
  await srcDriver.ensureSchema();
2824
2824
  sourceCounts = await getRowCounts(srcDriver);
2825
- const { HarnessDB } = await import("./db-L3AADJF5.js");
2825
+ const { HarnessDB } = await import("./db-3B2W2EBA.js");
2826
2826
  const srcDb = new HarnessDB(srcDriver, config, homeDir);
2827
2827
  sourceData = await srcDb.exportJson();
2828
2828
  } finally {
@@ -3117,7 +3117,7 @@ var TOOLS = [
3117
3117
  taskId: { type: "number", description: "The task ID from tasks.get" },
3118
3118
  agent: {
3119
3119
  type: "string",
3120
- description: "Agent name: lead | explorer | builder | reviewer | custom:<name>"
3120
+ description: "Agent name: lead | explorer | consultant | builder | reviewer | custom:<name>"
3121
3121
  }
3122
3122
  },
3123
3123
  required: ["taskId", "agent"]
@@ -3156,7 +3156,7 @@ var TOOLS = [
3156
3156
  },
3157
3157
  {
3158
3158
  name: "actions.get",
3159
- description: "Get the full action history for a task (all agents, all sections).",
3159
+ description: "Full task action history, including every action and section. Potentially large; use only for audit or diagnosis.",
3160
3160
  inputSchema: {
3161
3161
  type: "object",
3162
3162
  properties: {
@@ -3165,6 +3165,84 @@ var TOOLS = [
3165
3165
  required: ["taskId"]
3166
3166
  }
3167
3167
  },
3168
+ {
3169
+ name: "actions.list",
3170
+ description: "Compact newest-first action index. Use to discover actions before reading a specific action or section.",
3171
+ inputSchema: {
3172
+ type: "object",
3173
+ properties: {
3174
+ taskId: { type: "number", description: "Task ID" },
3175
+ agent: { type: "string", description: "Optional agent filter" },
3176
+ status: { type: "string", enum: ["in_progress", "completed", "blocked"], description: "Optional action status filter" },
3177
+ cursor: { type: "string", description: "Opaque cursor returned as nextCursor" },
3178
+ limit: { type: "number", description: "Maximum items (1-100; default 20)" }
3179
+ },
3180
+ required: ["taskId"]
3181
+ }
3182
+ },
3183
+ {
3184
+ name: "actions.get_by_id",
3185
+ description: "Get one action and a compact index of its sections; section contents are not included.",
3186
+ inputSchema: { type: "object", properties: { actionId: { type: "number", description: "Action ID" } }, required: ["actionId"] }
3187
+ },
3188
+ {
3189
+ name: "actions.sections.list",
3190
+ description: "Compact newest-first section index for one action. Filter by section types without loading content.",
3191
+ inputSchema: {
3192
+ type: "object",
3193
+ properties: {
3194
+ actionId: { type: "number", description: "Action ID" },
3195
+ types: { type: "array", items: { type: "string" }, description: "Optional section-type filter" },
3196
+ cursor: { type: "string", description: "Opaque cursor returned as nextCursor" },
3197
+ limit: { type: "number", description: "Maximum items (1-100; default 20)" }
3198
+ },
3199
+ required: ["actionId"]
3200
+ }
3201
+ },
3202
+ {
3203
+ name: "actions.sections.get",
3204
+ description: "Read one section content with an explicit character range. This is the only generic action reader that returns long text.",
3205
+ inputSchema: {
3206
+ type: "object",
3207
+ properties: {
3208
+ sectionId: { type: "number", description: "Section ID" },
3209
+ offset: { type: "number", description: "Zero-based character offset (default 0)" },
3210
+ length: { type: "number", description: "Characters to return (1-12000; default 8000)" }
3211
+ },
3212
+ required: ["sectionId"]
3213
+ }
3214
+ },
3215
+ {
3216
+ name: "actions.handoff.write",
3217
+ description: "Write a validated, recipient-directed, bounded handoff for a completed action to resume work without loading the full history.",
3218
+ inputSchema: {
3219
+ type: "object",
3220
+ properties: {
3221
+ actionId: { type: "number", description: "Producer action ID" },
3222
+ recipient: { type: "string", enum: ["lead", "explorer", "consultant", "builder", "reviewer"] },
3223
+ goal: { type: "string" },
3224
+ completed: { type: "array", items: { type: "string" } },
3225
+ decisions: { type: "array", items: { type: "string" } },
3226
+ files: { type: "array", items: { type: "string" } },
3227
+ verification: { type: "array", items: { type: "string" } },
3228
+ blockers: { type: "array", items: { type: "string" } },
3229
+ nextStep: { type: "string" }
3230
+ },
3231
+ required: ["actionId", "recipient", "goal", "completed", "decisions", "files", "verification", "blockers", "nextStep"]
3232
+ }
3233
+ },
3234
+ {
3235
+ name: "actions.handoff.get",
3236
+ description: "Get the newest completed canonical handoff addressed to a recipient. Returns HANDOFF_NOT_FOUND instead of falling back to history.",
3237
+ inputSchema: {
3238
+ type: "object",
3239
+ properties: {
3240
+ taskId: { type: "number", description: "Task ID" },
3241
+ recipient: { type: "string", enum: ["lead", "explorer", "consultant", "builder", "reviewer"], description: "Recipient role; defaults to builder" }
3242
+ },
3243
+ required: ["taskId"]
3244
+ }
3245
+ },
3168
3246
  {
3169
3247
  name: "tasks.get",
3170
3248
  description: "List tasks, optionally filtered by status. Excludes archived tasks by default.",
@@ -3426,6 +3504,7 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
3426
3504
  case "actions.write": {
3427
3505
  const actionId = num(args, "actionId");
3428
3506
  const sectionType = str(args, "sectionType");
3507
+ if (sectionType === "handoff") throw new Error("handoff sections must be written with actions.handoff.write");
3429
3508
  const content = str(args, "content");
3430
3509
  await db.writeSection(actionId, sectionType, content);
3431
3510
  return ok2(JSON.stringify({ recorded: true }));
@@ -3460,6 +3539,56 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
3460
3539
  );
3461
3540
  return ok2(JSON.stringify(full));
3462
3541
  }
3542
+ case "actions.list": {
3543
+ const taskId = num(args, "taskId");
3544
+ const limit = boundedInt(args, "limit", 20, 1, 100);
3545
+ const rows = await db.listActionsForTask(taskId, { agent: optionalAgent(args, "agent"), status: optionalActionStatus(args, "status"), cursor: decodeActionCursor(optionalStr(args, "cursor")), limit });
3546
+ const last = rows.at(-1);
3547
+ return ok2(JSON.stringify({ items: rows.map((row) => ({ id: row.id, agent: row.agent, status: row.status, createdAt: row.created_at, completedAt: row.completed_at, summaryPreview: preview(row.summary), sectionCount: Number(row.section_count) })), nextCursor: rows.length === limit && last ? encodeActionCursor(last.created_at, last.id) : null }));
3548
+ }
3549
+ case "actions.get_by_id": {
3550
+ const actionId = num(args, "actionId");
3551
+ const action = await db.getAction(actionId);
3552
+ if (!action) return ok2(JSON.stringify({ error: "ACTION_NOT_FOUND", actionId }), true);
3553
+ const sections = await db.listActionSections(actionId, { limit: 100 });
3554
+ return ok2(JSON.stringify({ id: action.id, taskId: action.task_id, agent: action.agent, status: action.status, summary: action.summary, createdAt: action.created_at, completedAt: action.completed_at, sections: sections.map(sectionIndex) }));
3555
+ }
3556
+ case "actions.sections.list": {
3557
+ const actionId = num(args, "actionId");
3558
+ const limit = boundedInt(args, "limit", 20, 1, 100);
3559
+ const rows = await db.listActionSections(actionId, { types: optionalStringArray(args, "types"), cursor: decodeSectionCursor(optionalStr(args, "cursor")), limit });
3560
+ const last = rows.at(-1);
3561
+ return ok2(JSON.stringify({ items: rows.map(sectionIndex), nextCursor: rows.length === limit && last ? encodeSectionCursor(last.id) : null }));
3562
+ }
3563
+ case "actions.sections.get": {
3564
+ const sectionId = num(args, "sectionId");
3565
+ const offset = boundedInt(args, "offset", 0, 0, Number.MAX_SAFE_INTEGER);
3566
+ const length = boundedInt(args, "length", 8e3, 1, 12e3);
3567
+ const section = await db.getActionSection(sectionId);
3568
+ if (!section) return ok2(JSON.stringify({ error: "SECTION_NOT_FOUND", sectionId }), true);
3569
+ const content = section.content.slice(offset, offset + length);
3570
+ const nextOffset = offset + content.length;
3571
+ return ok2(JSON.stringify({ sectionId: section.id, type: section.section_type, content, truncated: nextOffset < section.content.length, nextOffset: nextOffset < section.content.length ? nextOffset : null }));
3572
+ }
3573
+ case "actions.handoff.write": {
3574
+ const actionId = num(args, "actionId");
3575
+ const recipient = recipientRole(args["recipient"]);
3576
+ const handoff = handoffFromArgs(args);
3577
+ const serialized = JSON.stringify({ version: 1, recipient, handoff });
3578
+ if (Buffer.byteLength(serialized, "utf8") > 12e3) throw new Error("handoff must not exceed 12000 UTF-8 bytes");
3579
+ await db.writeSection(actionId, "handoff", serialized);
3580
+ return ok2(JSON.stringify({ recorded: true, recipient }));
3581
+ }
3582
+ case "actions.handoff.get": {
3583
+ const taskId = num(args, "taskId");
3584
+ const recipient = recipientRole(args["recipient"] ?? "builder");
3585
+ for (const candidate of await db.getCompletedHandoffSections(taskId)) {
3586
+ const decoded = decodeHandoff(candidate.content);
3587
+ if (decoded?.recipient !== recipient) continue;
3588
+ return ok2(JSON.stringify({ found: true, taskId, sourceActionId: candidate.action_id, sourceAgent: candidate.agent, recipient, createdAt: candidate.created_at, handoff: decoded.handoff }));
3589
+ }
3590
+ return ok2(JSON.stringify({ found: false, reason: "HANDOFF_NOT_FOUND" }));
3591
+ }
3463
3592
  case "tasks.get": {
3464
3593
  const status = args["status"];
3465
3594
  const includeArchived = args["includeArchived"];
@@ -3712,6 +3841,88 @@ function num(args, key) {
3712
3841
  if (typeof v4 !== "number") throw new Error(`${key} must be a number`);
3713
3842
  return v4;
3714
3843
  }
3844
+ function optionalStr(args, key) {
3845
+ const value = args[key];
3846
+ if (value === void 0) return void 0;
3847
+ if (typeof value !== "string") throw new Error(`${key} must be a string`);
3848
+ return value;
3849
+ }
3850
+ function boundedInt(args, key, fallback, min, max) {
3851
+ const value = args[key];
3852
+ if (value === void 0) return fallback;
3853
+ if (typeof value !== "number" || !Number.isInteger(value) || value < min || value > max) throw new Error(`${key} must be an integer between ${min} and ${max}`);
3854
+ return value;
3855
+ }
3856
+ function optionalStringArray(args, key) {
3857
+ const value = args[key];
3858
+ if (value === void 0) return void 0;
3859
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.length === 0)) throw new Error(`${key} must be an array of non-empty strings`);
3860
+ return value;
3861
+ }
3862
+ var RECIPIENTS = ["lead", "explorer", "consultant", "builder", "reviewer"];
3863
+ function recipientRole(value) {
3864
+ if (typeof value !== "string" || !RECIPIENTS.includes(value)) throw new Error(`recipient must be one of: ${RECIPIENTS.join(", ")}`);
3865
+ return value;
3866
+ }
3867
+ function requiredStringArray(args, key) {
3868
+ if (!(key in args)) throw new Error(`${key} is required`);
3869
+ return optionalStringArray(args, key) ?? [];
3870
+ }
3871
+ function handoffFromArgs(args) {
3872
+ const goal = str(args, "goal");
3873
+ const nextStep = str(args, "nextStep");
3874
+ if (!goal.trim() || !nextStep.trim()) throw new Error("goal and nextStep must be non-empty strings");
3875
+ return { goal, completed: requiredStringArray(args, "completed"), decisions: requiredStringArray(args, "decisions"), files: requiredStringArray(args, "files"), verification: requiredStringArray(args, "verification"), blockers: requiredStringArray(args, "blockers"), nextStep };
3876
+ }
3877
+ function decodeHandoff(content) {
3878
+ try {
3879
+ const value = JSON.parse(content);
3880
+ if (value.version !== 1 || !value.handoff) return null;
3881
+ return { recipient: recipientRole(value.recipient), handoff: handoffFromArgs(value.handoff) };
3882
+ } catch {
3883
+ return null;
3884
+ }
3885
+ }
3886
+ function optionalAgent(args, key) {
3887
+ const value = optionalStr(args, key);
3888
+ if (value === void 0) return void 0;
3889
+ if (!["lead", "explorer", "consultant", "builder", "reviewer"].includes(value) && !value.startsWith("custom:")) throw new Error("agent must be a known role or custom:<name>");
3890
+ return value;
3891
+ }
3892
+ function optionalActionStatus(args, key) {
3893
+ const value = optionalStr(args, key);
3894
+ if (value === void 0) return void 0;
3895
+ if (!["in_progress", "completed", "blocked"].includes(value)) throw new Error("status must be in_progress, completed, or blocked");
3896
+ return value;
3897
+ }
3898
+ function encodeActionCursor(createdAt, id) {
3899
+ return Buffer.from(JSON.stringify({ createdAt, id })).toString("base64url");
3900
+ }
3901
+ function decodeActionCursor(cursor) {
3902
+ if (!cursor) return void 0;
3903
+ try {
3904
+ const value = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
3905
+ if (typeof value.createdAt !== "string" || typeof value.id !== "number" || !Number.isInteger(value.id)) throw new Error();
3906
+ return { createdAt: value.createdAt, id: value.id };
3907
+ } catch {
3908
+ throw new Error("cursor is invalid");
3909
+ }
3910
+ }
3911
+ function encodeSectionCursor(id) {
3912
+ return Buffer.from(String(id)).toString("base64url");
3913
+ }
3914
+ function decodeSectionCursor(cursor) {
3915
+ if (!cursor) return void 0;
3916
+ const id = Number(Buffer.from(cursor, "base64url").toString("utf8"));
3917
+ if (!Number.isInteger(id) || id < 1) throw new Error("cursor is invalid");
3918
+ return id;
3919
+ }
3920
+ function preview(summary) {
3921
+ return summary ? summary.slice(0, 240) : null;
3922
+ }
3923
+ function sectionIndex(section) {
3924
+ return { id: section.id, type: section.section_type, chars: Number(section.chars), createdAt: section.created_at };
3925
+ }
3715
3926
  function nonEmptyArray(args, key) {
3716
3927
  const v4 = args[key];
3717
3928
  if (!Array.isArray(v4) || v4.length === 0) {