@brainervirus/workit-codex 1.0.9 → 1.0.10

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.
@@ -6626,8 +6626,8 @@ import path15 from "node:path";
6626
6626
 
6627
6627
  // packages/workit-core/src/core.ts
6628
6628
  import { spawnSync as spawnSync2 } from "node:child_process";
6629
- import { existsSync as existsSync3, realpathSync as realpathSync5 } from "node:fs";
6630
- import path6 from "node:path";
6629
+ import { existsSync as existsSync4, realpathSync as realpathSync5 } from "node:fs";
6630
+ import path7 from "node:path";
6631
6631
 
6632
6632
  // packages/workit-core/src/core/task-contract.ts
6633
6633
  import { createHash, randomUUID } from "node:crypto";
@@ -14419,6 +14419,7 @@ var decisionSchema = object({
14419
14419
  scope: scopeSchema,
14420
14420
  presented: text,
14421
14421
  approvedContent: text,
14422
+ displayed: text.optional(),
14422
14423
  contentRefs: array(refSchema)
14423
14424
  }).strict(),
14424
14425
  digest,
@@ -14497,6 +14498,10 @@ var actionProgressListSchema = array(actionProgressSchema).check((ctx) => {
14497
14498
  path: ["decisionId"]
14498
14499
  });
14499
14500
  });
14501
+ var runtimeSchema = object({
14502
+ createdWith: text.nullable(),
14503
+ updatedWith: text
14504
+ }).strict();
14500
14505
  var taskRecordSchema = object({
14501
14506
  schemaVersion: literal(1),
14502
14507
  id,
@@ -14510,6 +14515,8 @@ var taskRecordSchema = object({
14510
14515
  status: _enum(["active", "paused", "closed"]),
14511
14516
  closure: closureSchema.nullable(),
14512
14517
  progress: progressSchema,
14518
+ pauseReason: text.nullable().optional(),
14519
+ runtime: runtimeSchema.optional(),
14513
14520
  assessments: array(entrySchema(assessmentSchema)),
14514
14521
  policy: policySchema.nullable(),
14515
14522
  policyChanges: array(policyChangeSchema),
@@ -14525,6 +14532,7 @@ var workspaceRecordSchema = object({
14525
14532
  id,
14526
14533
  revision,
14527
14534
  root: nonEmpty,
14535
+ runtime: runtimeSchema.optional(),
14528
14536
  writer: object({ state: _enum(["held", "uncertain"]), owner: ownerSchema, acquiredAt: utc }).strict().nullable()
14529
14537
  }).strict();
14530
14538
  var capabilitySchema = object({
@@ -14552,6 +14560,9 @@ var taskSummarySchema = object({
14552
14560
  id,
14553
14561
  revision,
14554
14562
  workspaceRevision: revision,
14563
+ createdAt: utc,
14564
+ updatedAt: utc,
14565
+ runtime: runtimeSchema.nullable(),
14555
14566
  objective: text,
14556
14567
  status: _enum(["active", "paused", "closed"]),
14557
14568
  closure: closureSchema.nullable(),
@@ -14622,7 +14633,7 @@ var taskOperations = {
14622
14633
  expectedWorkspaceRevision: revision.optional(),
14623
14634
  outcome: outcomeSchema,
14624
14635
  summary: text,
14625
- decisionIds: array(id)
14636
+ decisionIds: array(id).optional()
14626
14637
  })
14627
14638
  };
14628
14639
  var policyOperations = {
@@ -14714,14 +14725,15 @@ var writerOperations = {
14714
14725
  ...taskId,
14715
14726
  expectedRevision: revision.optional(),
14716
14727
  expectedWorkspaceRevision: revision.optional(),
14717
- workerId: nullableId.optional()
14728
+ workerId: nullableId.optional(),
14729
+ reason: text.optional()
14718
14730
  }),
14719
14731
  release: operation({
14720
14732
  action: literal("release"),
14721
14733
  ...taskId,
14722
14734
  expectedRevision: revision.optional(),
14723
14735
  expectedWorkspaceRevision: revision.optional(),
14724
- reason: text
14736
+ reason: text.optional()
14725
14737
  })
14726
14738
  };
14727
14739
  var stateOperations = {
@@ -14928,7 +14940,26 @@ function requirementId(input) {
14928
14940
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
14929
14941
  import * as fs4 from "node:fs";
14930
14942
  import { hostname } from "node:os";
14931
- import path3 from "node:path";
14943
+ import path4 from "node:path";
14944
+
14945
+ // packages/workit-core/src/core/package-root.ts
14946
+ import { existsSync } from "node:fs";
14947
+ import path from "node:path";
14948
+ import { fileURLToPath } from "node:url";
14949
+ var packageRoot = () => {
14950
+ let dir = path.dirname(fileURLToPath(import.meta.url));
14951
+ while (true) {
14952
+ const parent = path.dirname(dir);
14953
+ if (existsSync(path.join(dir, "package.json")) || parent === dir)
14954
+ return dir;
14955
+ dir = parent;
14956
+ }
14957
+ };
14958
+ var assetRoot = () => {
14959
+ const root = packageRoot();
14960
+ const assets = path.join(root, "assets");
14961
+ return existsSync(assets) ? assets : root;
14962
+ };
14932
14963
 
14933
14964
  // node_modules/@openclaw/fs-safe/dist/errors.js
14934
14965
  var OPERATIONAL_CODES = new Set([
@@ -14979,7 +15010,7 @@ function sameFileIdentity(left, right, platform = process.platform) {
14979
15010
  // node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
14980
15011
  import { randomBytes } from "node:crypto";
14981
15012
  import fsSync from "node:fs";
14982
- import path from "node:path";
15013
+ import path2 from "node:path";
14983
15014
 
14984
15015
  // node_modules/@openclaw/fs-safe/dist/bounded-read.js
14985
15016
  import fs from "node:fs";
@@ -15069,13 +15100,13 @@ ${ownershipToken}
15069
15100
  return { raw, ownershipToken };
15070
15101
  }
15071
15102
  function relativeSidecarLockPath(lockRoot, lockPath) {
15072
- const resolved = path.resolve(lockPath);
15073
- const lexicalRelative = path.relative(lockRoot.rootDir, resolved);
15074
- const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path.sep}`) && !path.isAbsolute(lexicalRelative) ? lexicalRelative : path.relative(lockRoot.rootReal, resolved);
15075
- if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
15103
+ const resolved = path2.resolve(lockPath);
15104
+ const lexicalRelative = path2.relative(lockRoot.rootDir, resolved);
15105
+ const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(lexicalRelative) ? lexicalRelative : path2.relative(lockRoot.rootReal, resolved);
15106
+ if (!relative || relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative)) {
15076
15107
  throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
15077
15108
  }
15078
- return relative.split(path.sep).join(path.posix.sep);
15109
+ return relative.split(path2.sep).join(path2.posix.sep);
15079
15110
  }
15080
15111
  function parseSidecarLockPayload(raw, parser) {
15081
15112
  if (parser) {
@@ -15222,7 +15253,7 @@ function getFsSafeLockConfig() {
15222
15253
 
15223
15254
  // node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
15224
15255
  import fs3 from "node:fs";
15225
- import path2 from "node:path";
15256
+ import path3 from "node:path";
15226
15257
 
15227
15258
  // node_modules/@openclaw/fs-safe/dist/timing.js
15228
15259
  function sleepSync(ms) {
@@ -15313,26 +15344,26 @@ function canonicalLockParentSync(parent) {
15313
15344
  return process.platform === "win32" ? fs3.realpathSync.native(parent) : fs3.realpathSync(parent);
15314
15345
  }
15315
15346
  function normalizeTargetPath(targetPath) {
15316
- const resolved = path2.resolve(targetPath);
15317
- fs3.mkdirSync(path2.dirname(resolved), { recursive: true });
15347
+ const resolved = path3.resolve(targetPath);
15348
+ fs3.mkdirSync(path3.dirname(resolved), { recursive: true });
15318
15349
  try {
15319
- return path2.join(canonicalLockParentSync(path2.dirname(resolved)), path2.basename(resolved));
15350
+ return path3.join(canonicalLockParentSync(path3.dirname(resolved)), path3.basename(resolved));
15320
15351
  } catch {
15321
15352
  return resolved;
15322
15353
  }
15323
15354
  }
15324
15355
  function boundedLockPath(lockPath, lockRoot) {
15325
- const resolved = path2.resolve(lockPath);
15356
+ const resolved = path3.resolve(lockPath);
15326
15357
  if (!lockRoot)
15327
15358
  return resolved;
15328
15359
  relativeSidecarLockPath(lockRoot, resolved);
15329
- const parent = path2.dirname(resolved);
15360
+ const parent = path3.dirname(resolved);
15330
15361
  const parentReal = canonicalLockParentSync(parent);
15331
- const parentRelative = path2.relative(lockRoot.rootReal, parentReal);
15332
- if (parentRelative === ".." || parentRelative.startsWith(`..${path2.sep}`) || path2.isAbsolute(parentRelative)) {
15362
+ const parentRelative = path3.relative(lockRoot.rootReal, parentReal);
15363
+ if (parentRelative === ".." || parentRelative.startsWith(`..${path3.sep}`) || path3.isAbsolute(parentRelative)) {
15333
15364
  throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
15334
15365
  }
15335
- return path2.join(parentReal, path2.basename(resolved));
15366
+ return path3.join(parentReal, path3.basename(resolved));
15336
15367
  }
15337
15368
  function defaultShouldReclaim(snapshot, staleMs, nowMs) {
15338
15369
  const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
@@ -15545,6 +15576,33 @@ var metadataLockSchema = object({
15545
15576
  nonce: string2().min(1)
15546
15577
  }).strict();
15547
15578
  var now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
15579
+ var cachedRuntimeVersion = null;
15580
+ var runtimeVersion = () => {
15581
+ if (cachedRuntimeVersion === null) {
15582
+ try {
15583
+ const pkg = JSON.parse(fs4.readFileSync(path4.join(packageRoot(), "package.json"), "utf8"));
15584
+ cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
15585
+ } catch {
15586
+ cachedRuntimeVersion = "0.0.0";
15587
+ }
15588
+ }
15589
+ return cachedRuntimeVersion;
15590
+ };
15591
+ var stampNew = (record) => ({
15592
+ createdWith: record.runtime?.createdWith ?? null,
15593
+ updatedWith: runtimeVersion()
15594
+ });
15595
+ var isNewerVersion = (candidate, current) => {
15596
+ const parts = (value) => value.split("-")[0].split(".").map((item) => Number.parseInt(item, 10) || 0);
15597
+ const [next, now] = [parts(candidate), parts(current)];
15598
+ for (let index = 0;index < 3; index += 1) {
15599
+ const left = next[index] ?? 0;
15600
+ const right = now[index] ?? 0;
15601
+ if (left !== right)
15602
+ return left > right;
15603
+ }
15604
+ return false;
15605
+ };
15548
15606
  var jsonBytes = (value) => `${canonicalJson(value)}
15549
15607
  `;
15550
15608
  var digestBytes = (value) => createHash2("sha256").update(value).digest("hex");
@@ -15571,12 +15629,12 @@ var sameMetadataLock = (left, right) => {
15571
15629
  class TaskStore {
15572
15630
  root;
15573
15631
  constructor(root) {
15574
- this.root = fs4.existsSync(root) ? fs4.realpathSync(root) : path3.resolve(root);
15632
+ this.root = fs4.existsSync(root) ? fs4.realpathSync(root) : path4.resolve(root);
15575
15633
  }
15576
15634
  readTask(taskId) {
15577
15635
  if (!validId(taskId))
15578
15636
  return failure("invalid_input", "task ID is invalid", { taskId });
15579
- const result = this.readRecord(path3.join(this.tasksDir, `${taskId}.json`), taskRecordSchema);
15637
+ const result = this.readRecord(path4.join(this.tasksDir, `${taskId}.json`), taskRecordSchema);
15580
15638
  if (!result.exists)
15581
15639
  return failure("not_found", "task not found", { taskId });
15582
15640
  if (result.result.ok && result.result.data.id !== taskId)
@@ -15610,7 +15668,7 @@ class TaskStore {
15610
15668
  });
15611
15669
  const tasks = [];
15612
15670
  for (const name of names.sort()) {
15613
- const item = this.readRecord(path3.join(this.tasksDir, name), taskRecordSchema);
15671
+ const item = this.readRecord(path4.join(this.tasksDir, name), taskRecordSchema);
15614
15672
  if (!item.exists)
15615
15673
  continue;
15616
15674
  if (!item.result.ok)
@@ -15644,7 +15702,7 @@ class TaskStore {
15644
15702
  if (!current.ok)
15645
15703
  return current;
15646
15704
  if (current.data ? value.expectedWorkspaceRevision !== current.data.revision : value.expectedWorkspaceRevision !== null) {
15647
- return failure("revision_conflict", "workspace revision does not match", {
15705
+ return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
15648
15706
  expectedWorkspaceRevision: value.expectedWorkspaceRevision,
15649
15707
  actualWorkspaceRevision: current.data?.revision ?? null
15650
15708
  });
@@ -15656,11 +15714,17 @@ class TaskStore {
15656
15714
  const previousWorkspaceBytes = current.data ? this.snapshotBytes(this.workspacePath) : null;
15657
15715
  if (current.data && !previousWorkspaceBytes)
15658
15716
  return failure("storage_error", "workspace snapshot disappeared during creation");
15659
- const workspace = current.data ? { ...current.data, revision: newRevision(), root: this.root } : {
15717
+ const workspace = current.data ? {
15718
+ ...current.data,
15719
+ revision: newRevision(),
15720
+ root: this.root,
15721
+ runtime: stampNew(current.data)
15722
+ } : {
15660
15723
  schemaVersion: SCHEMA_VERSION,
15661
15724
  id: newId(),
15662
15725
  revision: newRevision(),
15663
15726
  root: this.root,
15727
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
15664
15728
  writer: null
15665
15729
  };
15666
15730
  const timestamp = value.now ?? now();
@@ -15682,6 +15746,7 @@ class TaskStore {
15682
15746
  status: "active",
15683
15747
  closure: null,
15684
15748
  progress: { summary: "", nextAction: null, blockers: [] },
15749
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
15685
15750
  assessments: [],
15686
15751
  policy: null,
15687
15752
  policyChanges: [],
@@ -15712,7 +15777,7 @@ class TaskStore {
15712
15777
  if (!current.ok)
15713
15778
  return current;
15714
15779
  if (current.data ? input.expectedWorkspaceRevision !== current.data.revision : input.expectedWorkspaceRevision !== null)
15715
- return failure("revision_conflict", "workspace revision does not match", {
15780
+ return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
15716
15781
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
15717
15782
  actualWorkspaceRevision: current.data?.revision ?? null
15718
15783
  });
@@ -15720,11 +15785,17 @@ class TaskStore {
15720
15785
  if (current.data && !previousWorkspaceBytes)
15721
15786
  return failure("storage_error", "workspace snapshot disappeared during import");
15722
15787
  const timestamp = input.now ?? now();
15723
- const workspace = current.data ? { ...current.data, revision: newRevision(), root: this.root } : {
15788
+ const workspace = current.data ? {
15789
+ ...current.data,
15790
+ revision: newRevision(),
15791
+ root: this.root,
15792
+ runtime: stampNew(current.data)
15793
+ } : {
15724
15794
  schemaVersion: SCHEMA_VERSION,
15725
15795
  id: input.workspaceId ?? newId(),
15726
15796
  revision: newRevision(),
15727
15797
  root: this.root,
15798
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
15728
15799
  writer: null
15729
15800
  };
15730
15801
  const task = {
@@ -15732,7 +15803,11 @@ class TaskStore {
15732
15803
  workspaceId: workspace.id,
15733
15804
  revision: newRevision(),
15734
15805
  createdAt: timestamp,
15735
- updatedAt: timestamp
15806
+ updatedAt: timestamp,
15807
+ runtime: {
15808
+ createdWith: input.task.runtime?.createdWith ?? null,
15809
+ updatedWith: runtimeVersion()
15810
+ }
15736
15811
  };
15737
15812
  const validTask = taskRecordSchema.safeParse(task);
15738
15813
  if (!validTask.success)
@@ -15781,7 +15856,8 @@ class TaskStore {
15781
15856
  workspaceId: current.data.workspaceId,
15782
15857
  createdAt: current.data.createdAt,
15783
15858
  revision: context.revision,
15784
- updatedAt: context.now
15859
+ updatedAt: context.now,
15860
+ runtime: stampNew(current.data)
15785
15861
  };
15786
15862
  const valid = taskRecordSchema.safeParse(record);
15787
15863
  if (!valid.success)
@@ -15815,7 +15891,8 @@ class TaskStore {
15815
15891
  ...changed.data,
15816
15892
  id: current.data.id,
15817
15893
  root: this.root,
15818
- revision: context.revision
15894
+ revision: context.revision,
15895
+ runtime: stampNew(current.data)
15819
15896
  };
15820
15897
  const valid = workspaceRecordSchema.safeParse(record);
15821
15898
  if (!valid.success)
@@ -15858,7 +15935,8 @@ class TaskStore {
15858
15935
  ...changedWorkspace.data,
15859
15936
  id: workspace.data.id,
15860
15937
  root: this.root,
15861
- revision: workspaceContext.revision
15938
+ revision: workspaceContext.revision,
15939
+ runtime: stampNew(workspace.data)
15862
15940
  });
15863
15941
  if (!nextWorkspace.success)
15864
15942
  return failure("invalid_input", "workspace mutation produced an invalid record");
@@ -15883,7 +15961,8 @@ class TaskStore {
15883
15961
  workspaceId: task.data.workspaceId,
15884
15962
  createdAt: task.data.createdAt,
15885
15963
  revision: taskContext.revision,
15886
- updatedAt: taskContext.now
15964
+ updatedAt: taskContext.now,
15965
+ runtime: stampNew(task.data)
15887
15966
  });
15888
15967
  if (!nextTask.success) {
15889
15968
  this.markUncertain(nextWorkspace.data);
@@ -15910,7 +15989,7 @@ class TaskStore {
15910
15989
  if (match)
15911
15990
  candidates.push({
15912
15991
  target: match[1],
15913
- path: path3.join(this.recoveryDir, name),
15992
+ path: path4.join(this.recoveryDir, name),
15914
15993
  digest: match[3]
15915
15994
  });
15916
15995
  }
@@ -16103,7 +16182,8 @@ class TaskStore {
16103
16182
  const value = {
16104
16183
  ...workspace,
16105
16184
  writer: { ...workspace.writer, state: "uncertain" },
16106
- revision: newRevision()
16185
+ revision: newRevision(),
16186
+ runtime: stampNew(workspace)
16107
16187
  };
16108
16188
  this.replaceSnapshot(this.workspacePath, value, workspace);
16109
16189
  }
@@ -16181,7 +16261,7 @@ class TaskStore {
16181
16261
  }
16182
16262
  fs4.renameSync(temporary, file);
16183
16263
  temporary = undefined;
16184
- this.fsyncDirectory(path3.dirname(file));
16264
+ this.fsyncDirectory(path4.dirname(file));
16185
16265
  return success(null, null, value);
16186
16266
  } catch (error) {
16187
16267
  return failure("storage_error", `snapshot replacement failed: ${String(error)}`, {
@@ -16195,9 +16275,9 @@ class TaskStore {
16195
16275
  }
16196
16276
  }
16197
16277
  saveRecovery(file, bytes) {
16198
- const target = path3.basename(file) === "workspace.json" ? "workspace" : "task";
16199
- const id = target === "task" ? path3.basename(file, ".json") : "workspace";
16200
- const destination = path3.join(this.recoveryDir, `${target}.${id}.${digestBytes(bytes)}.json`);
16278
+ const target = path4.basename(file) === "workspace.json" ? "workspace" : "task";
16279
+ const id = target === "task" ? path4.basename(file, ".json") : "workspace";
16280
+ const destination = path4.join(this.recoveryDir, `${target}.${id}.${digestBytes(bytes)}.json`);
16201
16281
  if (fs4.existsSync(destination)) {
16202
16282
  if (digestBytes(fs4.readFileSync(destination)) === digestBytes(bytes))
16203
16283
  return;
@@ -16275,7 +16355,7 @@ class TaskStore {
16275
16355
  for (const name of fs4.readdirSync(this.tasksDir)) {
16276
16356
  if (!name.endsWith(".json") || !validId(name.slice(0, -5)))
16277
16357
  return null;
16278
- const parsed = this.parseBytes(fs4.readFileSync(path3.join(this.tasksDir, name)), taskRecordSchema);
16358
+ const parsed = this.parseBytes(fs4.readFileSync(path4.join(this.tasksDir, name)), taskRecordSchema);
16279
16359
  if (!parsed.ok || parsed.data.id !== name.slice(0, -5))
16280
16360
  return null;
16281
16361
  ids.add(parsed.data.workspaceId);
@@ -16317,7 +16397,12 @@ class TaskStore {
16317
16397
  if (isObject2(value) && "schemaVersion" in value && value.schemaVersion !== SCHEMA_VERSION)
16318
16398
  return failure("unsupported_version", "unsupported snapshot schema version");
16319
16399
  const parsed = schema.safeParse(value);
16320
- return parsed.success ? success(null, null, parsed.data) : failure("recovery_required", "snapshot does not satisfy its schema");
16400
+ if (parsed.success)
16401
+ return success(null, null, parsed.data);
16402
+ const writerVersion = isObject2(value) && isObject2(value.runtime) ? value.runtime.updatedWith : null;
16403
+ if (typeof writerVersion === "string" && isNewerVersion(writerVersion, runtimeVersion()))
16404
+ return failure("recovery_required", `snapshot was written by workit ${writerVersion}; upgrade Workit before mutating this checkout`);
16405
+ return failure("recovery_required", "snapshot does not satisfy its schema");
16321
16406
  }
16322
16407
  processStart(pid) {
16323
16408
  try {
@@ -16327,43 +16412,98 @@ class TaskStore {
16327
16412
  }
16328
16413
  }
16329
16414
  conflict(expected, actual) {
16330
- return failure("revision_conflict", "snapshot revision does not match", {
16415
+ return failure("revision_conflict", "snapshot revision does not match; omit expectedRevision to use the current record", {
16331
16416
  expectedRevision: expected,
16332
16417
  actualRevision: actual
16333
16418
  });
16334
16419
  }
16335
16420
  taskPath(taskId) {
16336
- return path3.join(this.tasksDir, `${taskId}.json`);
16421
+ return path4.join(this.tasksDir, `${taskId}.json`);
16337
16422
  }
16338
16423
  get workitDir() {
16339
- return path3.join(this.root, ".workit");
16424
+ return path4.join(this.root, ".workit");
16340
16425
  }
16341
16426
  get tasksDir() {
16342
- return path3.join(this.workitDir, "tasks");
16427
+ return path4.join(this.workitDir, "tasks");
16343
16428
  }
16344
16429
  get recoveryDir() {
16345
- return path3.join(this.workitDir, "recovery");
16430
+ return path4.join(this.workitDir, "recovery");
16346
16431
  }
16347
16432
  get workspacePath() {
16348
- return path3.join(this.workitDir, "workspace.json");
16433
+ return path4.join(this.workitDir, "workspace.json");
16349
16434
  }
16350
16435
  get lockPath() {
16351
- return path3.join(this.workitDir, "metadata.lock");
16436
+ return path4.join(this.workitDir, "metadata.lock");
16352
16437
  }
16353
16438
  get gitignorePath() {
16354
- return path3.join(this.workitDir, ".gitignore");
16439
+ return path4.join(this.workitDir, ".gitignore");
16355
16440
  }
16356
16441
  }
16442
+ // packages/workit-core/src/core/workers.ts
16443
+ var same = (left, right) => {
16444
+ try {
16445
+ return canonicalJson(left) === canonicalJson(right);
16446
+ } catch {
16447
+ return false;
16448
+ }
16449
+ };
16450
+ var UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
16451
+ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
16452
+ var callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor };
16453
+ var validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
16454
+ const code = char.charCodeAt(0);
16455
+ return code < 32 || code === 127;
16456
+ }) && !value.split(/[\\/]/).includes("..");
16457
+ var callerValue = (value) => ({ host: value.host, actor: value.actor });
16458
+ var workerIdOf = (value) => value.workerId ?? null;
16459
+ function assertProductWriteAllowed(input) {
16460
+ if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
16461
+ return failure("invalid_input", "invalid product write path");
16462
+ if (!input.store)
16463
+ return failure("permission_denied", "write authorization requires core state");
16464
+ const task = input.store.readTask(input.task.id);
16465
+ if (!task.ok)
16466
+ return task;
16467
+ const workspace = input.store.readWorkspace();
16468
+ if (!workspace.ok)
16469
+ return workspace;
16470
+ if (!workspace.data)
16471
+ return failure("not_found", "workspace not found");
16472
+ const workspaceRecord = workspace.data;
16473
+ if (task.data.status !== "active")
16474
+ return failure("invalid_transition", "paused or closed tasks cannot own product writes");
16475
+ if (workspaceRecord.id !== task.data.workspaceId || workspaceRecord.root.length === 0)
16476
+ return failure("recovery_required", "task and workspace bindings are invalid");
16477
+ const writer = workspaceRecord.writer;
16478
+ if (!writer)
16479
+ return failure("permission_denied", "checkout has no writer owner");
16480
+ if (writer.state === "uncertain")
16481
+ return failure("recovery_required", "checkout writer requires recovery", {
16482
+ owner: writer.owner
16483
+ });
16484
+ const caller = callerValue(input.caller);
16485
+ const workerId = workerIdOf(input.caller);
16486
+ const owner = writer.owner;
16487
+ if (owner.taskId !== task.data.id || owner.workerId !== workerId || owner.session.host !== caller.host || owner.session.handle !== caller.actor)
16488
+ return failure("writer_conflict", "checkout is owned by another validated actor", { owner });
16489
+ const worker = workerId === null ? null : task.data.workers.find((entry) => entry.id === workerId);
16490
+ if (workerId !== null && (!worker || worker.data.assignment.role !== "implementer"))
16491
+ return failure("permission_denied", "only implementers can own product writes");
16492
+ if (workerId !== null && (!worker?.data.session || worker.data.state === "cancelling" || worker.data.state === "unknown" || worker.data.state === "stopped" || !same(worker.data.session, callerSession(input.caller))))
16493
+ return failure("recovery_required", "worker session is not an active writer");
16494
+ return success(null, workspaceRecord.revision, owner);
16495
+ }
16496
+
16357
16497
  // packages/workit-core/src/core/task-evaluation.ts
16358
16498
  import { createHash as createHash4 } from "node:crypto";
16359
16499
  import * as fs6 from "node:fs";
16360
16500
  import { spawnSync } from "node:child_process";
16361
- import path5 from "node:path";
16501
+ import path6 from "node:path";
16362
16502
 
16363
16503
  // packages/workit-core/src/core/authority.ts
16364
16504
  import { createHash as createHash3 } from "node:crypto";
16365
16505
  import * as fs5 from "node:fs";
16366
- import path4 from "node:path";
16506
+ import path5 from "node:path";
16367
16507
  var verifiedAuthorities = new WeakMap;
16368
16508
  var trustedAuthority = (kind, provenance, expected, binding, caller) => {
16369
16509
  const token = {};
@@ -16490,20 +16630,31 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
16490
16630
  continue;
16491
16631
  if (!reference.digest)
16492
16632
  return failure("invalid_input", "document references require a byte digest");
16493
- const target = path4.resolve(root, reference.path);
16494
- if (target !== root && !target.startsWith(`${root}${path4.sep}`))
16495
- return failure("invalid_input", "document reference escapes checkout");
16633
+ const target = path5.resolve(root, reference.path);
16634
+ if (target !== root && !target.startsWith(`${root}${path5.sep}`))
16635
+ return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
16636
+ fields: [
16637
+ { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
16638
+ ]
16639
+ });
16496
16640
  try {
16497
- const relative = path4.relative(root, target);
16641
+ const relative = path5.relative(root, target);
16498
16642
  let current = root;
16499
- for (const segment of relative.split(path4.sep)) {
16643
+ for (const segment of relative.split(path5.sep)) {
16500
16644
  if (!segment)
16501
16645
  continue;
16502
- current = path4.join(current, segment);
16646
+ current = path5.join(current, segment);
16503
16647
  const stat = fs5.lstatSync(current);
16504
16648
  const real = fs5.realpathSync(current);
16505
- if (real !== root && !real.startsWith(`${root}${path4.sep}`))
16506
- return failure("invalid_input", "document reference escapes checkout");
16649
+ if (real !== root && !real.startsWith(`${root}${path5.sep}`))
16650
+ return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
16651
+ fields: [
16652
+ {
16653
+ path: "contentRefs.path",
16654
+ reason: `${reference.path} resolves outside the checkout`
16655
+ }
16656
+ ]
16657
+ });
16507
16658
  if (stat.isSymbolicLink())
16508
16659
  return failure("invalid_input", "document reference uses a symlink");
16509
16660
  if (current === target && !stat.isFile())
@@ -16850,14 +17001,14 @@ var verifyDecisionContent = verifyContentRefs;
16850
17001
 
16851
17002
  // packages/workit-core/src/core/task-evaluation.ts
16852
17003
  var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex");
16853
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path5.sep}`);
16854
- var relative = (root, candidate) => path5.relative(root, candidate).split(path5.sep).join("/") || ".";
17004
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path6.sep}`);
17005
+ var relative = (root, candidate) => path6.relative(root, candidate).split(path6.sep).join("/") || ".";
16855
17006
  var canonicalPath = (value) => {
16856
17007
  if (typeof value !== "string" || !value)
16857
17008
  return null;
16858
17009
  if (value.includes("\\") || value.split("/").includes(".."))
16859
17010
  return null;
16860
- const normalized = path5.posix.normalize(value);
17011
+ const normalized = path6.posix.normalize(value);
16861
17012
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
16862
17013
  return null;
16863
17014
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -16905,14 +17056,14 @@ var gitPaths = (root) => {
16905
17056
  git: /not a git repository/i.test(stderr) ? false : true
16906
17057
  };
16907
17058
  }
16908
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path5.sep).join("/")) : [];
17059
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path6.sep).join("/")) : [];
16909
17060
  const stagedDeleted = spawnSync("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
16910
17061
  if (stagedDeleted.status !== 0) {
16911
17062
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
16912
17063
  if (!/not a git repository/i.test(stderr))
16913
17064
  return { paths, uncertain: true, git: true };
16914
17065
  } else if (stagedDeleted.stdout) {
16915
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path5.sep).join("/")));
17066
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path6.sep).join("/")));
16916
17067
  }
16917
17068
  return { paths: [...new Set(paths)], uncertain: false, git: true };
16918
17069
  };
@@ -16927,7 +17078,7 @@ var walk = (root, directory, output) => {
16927
17078
  for (const name of names) {
16928
17079
  if (name === ".git" || name === ".workit")
16929
17080
  continue;
16930
- const target = path5.join(directory, name);
17081
+ const target = path6.join(directory, name);
16931
17082
  let stat;
16932
17083
  try {
16933
17084
  stat = fs6.lstatSync(target);
@@ -16945,17 +17096,17 @@ var walk = (root, directory, output) => {
16945
17096
  var scopeRoots = (root, scope) => {
16946
17097
  const roots = [];
16947
17098
  for (const item of scope.paths.length ? scope.paths : ["."]) {
16948
- const target = path5.resolve(root, item);
17099
+ const target = path6.resolve(root, item);
16949
17100
  if (!inside(root, target))
16950
- return failure("invalid_input", "candidate scope escapes checkout");
17101
+ return failure("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
16951
17102
  let ancestor = target;
16952
17103
  while (!fs6.existsSync(ancestor) && ancestor !== root)
16953
- ancestor = path5.dirname(ancestor);
17104
+ ancestor = path6.dirname(ancestor);
16954
17105
  try {
16955
17106
  if (!inside(root, fs6.realpathSync(ancestor)))
16956
- return failure("invalid_input", "candidate scope escapes checkout");
17107
+ return failure("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
16957
17108
  } catch {
16958
- return failure("invalid_input", "candidate scope cannot be inspected");
17109
+ return failure("invalid_input", `candidate scope cannot be inspected: ${item}`);
16959
17110
  }
16960
17111
  roots.push(target);
16961
17112
  }
@@ -16970,7 +17121,7 @@ function captureCandidate(root, scope, environment = []) {
16970
17121
  }
16971
17122
  const normalizedScope = canonicalScope(scope);
16972
17123
  if (!normalizedScope)
16973
- return failure("invalid_input", "candidate scope is invalid");
17124
+ return failure("invalid_input", "candidate scope is invalid: paths must be checkout-relative — to work in another repository, keep this task here and coordinate a separate linked task in that checkout", { fields: [{ path: "scope.paths", reason: "must be checkout-relative" }] });
16974
17125
  const roots = scopeRoots(checkout, normalizedScope);
16975
17126
  if (!roots.ok)
16976
17127
  return roots;
@@ -17002,7 +17153,7 @@ function captureCandidate(root, scope, environment = []) {
17002
17153
  for (const item of [...names].sort()) {
17003
17154
  if (!scopeMatches(item, normalizedScope))
17004
17155
  continue;
17005
- const target = path5.join(checkout, item);
17156
+ const target = path6.join(checkout, item);
17006
17157
  try {
17007
17158
  const stat = fs6.lstatSync(target);
17008
17159
  if (stat.isSymbolicLink()) {
@@ -17159,7 +17310,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
17159
17310
  return true;
17160
17311
  const reviewSession = sessionFromRef(entry.data.reviewContext);
17161
17312
  const sameImplementation = sameSession(reviewSession, task.intent.provenance.session);
17162
- const sameEvidenceSession = task.evidence.some((other) => other.id !== entry.id && sameSession(reviewSession, other.provenance.session));
17313
+ const sameEvidenceSession = task.evidence.some((other) => other.id !== entry.id && other.data.kind === "review" && sameSession(reviewSession, other.provenance.session));
17163
17314
  return entry.data.kind === "review" && reviewSession !== null && sameSession(reviewSession, entry.provenance.session) && (requirement.ruleId === "self-review" || !sameImplementation && !sameEvidenceSession);
17164
17315
  });
17165
17316
  if (passed.length) {
@@ -17255,7 +17406,17 @@ function evaluateClosure(requestedOutcome, view) {
17255
17406
  const blocking = evaluations.filter((item) => item.status === "unsatisfied" || item.status === "unavailable");
17256
17407
  if (requestedOutcome !== "stopped" && blocking.length)
17257
17408
  return failure("requirements_unsatisfied", "applicable requirements are unsatisfied", {
17258
- requirementIds: blocking.map((item) => item.requirementId)
17409
+ requirementIds: blocking.map((item) => item.requirementId),
17410
+ requirements: blocking.map((item) => {
17411
+ const requirement = view.task.policy?.requirements.find((candidate) => candidate.id === item.requirementId);
17412
+ return {
17413
+ requirementId: item.requirementId,
17414
+ ruleId: requirement?.ruleId ?? "unknown",
17415
+ reason: item.reason,
17416
+ satisfaction: requirement?.satisfaction ?? "",
17417
+ dependentAction: requirement?.dependentAction ?? null
17418
+ };
17419
+ })
17259
17420
  });
17260
17421
  const accepted = evaluations.filter((item) => item.status === "accepted_limitation");
17261
17422
  if (requestedOutcome === "verified" && accepted.length)
@@ -17291,7 +17452,7 @@ function reconcileResume(view, observations = []) {
17291
17452
  return candidate;
17292
17453
  const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
17293
17454
  const blockers = [...view.task.progress.blockers];
17294
- if (view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
17455
+ if (view.task.workers.some((entry) => isUncertainWorker(entry.data.state)))
17295
17456
  blockers.push({
17296
17457
  reason: "worker state requires reconciliation",
17297
17458
  dependentAction: "resume",
@@ -17324,7 +17485,11 @@ var externalActionSchema = discriminatedUnion("operation", [
17324
17485
  }).strict(),
17325
17486
  object({
17326
17487
  operation: literal("git.commit"),
17327
- payload: object({ message: string2().min(1) }).strict()
17488
+ payload: object({
17489
+ message: string2().min(1).optional(),
17490
+ plan_steps: array(string2().min(1)).optional(),
17491
+ plan_branch: string2().min(1).optional()
17492
+ }).strict()
17328
17493
  }).strict(),
17329
17494
  object({
17330
17495
  operation: literal("git.push"),
@@ -17559,7 +17724,7 @@ function resolveRequirements(input) {
17559
17724
  ].join(", ")}`,
17560
17725
  satisfaction: "An observed or inferred fact resolves the uncertainty and records its supporting references.",
17561
17726
  before: "dependent_action",
17562
- dependentAction: "dependent-action",
17727
+ dependentAction: null,
17563
17728
  acceptanceAllowed: false
17564
17729
  }));
17565
17730
  if (signals.productChoiceOpen.value === true)
@@ -17570,7 +17735,7 @@ function resolveRequirements(input) {
17570
17735
  reason: `A consequential product choice remains open: ${signalReason(input, "productChoiceOpen")}`,
17571
17736
  satisfaction: "Present the material alternatives and record the user's decision before the dependent action.",
17572
17737
  before: "dependent_action",
17573
- dependentAction: "dependent-action",
17738
+ dependentAction: null,
17574
17739
  acceptanceAllowed: false
17575
17740
  }));
17576
17741
  if (signals.behaviorChange.value === true)
@@ -17728,59 +17893,6 @@ function diffPolicy(previous, next, reason, now) {
17728
17893
  return parsed.data;
17729
17894
  }
17730
17895
 
17731
- // packages/workit-core/src/core/workers.ts
17732
- var same = (left, right) => {
17733
- try {
17734
- return canonicalJson(left) === canonicalJson(right);
17735
- } catch {
17736
- return false;
17737
- }
17738
- };
17739
- var callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor };
17740
- var validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
17741
- const code = char.charCodeAt(0);
17742
- return code < 32 || code === 127;
17743
- }) && !value.split(/[\\/]/).includes("..");
17744
- var callerValue = (value) => ({ host: value.host, actor: value.actor });
17745
- var workerIdOf = (value) => value.workerId ?? null;
17746
- function assertProductWriteAllowed(input) {
17747
- if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
17748
- return failure("invalid_input", "invalid product write path");
17749
- if (!input.store)
17750
- return failure("permission_denied", "write authorization requires core state");
17751
- const task = input.store.readTask(input.task.id);
17752
- if (!task.ok)
17753
- return task;
17754
- const workspace = input.store.readWorkspace();
17755
- if (!workspace.ok)
17756
- return workspace;
17757
- if (!workspace.data)
17758
- return failure("not_found", "workspace not found");
17759
- const workspaceRecord = workspace.data;
17760
- if (task.data.status !== "active")
17761
- return failure("invalid_transition", "paused or closed tasks cannot own product writes");
17762
- if (workspaceRecord.id !== task.data.workspaceId || workspaceRecord.root.length === 0)
17763
- return failure("recovery_required", "task and workspace bindings are invalid");
17764
- const writer = workspaceRecord.writer;
17765
- if (!writer)
17766
- return failure("permission_denied", "checkout has no writer owner");
17767
- if (writer.state === "uncertain")
17768
- return failure("recovery_required", "checkout writer requires recovery", {
17769
- owner: writer.owner
17770
- });
17771
- const caller = callerValue(input.caller);
17772
- const workerId = workerIdOf(input.caller);
17773
- const owner = writer.owner;
17774
- if (owner.taskId !== task.data.id || owner.workerId !== workerId || owner.session.host !== caller.host || owner.session.handle !== caller.actor)
17775
- return failure("writer_conflict", "checkout is owned by another validated actor", { owner });
17776
- const worker = workerId === null ? null : task.data.workers.find((entry) => entry.id === workerId);
17777
- if (workerId !== null && (!worker || worker.data.assignment.role !== "implementer"))
17778
- return failure("permission_denied", "only implementers can own product writes");
17779
- if (workerId !== null && (!worker?.data.session || worker.data.state === "cancelling" || worker.data.state === "unknown" || worker.data.state === "stopped" || !same(worker.data.session, callerSession(input.caller))))
17780
- return failure("recovery_required", "worker session is not an active writer");
17781
- return success(null, workspaceRecord.revision, owner);
17782
- }
17783
-
17784
17896
  // packages/workit-core/src/core/task-engine.ts
17785
17897
  var provenance = (context, kind = context.provenanceKind ?? "host_observed") => ({
17786
17898
  kind,
@@ -17911,15 +18023,20 @@ var applyWorkerLifecycle = (input) => {
17911
18023
  return failure("invalid_transition", "worker has not started");
17912
18024
  if (input.state === "running" && entry.data.state === "stopped")
17913
18025
  return failure("invalid_transition", "stopped worker cannot run again");
18026
+ const terminalCancel = entry.data.state === "cancelling" && input.state === "running";
17914
18027
  const nextEntry = {
17915
18028
  ...entry,
17916
18029
  recordedAt: input.now ?? entry.recordedAt,
17917
18030
  provenance: authority.provenance,
17918
- data: { ...entry.data, state: input.state, session: input.session }
18031
+ data: {
18032
+ ...entry.data,
18033
+ state: terminalCancel ? "cancelling" : input.state,
18034
+ session: input.session
18035
+ }
17919
18036
  };
17920
18037
  const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
17921
18038
  const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
17922
- if (entry.data.state === input.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
18039
+ if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
17923
18040
  return success(task.data.revision, workspace.data.revision, entry);
17924
18041
  const changed = input.store.mutateTaskAndWorkspace({
17925
18042
  taskId: task.data.id,
@@ -18289,7 +18406,7 @@ class WorkitCore {
18289
18406
  activeWorkerBlocker(task, workspace) {
18290
18407
  if (workspace.writer)
18291
18408
  return failure("recovery_required", "writer ownership must be released first");
18292
- if (task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
18409
+ if (task.workers.some((entry) => isUncertainWorker(entry.data.state)))
18293
18410
  return failure("recovery_required", "worker state requires reconciliation");
18294
18411
  return null;
18295
18412
  }
@@ -18526,11 +18643,6 @@ class WorkitCore {
18526
18643
  if (helper && !helper.ok)
18527
18644
  return helper;
18528
18645
  const evidence = input.evidence;
18529
- if (helper?.ok) {
18530
- const assignment = helper.data.data.assignment;
18531
- if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || assignment.candidateId !== null && evidence.candidateId !== assignment.candidateId && evidence.beforeCandidateId !== assignment.candidateId || !refsWithinScope(evidence.refs, assignment.scope) || !refsWithinScope(evidence.refs, task.data.intent.data.scope))
18532
- return failure("permission_denied", "evidence is outside the worker assignment");
18533
- }
18534
18646
  if ((evidence.result === "missing" || evidence.result === "skipped") && !evidence.summary.trim())
18535
18647
  return failure("invalid_input", "missing or skipped evidence requires a reason");
18536
18648
  if (evidence.kind === "review") {
@@ -18547,6 +18659,11 @@ class WorkitCore {
18547
18659
  evidence.beforeCandidateId ??= null;
18548
18660
  evidence.candidateId ??= null;
18549
18661
  }
18662
+ if (helper?.ok) {
18663
+ const assignment = helper.data.data.assignment;
18664
+ if (evidence.requirementIds.some((id) => !assignment.requirementIds.includes(id)) || assignment.candidateId !== null && evidence.candidateId !== assignment.candidateId && evidence.beforeCandidateId !== assignment.candidateId || !refsWithinScope(evidence.refs, assignment.scope) || !refsWithinScope(evidence.refs, task.data.intent.data.scope))
18665
+ return failure("permission_denied", "evidence is outside the worker assignment");
18666
+ }
18550
18667
  const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
18551
18668
  for (const candidateId of [evidence.beforeCandidateId, evidence.candidateId]) {
18552
18669
  if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
@@ -19442,6 +19559,9 @@ class WorkitCore {
19442
19559
  id: task.id,
19443
19560
  revision: task.revision,
19444
19561
  workspaceRevision: workspace.data.revision,
19562
+ createdAt: task.createdAt,
19563
+ updatedAt: task.updatedAt,
19564
+ runtime: task.runtime ?? null,
19445
19565
  objective: task.intent.data.objective,
19446
19566
  status: task.status,
19447
19567
  closure: task.closure,
@@ -19527,7 +19647,7 @@ class WorkitCore {
19527
19647
  const base = reconcileResume(effectiveView);
19528
19648
  if (!base.ok)
19529
19649
  return base;
19530
- const needsReconciliation = view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
19650
+ const needsReconciliation = view.task.workers.some((entry) => isUncertainWorker(observedStates.get(entry.id) ?? entry.data.state));
19531
19651
  const blockers = [...base.data.blockers];
19532
19652
  if (needsReconciliation && !blockers.some((entry) => entry.reason === "worker state requires reconciliation"))
19533
19653
  blockers.push({
@@ -19594,7 +19714,8 @@ class WorkitCore {
19594
19714
  ...current,
19595
19715
  status,
19596
19716
  candidates: resumeCandidate && !current.candidates.some((candidate) => candidate.id === resumeCandidate.id) ? [...current.candidates, resumeCandidate] : current.candidates,
19597
- progress: status === "paused" ? { ...current.progress, summary: input.reason } : current.progress
19717
+ progress: current.progress,
19718
+ pauseReason: status === "paused" ? input.reason ?? null : null
19598
19719
  })
19599
19720
  });
19600
19721
  if (!changed.ok)
@@ -19630,7 +19751,7 @@ class WorkitCore {
19630
19751
  return workspace;
19631
19752
  if (!workspace.data)
19632
19753
  return failure("not_found", "workspace not found");
19633
- if (workspace.data.writer || task.data.workers.some((entry) => entry.data.state !== "stopped"))
19754
+ if (workspace.data.writer || task.data.workers.some((entry) => isUncertainWorker(entry.data.state)))
19634
19755
  return failure("recovery_required", "scope revision requires worker ownership reconciliation");
19635
19756
  this.fillRevisions(input, task.data, workspace.data);
19636
19757
  const changed = this.store.mutateTaskAndWorkspace({
@@ -19741,15 +19862,15 @@ function resolveGitRevision(root, value) {
19741
19862
  }
19742
19863
  function resolveInside(root, candidate) {
19743
19864
  const base = realpathSync5(root);
19744
- const target = path6.resolve(base, candidate);
19745
- if (target !== base && !target.startsWith(base + path6.sep)) {
19865
+ const target = path7.resolve(base, candidate);
19866
+ if (target !== base && !target.startsWith(base + path7.sep)) {
19746
19867
  throw new Error("path must stay inside repository root");
19747
19868
  }
19748
19869
  let ancestor = target;
19749
- while (!existsSync3(ancestor))
19750
- ancestor = path6.dirname(ancestor);
19870
+ while (!existsSync4(ancestor))
19871
+ ancestor = path7.dirname(ancestor);
19751
19872
  const canonicalAncestor = realpathSync5(ancestor);
19752
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path6.sep)) {
19873
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path7.sep)) {
19753
19874
  throw new Error("path must stay inside repository root");
19754
19875
  }
19755
19876
  return target;
@@ -25796,14 +25917,14 @@ var gitContext = (workspaceRoot, paths = []) => {
25796
25917
  import {
25797
25918
  copyFileSync,
25798
25919
  cpSync,
25799
- existsSync as existsSync4,
25920
+ existsSync as existsSync5,
25800
25921
  mkdirSync as mkdirSync2,
25801
25922
  readFileSync as readFileSync4,
25802
25923
  readdirSync as readdirSync3,
25803
25924
  writeFileSync as writeFileSync2
25804
25925
  } from "node:fs";
25805
25926
  import os2 from "node:os";
25806
- import path7 from "node:path";
25927
+ import path8 from "node:path";
25807
25928
  var diagnosticLogger;
25808
25929
  var PRESETS = {
25809
25930
  gitflow: {
@@ -25824,7 +25945,7 @@ var mergePreset = (preset, input = {}, current = {
25824
25945
  protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
25825
25946
  };
25826
25947
  };
25827
- var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path7.join(process.env.XDG_CONFIG_HOME || path7.join(os2.homedir(), ".config"), "workit");
25948
+ var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path8.join(process.env.XDG_CONFIG_HOME || path8.join(os2.homedir(), ".config"), "workit");
25828
25949
  var migratedDir = null;
25829
25950
  var migrationFailed = false;
25830
25951
  var ensureConfigDir = (dir = resolveConfigDir()) => {
@@ -25834,12 +25955,12 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
25834
25955
  migratedDir = dir;
25835
25956
  return dir;
25836
25957
  }
25837
- const legacy = path7.join(process.env.XDG_CONFIG_HOME || path7.join(os2.homedir(), ".config"), "workflow-toolkit");
25838
- if (!existsSync4(legacy)) {
25958
+ const legacy = path8.join(process.env.XDG_CONFIG_HOME || path8.join(os2.homedir(), ".config"), "workflow-toolkit");
25959
+ if (!existsSync5(legacy)) {
25839
25960
  migratedDir = dir;
25840
25961
  return dir;
25841
25962
  }
25842
- if (!migrationFailed && existsSync4(dir)) {
25963
+ if (!migrationFailed && existsSync5(dir)) {
25843
25964
  migratedDir = dir;
25844
25965
  return dir;
25845
25966
  }
@@ -25847,9 +25968,9 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
25847
25968
  mkdirSync2(dir, { recursive: true });
25848
25969
  diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
25849
25970
  for (const entry of readdirSync3(legacy, { withFileTypes: true })) {
25850
- const src = path7.join(legacy, entry.name);
25851
- const dest = path7.join(dir, entry.name);
25852
- if (existsSync4(dest))
25971
+ const src = path8.join(legacy, entry.name);
25972
+ const dest = path8.join(dir, entry.name);
25973
+ if (existsSync5(dest))
25853
25974
  continue;
25854
25975
  try {
25855
25976
  if (entry.isDirectory())
@@ -25922,7 +26043,7 @@ var parseConfigResult = (raw, file) => {
25922
26043
  };
25923
26044
  };
25924
26045
  var readConfigTyped = (dir) => {
25925
- const file = path7.join(dir ?? configDir(), "config.json");
26046
+ const file = path8.join(dir ?? configDir(), "config.json");
25926
26047
  return parseConfigResult(readSafe(file), file);
25927
26048
  };
25928
26049
  var readConfig = () => {
@@ -25958,9 +26079,9 @@ var COMMIT_PRESETS = [
25958
26079
 
25959
26080
  // packages/workit-core/src/core/workspaces.ts
25960
26081
  import { readFileSync as readFileSync5, realpathSync as realpathSync6 } from "node:fs";
25961
- import path8 from "node:path";
26082
+ import path9 from "node:path";
25962
26083
  var readWorkspacesResult = (dir = configDir()) => {
25963
- const file = path8.join(dir, "workspaces.json");
26084
+ const file = path9.join(dir, "workspaces.json");
25964
26085
  let raw;
25965
26086
  try {
25966
26087
  raw = readFileSync5(file, "utf8");
@@ -26058,10 +26179,10 @@ var resolveWorkspace = (cwd) => resolveWorkspaceFrom(cwd, configDir());
26058
26179
 
26059
26180
  // packages/workit-core/src/core/vcs-config.ts
26060
26181
  import fs7 from "node:fs";
26061
- import path9 from "node:path";
26182
+ import path10 from "node:path";
26062
26183
  import { spawnSync as spawnSync3 } from "node:child_process";
26063
26184
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
26064
- var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path9.join(configDir(), "vcs.json");
26185
+ var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path10.join(configDir(), "vcs.json");
26065
26186
  var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
26066
26187
  var remoteProvider = (cwd) => {
26067
26188
  const r = spawnSync3("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
@@ -26144,8 +26265,8 @@ function vcsConfig(mode, cwd) {
26144
26265
  }
26145
26266
  const prov = cfg[provider] ?? {};
26146
26267
  const wsTokenFile = typeof wsVcs.tokenFile === "string" && wsVcs.tokenFile.trim() !== "" ? wsVcs.tokenFile : null;
26147
- const tokenFile = String(wsTokenFile ?? prov.tokenFile ?? path9.join(configDir(), `${provider}.token`));
26148
- const tokenPath = path9.resolve(tokenFile);
26268
+ const tokenFile = String(wsTokenFile ?? prov.tokenFile ?? path10.join(configDir(), `${provider}.token`));
26269
+ const tokenPath = path10.resolve(tokenFile);
26149
26270
  let tokenOk = false;
26150
26271
  if (fs7.existsSync(tokenPath)) {
26151
26272
  const token = fs7.readFileSync(tokenPath, "utf8").trim();
@@ -26154,7 +26275,7 @@ function vcsConfig(mode, cwd) {
26154
26275
  }
26155
26276
  const out = {
26156
26277
  ok: true,
26157
- configPath: path9.resolve(cfgPath),
26278
+ configPath: path10.resolve(cfgPath),
26158
26279
  provider,
26159
26280
  defaultTargetBranch: defaultTarget,
26160
26281
  pr: cfg.pr ?? {},
@@ -26247,18 +26368,18 @@ var resolveBranchPolicyFor = (workspaceRoot) => resolveBranchPolicy(readConfig()
26247
26368
 
26248
26369
  // packages/workit-core/src/core/pr-create.ts
26249
26370
  import fs8 from "node:fs";
26250
- import path10 from "node:path";
26371
+ import path11 from "node:path";
26251
26372
  function parseGhIssue(value) {
26252
26373
  const m = /issues\/(\d+)/.exec(value);
26253
26374
  return m ? m[1] : String(value).trim().replace(/^#/, "");
26254
26375
  }
26255
26376
  function whichOnPath(tool) {
26256
26377
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
26257
- for (const dir of (process.env.PATH ?? "").split(path10.delimiter)) {
26378
+ for (const dir of (process.env.PATH ?? "").split(path11.delimiter)) {
26258
26379
  if (!dir)
26259
26380
  continue;
26260
26381
  for (const name of names) {
26261
- const candidate = path10.join(dir, name);
26382
+ const candidate = path11.join(dir, name);
26262
26383
  try {
26263
26384
  fs8.accessSync(candidate, fs8.constants.X_OK);
26264
26385
  return candidate;
@@ -26271,8 +26392,8 @@ var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "gla
26271
26392
 
26272
26393
  // packages/workit-core/src/core/repo-context.ts
26273
26394
  import { spawnSync as spawnSync4 } from "node:child_process";
26274
- import { existsSync as existsSync5, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
26275
- import path11 from "node:path";
26395
+ import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
26396
+ import path12 from "node:path";
26276
26397
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
26277
26398
  try {
26278
26399
  gitRevisionParts(value);
@@ -26406,7 +26527,7 @@ var findPrTemplate = (cwd) => {
26406
26527
  ]) {
26407
26528
  const parts = rel.split("/");
26408
26529
  const base = parts[parts.length - 1];
26409
- const dir = parts.length > 1 ? path11.join(cwd, ...parts.slice(0, -1)) : cwd;
26530
+ const dir = parts.length > 1 ? path12.join(cwd, ...parts.slice(0, -1)) : cwd;
26410
26531
  const actual = probeCaseInsensitive(dir, base);
26411
26532
  if (actual)
26412
26533
  return [...parts.slice(0, -1), actual].join("/");
@@ -26437,8 +26558,8 @@ var diffStatForRange = (cwd, range) => {
26437
26558
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
26438
26559
  };
26439
26560
  var packageScripts = (cwd, keys) => {
26440
- const pkgPath = path11.join(cwd, "package.json");
26441
- if (!existsSync5(pkgPath))
26561
+ const pkgPath = path12.join(cwd, "package.json");
26562
+ if (!existsSync6(pkgPath))
26442
26563
  return [];
26443
26564
  try {
26444
26565
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
@@ -26463,9 +26584,9 @@ var documentationFiles = (cwd) => {
26463
26584
  if (entry.isDirectory()) {
26464
26585
  if (excluded.has(entry.name))
26465
26586
  continue;
26466
- walk(path11.join(dir, entry.name), depth + 1);
26587
+ walk(path12.join(dir, entry.name), depth + 1);
26467
26588
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
26468
- matches.push("./" + path11.relative(cwd, path11.join(dir, entry.name)).replaceAll("\\", "/"));
26589
+ matches.push("./" + path12.relative(cwd, path12.join(dir, entry.name)).replaceAll("\\", "/"));
26469
26590
  }
26470
26591
  }
26471
26592
  };
@@ -26473,7 +26594,7 @@ var documentationFiles = (cwd) => {
26473
26594
  return matches.sort().slice(0, 200);
26474
26595
  };
26475
26596
  var readTrimmed = (file, maxLines) => {
26476
- if (!existsSync5(file))
26597
+ if (!existsSync6(file))
26477
26598
  return "";
26478
26599
  const lines = readFileSync6(file, "utf8").split(`
26479
26600
  `);
@@ -26481,7 +26602,7 @@ var readTrimmed = (file, maxLines) => {
26481
26602
  `);
26482
26603
  };
26483
26604
  function prReadyContext(root, range) {
26484
- const cwd = path11.resolve(repoRoot(root));
26605
+ const cwd = path12.resolve(repoRoot(root));
26485
26606
  if (range !== undefined && !isResolvableContextRange(cwd, range))
26486
26607
  return invalidRange(cwd);
26487
26608
  let stdout = "";
@@ -26539,7 +26660,7 @@ ${status.stderr}` : "");
26539
26660
  stdout += `template_path: ${template}
26540
26661
 
26541
26662
  `;
26542
- stdout += readTrimmed(path11.join(cwd, template), 220);
26663
+ stdout += readTrimmed(path12.join(cwd, template), 220);
26543
26664
  } else {
26544
26665
  stdout += `template_path: none
26545
26666
 
@@ -26549,7 +26670,7 @@ ${status.stderr}` : "");
26549
26670
  stdout += `
26550
26671
  `;
26551
26672
  stdout += printSection("Recent Validation Signals");
26552
- if (existsSync5(path11.join(cwd, "package.json"))) {
26673
+ if (existsSync6(path12.join(cwd, "package.json"))) {
26553
26674
  stdout += `package.json detected. Common scripts:
26554
26675
  `;
26555
26676
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -26558,7 +26679,7 @@ ${status.stderr}` : "");
26558
26679
  `) + `
26559
26680
  `;
26560
26681
  }
26561
- if (existsSync5(path11.join(cwd, "Cargo.toml")) || existsSync5(path11.join(cwd, "src-tauri/Cargo.toml"))) {
26682
+ if (existsSync6(path12.join(cwd, "Cargo.toml")) || existsSync6(path12.join(cwd, "src-tauri/Cargo.toml"))) {
26562
26683
  stdout += `Rust project detected.
26563
26684
  `;
26564
26685
  }
@@ -26591,7 +26712,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
26591
26712
  - Apply with the native workit_changelog_apply tool only (not hand-edits under Unreleased).
26592
26713
  - If Unreleased already has duplicate category headings, normalize_only first.`;
26593
26714
  function changelogContext(root, range) {
26594
- const cwd = path11.resolve(repoRoot(root));
26715
+ const cwd = path12.resolve(repoRoot(root));
26595
26716
  if (range !== undefined && !isResolvableContextRange(cwd, range))
26596
26717
  return invalidRange(cwd);
26597
26718
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -26609,8 +26730,8 @@ function changelogContext(root, range) {
26609
26730
  stdout += CHANGELOG_RULES + `
26610
26731
  `;
26611
26732
  stdout += printSection("Existing CHANGELOG.md");
26612
- const changelogPath = path11.join(cwd, "CHANGELOG.md");
26613
- if (existsSync5(changelogPath)) {
26733
+ const changelogPath = path12.join(cwd, "CHANGELOG.md");
26734
+ if (existsSync6(changelogPath)) {
26614
26735
  stdout += readTrimmed(changelogPath, 260) + `
26615
26736
  `;
26616
26737
  } else {
@@ -26629,7 +26750,7 @@ function changelogContext(root, range) {
26629
26750
  return { stdout, stderr: "", exitCode: 0, cwd };
26630
26751
  }
26631
26752
  function docsRefreshContext(root, range) {
26632
- const cwd = path11.resolve(repoRoot(root));
26753
+ const cwd = path12.resolve(repoRoot(root));
26633
26754
  if (range !== undefined && !isResolvableContextRange(cwd, range))
26634
26755
  return invalidRange(cwd);
26635
26756
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -26651,8 +26772,8 @@ function docsRefreshContext(root, range) {
26651
26772
  `) + `
26652
26773
  `;
26653
26774
  stdout += printSection("README Preview");
26654
- const readme = path11.join(cwd, "README.md");
26655
- if (existsSync5(readme)) {
26775
+ const readme = path12.join(cwd, "README.md");
26776
+ if (existsSync6(readme)) {
26656
26777
  stdout += readTrimmed(readme, 220) + `
26657
26778
  `;
26658
26779
  } else {
@@ -26660,8 +26781,8 @@ function docsRefreshContext(root, range) {
26660
26781
  `;
26661
26782
  }
26662
26783
  stdout += printSection("Package Scripts");
26663
- const pkgPath = path11.join(cwd, "package.json");
26664
- if (existsSync5(pkgPath)) {
26784
+ const pkgPath = path12.join(cwd, "package.json");
26785
+ if (existsSync6(pkgPath)) {
26665
26786
  try {
26666
26787
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
26667
26788
  stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
@@ -26674,7 +26795,7 @@ function docsRefreshContext(root, range) {
26674
26795
  return { stdout, stderr: "", exitCode: 0, cwd };
26675
26796
  }
26676
26797
  function releaseNotesContext(root, rangeOrTag) {
26677
- const cwd = path11.resolve(repoRoot(root));
26798
+ const cwd = path12.resolve(repoRoot(root));
26678
26799
  if (!rangeOrTag) {
26679
26800
  return { stdout: "", stderr: `ERROR: release tag or range required
26680
26801
  `, exitCode: 1, cwd };
@@ -26712,7 +26833,7 @@ function releaseNotesContext(root, rangeOrTag) {
26712
26833
  `;
26713
26834
  stdout += printSection("Existing Release Files");
26714
26835
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
26715
- if (existsSync5(path11.join(cwd, rel)))
26836
+ if (existsSync6(path12.join(cwd, rel)))
26716
26837
  stdout += rel + `
26717
26838
  `;
26718
26839
  }
@@ -26903,25 +27024,6 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
26903
27024
  import fs10 from "node:fs";
26904
27025
  import path13 from "node:path";
26905
27026
 
26906
- // packages/workit-core/src/core/package-root.ts
26907
- import { existsSync as existsSync6 } from "node:fs";
26908
- import path12 from "node:path";
26909
- import { fileURLToPath } from "node:url";
26910
- var packageRoot = () => {
26911
- let dir = path12.dirname(fileURLToPath(import.meta.url));
26912
- while (true) {
26913
- const parent = path12.dirname(dir);
26914
- if (existsSync6(path12.join(dir, "package.json")) || parent === dir)
26915
- return dir;
26916
- dir = parent;
26917
- }
26918
- };
26919
- var assetRoot = () => {
26920
- const root = packageRoot();
26921
- const assets = path12.join(root, "assets");
26922
- return existsSync6(assets) ? assets : root;
26923
- };
26924
-
26925
27027
  // packages/workit-core/src/core/templates.ts
26926
27028
  var repoRoot2 = assetRoot();
26927
27029
 
@@ -27425,6 +27527,32 @@ var toolInputSchema = (family) => {
27425
27527
  const schema = boundedOperationJsonSchema(family);
27426
27528
  return { type: "object", ...schema };
27427
27529
  };
27530
+ var schemaBranches = (schema) => Array.isArray(schema.oneOf) ? schema.oneOf : Array.isArray(schema.anyOf) ? schema.anyOf : [schema];
27531
+ var readOnlyBranches = (family) => schemaBranches(boundedOperationJsonSchema(family)).filter((branch) => {
27532
+ const action = branch.properties?.action?.const;
27533
+ return typeof action === "string" && READ_ONLY_ACTIONS.has(action);
27534
+ });
27535
+ var advertisedToolSchemas = (attested) => {
27536
+ if (attested)
27537
+ return OPERATION_FAMILIES.map((family) => ({
27538
+ name: `workit_${family}`,
27539
+ description: operationDescription(family),
27540
+ inputSchema: toolInputSchema(family)
27541
+ }));
27542
+ return OPERATION_FAMILIES.flatMap((family) => {
27543
+ const branches = readOnlyBranches(family);
27544
+ if (!branches.length)
27545
+ return [];
27546
+ const schema = boundedOperationJsonSchema(family);
27547
+ return [
27548
+ {
27549
+ name: `workit_${family}`,
27550
+ description: `${operationDescription(family)} Unattested MCP callers can only run read-only actions; mutating operations require an attested host session.`,
27551
+ inputSchema: { type: "object", ...schema, oneOf: branches }
27552
+ }
27553
+ ];
27554
+ });
27555
+ };
27428
27556
  var sanitizeFailure = (result, workspaceRoot) => {
27429
27557
  if (result.ok)
27430
27558
  return result;
@@ -27479,13 +27607,16 @@ function createMcpServer(host, contextProvider) {
27479
27607
  ].filter((file) => existsSync7(file)));
27480
27608
  let staleWarned = false;
27481
27609
  const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
27482
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
27483
- tools: OPERATION_FAMILIES.map((family) => ({
27484
- name: `workit_${family}`,
27485
- description: operationDescription(family),
27486
- inputSchema: toolInputSchema(family)
27487
- }))
27488
- }));
27610
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
27611
+ let attested = false;
27612
+ try {
27613
+ const context = await contextProvider.current();
27614
+ attested = context.callerAttested === true;
27615
+ } catch {
27616
+ attested = false;
27617
+ }
27618
+ return { tools: advertisedToolSchemas(attested) };
27619
+ });
27489
27620
  server.setRequestHandler(ListResourcesRequestSchema, async () => ({
27490
27621
  resources: CONTEXT_KINDS.map((kind) => ({
27491
27622
  uri: `workit://context/${kind}`,
@@ -27600,7 +27731,7 @@ function createMcpServer(host, contextProvider) {
27600
27731
  const parsed = parseOperation(family, request.params.arguments);
27601
27732
  if (!parsed.ok)
27602
27733
  return resultForClient(parsed, workspaceRoot);
27603
- if (context.callerAttested === false && requiresCallerIdentity(parsed.data))
27734
+ if (context.callerAttested !== true && requiresCallerIdentity(parsed.data))
27604
27735
  return resultForClient({
27605
27736
  ok: false,
27606
27737
  schemaVersion: 1,
@@ -27660,6 +27791,13 @@ var codexCapabilities = (host, availability = {}) => {
27660
27791
  reason: "file writes are host-policy; PreToolUse allows write tools",
27661
27792
  refs: [ref(host, "PreToolUse")]
27662
27793
  },
27794
+ {
27795
+ name: "fresh-context-review",
27796
+ surface: "SubagentStart",
27797
+ assurance: has("subagentStart") ? "agent_guided" : "unavailable",
27798
+ reason: has("subagentStart") ? "independent review runs as a bounded subagent; evidence evaluation enforces reviewer exclusivity, and stops remain untrusted" : "Codex subagent lifecycle hooks are unavailable for independent review",
27799
+ refs: [ref(host, "SubagentStart")]
27800
+ },
27663
27801
  {
27664
27802
  name: "native_subagents",
27665
27803
  surface: "SubagentStart/SubagentStop",