harnessed 4.1.1 → 4.1.3

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.mjs CHANGED
@@ -186,6 +186,20 @@ var init_schemaVersion = __esm({
186
186
  ]);
187
187
  }
188
188
  });
189
+ async function writeFileAtomic(path, data) {
190
+ const tmp = `${path}.tmp`;
191
+ await writeFile(tmp, data, "utf8");
192
+ await rename(tmp, path);
193
+ }
194
+ function writeFileSyncAtomic(path, data) {
195
+ const tmp = `${path}.tmp`;
196
+ writeFileSync(tmp, data, "utf8");
197
+ renameSync(tmp, path);
198
+ }
199
+ var init_atomicWrite = __esm({
200
+ "src/checkpoint/atomicWrite.ts"() {
201
+ }
202
+ });
189
203
  var CheckpointStatus, CheckpointV1;
190
204
  var init_checkpoint_v1 = __esm({
191
205
  "src/checkpoint/schema/checkpoint.v1.ts"() {
@@ -305,7 +319,7 @@ async function writeCurrentWorkflow(s) {
305
319
  const path = statePath();
306
320
  await mkdir(dirname(path), { recursive: true });
307
321
  await withLock(async () => {
308
- await writeFile(path, JSON.stringify(s, null, 2), "utf8");
322
+ await writeFileAtomic(path, JSON.stringify(s, null, 2));
309
323
  });
310
324
  }
311
325
  async function activate(phase, checkpointPath = null) {
@@ -332,6 +346,7 @@ var init_state = __esm({
332
346
  "src/checkpoint/state.ts"() {
333
347
  init_harnessedRoot();
334
348
  init_schemaVersion();
349
+ init_atomicWrite();
335
350
  init_schema();
336
351
  WorkflowStateError = class extends Error {
337
352
  constructor(message) {
@@ -375,13 +390,14 @@ function writeCheckpoint(c, customPath) {
375
390
  const enforced = enforceBudget(c);
376
391
  const path = join(harnessedSubdir("checkpoints"), `${enforced.phase}.json`);
377
392
  mkdirSync(dirname(path), { recursive: true });
378
- writeFileSync(path, JSON.stringify(enforced, null, 2), "utf8");
393
+ writeFileSyncAtomic(path, JSON.stringify(enforced, null, 2));
379
394
  return path;
380
395
  }
381
396
  var BUDGET_TOKEN, CheckpointTooLargeError, CheckpointWriteError;
382
397
  var init_template = __esm({
383
398
  "src/checkpoint/template.ts"() {
384
399
  init_harnessedRoot();
400
+ init_atomicWrite();
385
401
  init_schema();
386
402
  BUDGET_TOKEN = 1e3;
387
403
  CheckpointTooLargeError = class extends Error {
@@ -1271,7 +1287,7 @@ var init_auto_install = __esm({
1271
1287
 
1272
1288
  // package.json
1273
1289
  var package_default = {
1274
- version: "4.1.1"};
1290
+ version: "4.1.3"};
1275
1291
 
1276
1292
  // src/manifest/errors.ts
1277
1293
  function instancePathToKeyPath(instancePath) {
@@ -2572,23 +2588,18 @@ async function resolveJudgmentGate(gateRef, context, packageRoot) {
2572
2588
  }
2573
2589
  return evalGate(expr, context);
2574
2590
  }
2575
- function getPackageRoot() {
2576
- const thisFile = fileURLToPath(import.meta.url);
2577
- const thisDir = dirname(thisFile);
2578
- if (thisDir.endsWith("dist") || thisDir.replace(/\\/g, "/").endsWith("/dist")) {
2579
- return resolve(thisDir, "..");
2580
- }
2581
- return resolve(thisDir, "..", "..", "..");
2582
- }
2583
2591
 
2584
- // src/cli/gates.ts
2585
- var VALID_MASTERS = /* @__PURE__ */ new Set(["auto", "discuss", "plan", "task", "verify"]);
2586
- var STAGE_MASTERS = /* @__PURE__ */ new Set(["discuss", "plan", "task", "verify"]);
2587
- var PARALLELISM_GATE = "judgments.parallelism-gate.agent-teams-upgrade.fires";
2588
- function defaultContext(task, stage) {
2592
+ // src/cli/lib/gateContext.ts
2593
+ function buildDefaultGateContext(task, stage) {
2589
2594
  return {
2590
2595
  task,
2591
2596
  user_understanding_unclear: false,
2597
+ // v4.1.2 — parallelism-gate.agent-teams-upgrade team-routing facts (default off).
2598
+ teammate_send_message_needed: false,
2599
+ subagent_context_overflow: false,
2600
+ shared_task_list: false,
2601
+ opposing_hypothesis_debate: false,
2602
+ fullstack_three_way: false,
2592
2603
  phase: {
2593
2604
  stage,
2594
2605
  is_critical_module: true,
@@ -2627,6 +2638,30 @@ function defaultContext(task, stage) {
2627
2638
  }
2628
2639
  };
2629
2640
  }
2641
+ function mergeGateContext(base, extra) {
2642
+ const merged = { ...base };
2643
+ for (const [k, v] of Object.entries(extra)) {
2644
+ if ((k === "phase" || k === "subtask") && v && typeof v === "object" && !Array.isArray(v)) {
2645
+ merged[k] = { ...base[k], ...v };
2646
+ } else {
2647
+ merged[k] = v;
2648
+ }
2649
+ }
2650
+ return merged;
2651
+ }
2652
+ function getPackageRoot() {
2653
+ const thisFile = fileURLToPath(import.meta.url);
2654
+ const thisDir = dirname(thisFile);
2655
+ if (thisDir.endsWith("dist") || thisDir.replace(/\\/g, "/").endsWith("/dist")) {
2656
+ return resolve(thisDir, "..");
2657
+ }
2658
+ return resolve(thisDir, "..", "..", "..");
2659
+ }
2660
+
2661
+ // src/cli/gates.ts
2662
+ var VALID_MASTERS = /* @__PURE__ */ new Set(["auto", "discuss", "plan", "task", "verify"]);
2663
+ var STAGE_MASTERS = /* @__PURE__ */ new Set(["discuss", "plan", "task", "verify"]);
2664
+ var PARALLELISM_GATE = "judgments.parallelism-gate.agent-teams-upgrade.fires";
2630
2665
  function resolveMasterYamlPath(master, packageRoot) {
2631
2666
  return master === "auto" ? resolve(packageRoot, "workflows", "auto", "workflow.yaml") : resolve(packageRoot, "workflows", master, "auto", "workflow.yaml");
2632
2667
  }
@@ -2670,7 +2705,7 @@ function registerGates(program2) {
2670
2705
  const clauses = delegates;
2671
2706
  const task = typeof raw.task === "string" ? raw.task : "";
2672
2707
  const stage = master;
2673
- const ctx = defaultContext(task, stage);
2708
+ let ctx = buildDefaultGateContext(task, stage);
2674
2709
  if (typeof raw.context === "string" && raw.context.length > 0) {
2675
2710
  let extra;
2676
2711
  try {
@@ -2680,15 +2715,14 @@ function registerGates(program2) {
2680
2715
  process.exit(1);
2681
2716
  return;
2682
2717
  }
2683
- if (extra && typeof extra === "object") {
2684
- Object.assign(ctx, extra);
2718
+ if (extra && typeof extra === "object" && !Array.isArray(extra)) {
2719
+ ctx = mergeGateContext(ctx, extra);
2685
2720
  }
2686
2721
  }
2687
2722
  const skipSubs = new Set(
2688
2723
  typeof raw.skipSub === "string" && raw.skipSub.length > 0 ? raw.skipSub.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : []
2689
2724
  );
2690
2725
  ctx.skip_subs = [...skipSubs];
2691
- ctx.task = task;
2692
2726
  const fire = [];
2693
2727
  const skip = [];
2694
2728
  for (const clause of clauses) {
@@ -2700,13 +2734,13 @@ function registerGates(program2) {
2700
2734
  continue;
2701
2735
  }
2702
2736
  if (!clause.gate) {
2703
- fire.push(fireEntry(clause));
2737
+ fire.push(fireEntry(clause, master));
2704
2738
  continue;
2705
2739
  }
2706
2740
  try {
2707
2741
  const passes = await resolveJudgmentGate(clause.gate, ctx, packageRoot);
2708
2742
  if (passes) {
2709
- fire.push(fireEntry(clause));
2743
+ fire.push(fireEntry(clause, master));
2710
2744
  } else {
2711
2745
  skip.push({ sub: clause.sub, reason: `gate ${clause.gate} = false` });
2712
2746
  }
@@ -2714,7 +2748,7 @@ function registerGates(program2) {
2714
2748
  console.warn(
2715
2749
  `\u26A0\uFE0F master ${master} sub ${clause.sub} gate ${clause.gate} eval failed (${e.message}); firing sub as if gate=true (ADR 0029 fail-soft).`
2716
2750
  );
2717
- fire.push(fireEntry(clause));
2751
+ fire.push(fireEntry(clause, master));
2718
2752
  }
2719
2753
  }
2720
2754
  let parallelism = { escalate_to_teams: false, reason: null };
@@ -2729,14 +2763,14 @@ function registerGates(program2) {
2729
2763
  process.exit(0);
2730
2764
  });
2731
2765
  }
2732
- function fireEntry(clause) {
2733
- const entry = { sub: clause.sub };
2766
+ function fireEntry(clause, master) {
2767
+ const isMaster = STAGE_MASTERS.has(clause.sub);
2768
+ const sub = master !== "auto" && !isMaster ? `${master}-${clause.sub}` : clause.sub;
2769
+ const entry = { sub };
2734
2770
  if (clause.order !== void 0) entry.order = clause.order;
2735
2771
  if (clause.mode !== void 0) entry.mode = clause.mode;
2736
2772
  if (clause.gate !== void 0) entry.gate = clause.gate;
2737
- if (STAGE_MASTERS.has(clause.sub)) {
2738
- entry.is_master = true;
2739
- }
2773
+ if (isMaster) entry.is_master = true;
2740
2774
  return entry;
2741
2775
  }
2742
2776
  var DURATION_RE = /^(\d+)([dhmw])$/;
@@ -2790,7 +2824,7 @@ ${t("gc.invalid_duration.fix")}`
2790
2824
  return;
2791
2825
  }
2792
2826
  const cutoff = Date.now() - olderMs;
2793
- const kept = new Set(dirs.slice(-keepLast));
2827
+ const kept = new Set(keepLast > 0 ? dirs.slice(-keepLast) : []);
2794
2828
  const candidates = [];
2795
2829
  for (const ts of dirs) {
2796
2830
  if (kept.has(ts)) continue;
@@ -4537,7 +4571,7 @@ function nameToYamlHintPath(name) {
4537
4571
  }
4538
4572
  return `${name}/workflow.yaml`;
4539
4573
  }
4540
- var HARNESSED_MARKER_RX = /<!--\s*harnessed-generated:v3\.4\.\d+\s*-->/;
4574
+ var HARNESSED_MARKER_RX = /<!--\s*harnessed-generated:v\d+\.\d+\.\d+\s*-->/;
4541
4575
  var V3_4_3_SIGNATURE_SUB_RX = /\*\*Preferred path\*\*[\s\S]*use the SlashCommand tool[\s\S]*\*\*Fallback path\*\*[\s\S]*use the Task tool to spawn/;
4542
4576
  var V3_4_3_SIGNATURE_MASTER_RX = /\*\*Preferred path\*\*[\s\S]*dispatch to the per-sub-workflow/;
4543
4577
  function shouldOverwriteFile(content) {
@@ -5801,63 +5835,7 @@ function registerRun(program2) {
5801
5835
  }
5802
5836
  const stage = name.includes("-") ? name.split("-")[0] ?? "" : name;
5803
5837
  const gateContext = {
5804
- task,
5805
- // v3.9.24 — top-level fact set by runAutoPreFlight() when /auto super-master
5806
- // Phase 0.5 understanding-check prompt fires. CLI path skips preflight
5807
- // (no readline hooks), so default to false → research sub skips by default.
5808
- user_understanding_unclear: false,
5809
- phase: {
5810
- stage,
5811
- // verify-stage gates
5812
- is_critical_module: true,
5813
- // verify-paranoid fires
5814
- is_final_step: true,
5815
- // verify-simplify fires
5816
- is_major_release: false,
5817
- // verify-multispec only for major
5818
- has_auth_or_secrets: false,
5819
- has_design_changes: false,
5820
- has_ui_changes: false,
5821
- requires_creative_polish: false,
5822
- // plan-stage gates
5823
- is_complex_architecture: true,
5824
- // plan-architecture fires
5825
- // discuss-stage gates
5826
- has_cross_phase_data_flow: true,
5827
- // discuss-phase fires
5828
- open_decisions: 2,
5829
- // ≥2 fires phase-gate
5830
- scope_days: 2,
5831
- // >1 day fires phase-gate
5832
- scope_locked_in_history: false,
5833
- single_task: false,
5834
- type: "general"
5835
- },
5836
- subtask: {
5837
- // subtask brainstorming gate
5838
- approaches: 2,
5839
- // ≥2 fires
5840
- core_algorithm: true,
5841
- has_api_contract: true,
5842
- error_cost: "high",
5843
- lines: 50,
5844
- // ≥20 → no skip
5845
- type: "general",
5846
- // not crud/standard_lib_call → no skip
5847
- // tdd gate
5848
- is_core_business_logic: true,
5849
- is_algorithm: true,
5850
- is_data_processing: true,
5851
- regression_risk: "high",
5852
- reliability_required: true,
5853
- // misc
5854
- communication_needed: false,
5855
- needs_lib_docs: false,
5856
- needs_web_search: false,
5857
- parallel_count: 1,
5858
- search_type: "general",
5859
- test_type: "general"
5860
- },
5838
+ ...buildDefaultGateContext(task, stage),
5861
5839
  ...raw.model ? { modelOverride: raw.model } : {},
5862
5840
  ...raw.maxIterations ? { maxIterations: raw.maxIterations } : {},
5863
5841
  ...raw.staged ? { staged: true } : {},
@@ -5989,14 +5967,17 @@ var LANG_NAMES = {
5989
5967
  "zh-Hant": "\u7E41\u9AD4\u4E2D\u6587 (Traditional Chinese)",
5990
5968
  "zh-TW": "\u7E41\u9AD4\u4E2D\u6587 (Traditional Chinese)"
5991
5969
  };
5970
+ async function loadSubArrayField(sub, packageRoot, field) {
5971
+ const subYaml = await resolveWorkflowYaml(sub, resolve(packageRoot, "workflows"));
5972
+ if (!subYaml) return [];
5973
+ const wf = parse(await readFile(subYaml, "utf8"));
5974
+ const v = wf?.[field];
5975
+ return Array.isArray(v) ? v : [];
5976
+ }
5992
5977
  async function buildToolsSection(sub, packageRoot) {
5993
5978
  try {
5994
5979
  const workflowsDir = resolve(packageRoot, "workflows");
5995
- const subYaml = await resolveWorkflowYaml(sub, workflowsDir);
5996
- if (!subYaml) return "";
5997
- const wfRaw = await readFile(subYaml, "utf8");
5998
- const wf = parse(wfRaw);
5999
- const tools = Array.isArray(wf?.tools_available) ? wf.tools_available : [];
5980
+ const tools = await loadSubArrayField(sub, packageRoot, "tools_available");
6000
5981
  if (tools.length === 0) return "";
6001
5982
  const capRaw = await readFile(resolve(workflowsDir, "capabilities.yaml"), "utf8");
6002
5983
  const capDoc = parse(capRaw);
@@ -6020,11 +6001,7 @@ ${lines.join("\n")}
6020
6001
  async function buildDisciplinesSection(sub, packageRoot) {
6021
6002
  try {
6022
6003
  const workflowsDir = resolve(packageRoot, "workflows");
6023
- const subYaml = await resolveWorkflowYaml(sub, workflowsDir);
6024
- if (!subYaml) return "";
6025
- const wfRaw = await readFile(subYaml, "utf8");
6026
- const wf = parse(wfRaw);
6027
- const applied = Array.isArray(wf?.disciplines_applied) ? wf.disciplines_applied : [];
6004
+ const applied = await loadSubArrayField(sub, packageRoot, "disciplines_applied");
6028
6005
  const names = applied.filter((d) => d !== "language");
6029
6006
  if (names.length === 0) return "";
6030
6007
  const blocks = [];
@@ -6097,7 +6074,7 @@ function registerPrompt(program2) {
6097
6074
  const packageRoot = getPackageRoot();
6098
6075
  const workflowsDir = resolve(packageRoot, "workflows");
6099
6076
  const rolePrompts = await loadRolePrompts(workflowsDir);
6100
- const def = buildAgentDef(sub, rolePrompts, void 0, void 0, void 0);
6077
+ const def = buildAgentDef(sub, rolePrompts);
6101
6078
  const body = def.prompt;
6102
6079
  const taskSection = typeof raw.task === "string" && raw.task.length > 0 ? `## Task
6103
6080
  ${raw.task}
@@ -6121,7 +6098,6 @@ ${toolsSection}${disciplinesSection}${PROTOCOLS}${buildLanguageSection()}`;
6121
6098
  })
6122
6099
  );
6123
6100
  process.exit(0);
6124
- return;
6125
6101
  }
6126
6102
  console.log(fullPrompt);
6127
6103
  process.exit(0);
@@ -6216,21 +6192,21 @@ ${t("rollback.metadata_unreadable.fix")}`
6216
6192
  process.exit(1);
6217
6193
  return;
6218
6194
  }
6219
- for (const entry of [...meta.files].reverse()) {
6195
+ const ordered = [...meta.files].reverse();
6196
+ const planned = [];
6197
+ for (const entry of ordered) {
6220
6198
  if (entry.backup === "") {
6221
- try {
6222
- await unlink(entry.target);
6223
- } catch (err2) {
6224
- const code = err2.code;
6225
- if (code !== "ENOENT") {
6226
- console.error(`error: cannot unlink ${entry.target}: ${err2.message}`);
6227
- process.exit(1);
6228
- return;
6229
- }
6230
- }
6199
+ planned.push({ target: entry.target, action: "unlink" });
6231
6200
  continue;
6232
6201
  }
6233
- const buf = await readFile(entry.backup);
6202
+ let buf;
6203
+ try {
6204
+ buf = await readFile(entry.backup);
6205
+ } catch (err2) {
6206
+ console.error(`error: cannot read backup ${entry.backup}: ${err2.message}`);
6207
+ process.exit(1);
6208
+ return;
6209
+ }
6234
6210
  const sha1 = createHash("sha1").update(buf).digest("hex");
6235
6211
  if (sha1 !== entry.sha1) {
6236
6212
  console.error(
@@ -6243,7 +6219,23 @@ ${t("rollback.metadata_unreadable.fix")}`
6243
6219
  process.exit(1);
6244
6220
  return;
6245
6221
  }
6246
- await writeFile(entry.target, normalizeEol(buf, entry.eol));
6222
+ planned.push({ target: entry.target, action: "write", data: normalizeEol(buf, entry.eol) });
6223
+ }
6224
+ for (const op of planned) {
6225
+ if (op.action === "unlink") {
6226
+ try {
6227
+ await unlink(op.target);
6228
+ } catch (err2) {
6229
+ const code = err2.code;
6230
+ if (code !== "ENOENT") {
6231
+ console.error(`error: cannot unlink ${op.target}: ${err2.message}`);
6232
+ process.exit(1);
6233
+ return;
6234
+ }
6235
+ }
6236
+ } else {
6237
+ await writeFile(op.target, op.data);
6238
+ }
6247
6239
  }
6248
6240
  console.log(t("rollback.restored", { count: meta.files.length, timestamp }));
6249
6241
  });