@brainervirus/workit-cursor 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.
@@ -7492,7 +7492,7 @@ var SCHEMA_VERSION = 1, POLICY_VERSION = "1.0.0", OPERATION_FAMILIES, uuidPatter
7492
7492
  const covers = (path) => normOuter.paths.some((base) => base === "." || path === base || path.startsWith(`${base}/`)) && !excluded(path, normOuter);
7493
7493
  const innerExcludes = (path) => innerExclusions.some((excludedPath) => excludedPath === path || path.startsWith(`${excludedPath}/`));
7494
7494
  return innerPaths.every(covers) && !normOuter.exclusions.some((excludedPath) => innerPaths.some((base) => (base === "." || excludedPath === base || excludedPath.startsWith(`${base}/`)) && !innerExcludes(excludedPath)));
7495
- }, refSchema, provenanceSchema, entrySchema = (data) => object({ id, recordedAt: utc, provenance: provenanceSchema, data }).strict(), factSchema, signalSchema, signalSet, consequenceSchema, verificationSchema, assessmentSchema, constraintSchema, intentSchema, progressSchema, dimensionSchema, policySchema, requirementSchema, policyChangeSchema, candidateSchema, evidenceSchema, evidenceEvaluationSchema, decisionSchema, findingSchema, assignmentSchema, workerReportSchema, workerSchema, ownerSchema, closureSchema, actionProgressSchema, actionProgressListSchema, taskRecordSchema, workspaceRecordSchema, capabilitySchema, requirementEvaluationSchema, taskViewSchema, taskSummarySchema, exportBundleSchema, callerSchema, base, revisions, taskId, operation = (shape) => object({ ...base, ...shape }).strict(), taskOperations, policyOperations, evidenceOperations, findingOperations, decisionOperations, workerOperations, writerOperations, stateOperations, operationSchemas, compiledOperationSchemas, success = (revisionValue, workspaceRevisionValue, data) => ({
7495
+ }, refSchema, provenanceSchema, entrySchema = (data) => object({ id, recordedAt: utc, provenance: provenanceSchema, data }).strict(), factSchema, signalSchema, signalSet, consequenceSchema, verificationSchema, assessmentSchema, constraintSchema, intentSchema, progressSchema, dimensionSchema, policySchema, requirementSchema, policyChangeSchema, candidateSchema, evidenceSchema, evidenceEvaluationSchema, decisionSchema, findingSchema, assignmentSchema, workerReportSchema, workerSchema, ownerSchema, closureSchema, actionProgressSchema, actionProgressListSchema, runtimeSchema, taskRecordSchema, workspaceRecordSchema, capabilitySchema, requirementEvaluationSchema, taskViewSchema, taskSummarySchema, exportBundleSchema, callerSchema, base, revisions, taskId, operation = (shape) => object({ ...base, ...shape }).strict(), taskOperations, policyOperations, evidenceOperations, findingOperations, decisionOperations, workerOperations, writerOperations, stateOperations, operationSchemas, compiledOperationSchemas, success = (revisionValue, workspaceRevisionValue, data) => ({
7496
7496
  ok: true,
7497
7497
  schemaVersion: 1,
7498
7498
  revision: revisionValue,
@@ -7794,6 +7794,7 @@ var init_task_contract = __esm(() => {
7794
7794
  scope: scopeSchema,
7795
7795
  presented: text,
7796
7796
  approvedContent: text,
7797
+ displayed: text.optional(),
7797
7798
  contentRefs: array(refSchema)
7798
7799
  }).strict(),
7799
7800
  digest,
@@ -7872,6 +7873,10 @@ var init_task_contract = __esm(() => {
7872
7873
  path: ["decisionId"]
7873
7874
  });
7874
7875
  });
7876
+ runtimeSchema = object({
7877
+ createdWith: text.nullable(),
7878
+ updatedWith: text
7879
+ }).strict();
7875
7880
  taskRecordSchema = object({
7876
7881
  schemaVersion: literal(1),
7877
7882
  id,
@@ -7885,6 +7890,8 @@ var init_task_contract = __esm(() => {
7885
7890
  status: _enum(["active", "paused", "closed"]),
7886
7891
  closure: closureSchema.nullable(),
7887
7892
  progress: progressSchema,
7893
+ pauseReason: text.nullable().optional(),
7894
+ runtime: runtimeSchema.optional(),
7888
7895
  assessments: array(entrySchema(assessmentSchema)),
7889
7896
  policy: policySchema.nullable(),
7890
7897
  policyChanges: array(policyChangeSchema),
@@ -7900,6 +7907,7 @@ var init_task_contract = __esm(() => {
7900
7907
  id,
7901
7908
  revision,
7902
7909
  root: nonEmpty,
7910
+ runtime: runtimeSchema.optional(),
7903
7911
  writer: object({ state: _enum(["held", "uncertain"]), owner: ownerSchema, acquiredAt: utc }).strict().nullable()
7904
7912
  }).strict();
7905
7913
  capabilitySchema = object({
@@ -7927,6 +7935,9 @@ var init_task_contract = __esm(() => {
7927
7935
  id,
7928
7936
  revision,
7929
7937
  workspaceRevision: revision,
7938
+ createdAt: utc,
7939
+ updatedAt: utc,
7940
+ runtime: runtimeSchema.nullable(),
7930
7941
  objective: text,
7931
7942
  status: _enum(["active", "paused", "closed"]),
7932
7943
  closure: closureSchema.nullable(),
@@ -7996,7 +8007,7 @@ var init_task_contract = __esm(() => {
7996
8007
  expectedWorkspaceRevision: revision.optional(),
7997
8008
  outcome: outcomeSchema,
7998
8009
  summary: text,
7999
- decisionIds: array(id)
8010
+ decisionIds: array(id).optional()
8000
8011
  })
8001
8012
  };
8002
8013
  policyOperations = {
@@ -8088,14 +8099,15 @@ var init_task_contract = __esm(() => {
8088
8099
  ...taskId,
8089
8100
  expectedRevision: revision.optional(),
8090
8101
  expectedWorkspaceRevision: revision.optional(),
8091
- workerId: nullableId.optional()
8102
+ workerId: nullableId.optional(),
8103
+ reason: text.optional()
8092
8104
  }),
8093
8105
  release: operation({
8094
8106
  action: literal("release"),
8095
8107
  ...taskId,
8096
8108
  expectedRevision: revision.optional(),
8097
8109
  expectedWorkspaceRevision: revision.optional(),
8098
- reason: text
8110
+ reason: text.optional()
8099
8111
  })
8100
8112
  };
8101
8113
  stateOperations = {
@@ -8130,6 +8142,21 @@ var init_task_contract = __esm(() => {
8130
8142
  compiledOperationSchemas = Object.fromEntries(OPERATION_FAMILIES.map((family) => [family, compile(operationSchemas[family])]));
8131
8143
  });
8132
8144
 
8145
+ // packages/workit-core/src/core/package-root.ts
8146
+ import { existsSync } from "node:fs";
8147
+ import path from "node:path";
8148
+ import { fileURLToPath } from "node:url";
8149
+ var packageRoot = () => {
8150
+ let dir = path.dirname(fileURLToPath(import.meta.url));
8151
+ while (true) {
8152
+ const parent = path.dirname(dir);
8153
+ if (existsSync(path.join(dir, "package.json")) || parent === dir)
8154
+ return dir;
8155
+ dir = parent;
8156
+ }
8157
+ };
8158
+ var init_package_root = () => {};
8159
+
8133
8160
  // node_modules/@openclaw/fs-safe/dist/errors.js
8134
8161
  function categorizeFsSafeError(code) {
8135
8162
  return OPERATIONAL_CODES.has(code) ? "operational" : "policy";
@@ -8240,7 +8267,7 @@ var init_read_open_flags = () => {};
8240
8267
  // node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
8241
8268
  import { randomBytes } from "node:crypto";
8242
8269
  import fsSync from "node:fs";
8243
- import path from "node:path";
8270
+ import path2 from "node:path";
8244
8271
  function parseSidecarLockSnapshot(snapshot, parser) {
8245
8272
  return snapshot && { ...snapshot, payload: parseSidecarLockPayload(snapshot.raw, parser) };
8246
8273
  }
@@ -8268,13 +8295,13 @@ ${ownershipToken}
8268
8295
  return { raw, ownershipToken };
8269
8296
  }
8270
8297
  function relativeSidecarLockPath(lockRoot, lockPath) {
8271
- const resolved = path.resolve(lockPath);
8272
- const lexicalRelative = path.relative(lockRoot.rootDir, resolved);
8273
- const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path.sep}`) && !path.isAbsolute(lexicalRelative) ? lexicalRelative : path.relative(lockRoot.rootReal, resolved);
8274
- if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
8298
+ const resolved = path2.resolve(lockPath);
8299
+ const lexicalRelative = path2.relative(lockRoot.rootDir, resolved);
8300
+ const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(lexicalRelative) ? lexicalRelative : path2.relative(lockRoot.rootReal, resolved);
8301
+ if (!relative || relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative)) {
8275
8302
  throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
8276
8303
  }
8277
- return relative.split(path.sep).join(path.posix.sep);
8304
+ return relative.split(path2.sep).join(path2.posix.sep);
8278
8305
  }
8279
8306
  function parseSidecarLockPayload(raw, parser) {
8280
8307
  if (parser) {
@@ -8444,7 +8471,7 @@ function sleepSync(ms) {
8444
8471
 
8445
8472
  // node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
8446
8473
  import fs3 from "node:fs";
8447
- import path2 from "node:path";
8474
+ import path3 from "node:path";
8448
8475
  function getSyncHeldLocks() {
8449
8476
  const globalWithState = globalThis;
8450
8477
  if (!globalWithState[SYNC_HELD_LOCKS_KEY]) {
@@ -8522,26 +8549,26 @@ function canonicalLockParentSync(parent) {
8522
8549
  return process.platform === "win32" ? fs3.realpathSync.native(parent) : fs3.realpathSync(parent);
8523
8550
  }
8524
8551
  function normalizeTargetPath(targetPath) {
8525
- const resolved = path2.resolve(targetPath);
8526
- fs3.mkdirSync(path2.dirname(resolved), { recursive: true });
8552
+ const resolved = path3.resolve(targetPath);
8553
+ fs3.mkdirSync(path3.dirname(resolved), { recursive: true });
8527
8554
  try {
8528
- return path2.join(canonicalLockParentSync(path2.dirname(resolved)), path2.basename(resolved));
8555
+ return path3.join(canonicalLockParentSync(path3.dirname(resolved)), path3.basename(resolved));
8529
8556
  } catch {
8530
8557
  return resolved;
8531
8558
  }
8532
8559
  }
8533
8560
  function boundedLockPath(lockPath, lockRoot) {
8534
- const resolved = path2.resolve(lockPath);
8561
+ const resolved = path3.resolve(lockPath);
8535
8562
  if (!lockRoot)
8536
8563
  return resolved;
8537
8564
  relativeSidecarLockPath(lockRoot, resolved);
8538
- const parent = path2.dirname(resolved);
8565
+ const parent = path3.dirname(resolved);
8539
8566
  const parentReal = canonicalLockParentSync(parent);
8540
- const parentRelative = path2.relative(lockRoot.rootReal, parentReal);
8541
- if (parentRelative === ".." || parentRelative.startsWith(`..${path2.sep}`) || path2.isAbsolute(parentRelative)) {
8567
+ const parentRelative = path3.relative(lockRoot.rootReal, parentReal);
8568
+ if (parentRelative === ".." || parentRelative.startsWith(`..${path3.sep}`) || path3.isAbsolute(parentRelative)) {
8542
8569
  throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
8543
8570
  }
8544
- return path2.join(parentReal, path2.basename(resolved));
8571
+ return path3.join(parentReal, path3.basename(resolved));
8545
8572
  }
8546
8573
  function defaultShouldReclaim(snapshot, staleMs, nowMs) {
8547
8574
  const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
@@ -8760,17 +8787,17 @@ var init_file_lock = __esm(() => {
8760
8787
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
8761
8788
  import * as fs4 from "node:fs";
8762
8789
  import { hostname } from "node:os";
8763
- import path3 from "node:path";
8790
+ import path4 from "node:path";
8764
8791
 
8765
8792
  class TaskStore {
8766
8793
  root;
8767
8794
  constructor(root) {
8768
- this.root = fs4.existsSync(root) ? fs4.realpathSync(root) : path3.resolve(root);
8795
+ this.root = fs4.existsSync(root) ? fs4.realpathSync(root) : path4.resolve(root);
8769
8796
  }
8770
8797
  readTask(taskId) {
8771
8798
  if (!validId(taskId))
8772
8799
  return failure("invalid_input", "task ID is invalid", { taskId });
8773
- const result = this.readRecord(path3.join(this.tasksDir, `${taskId}.json`), taskRecordSchema);
8800
+ const result = this.readRecord(path4.join(this.tasksDir, `${taskId}.json`), taskRecordSchema);
8774
8801
  if (!result.exists)
8775
8802
  return failure("not_found", "task not found", { taskId });
8776
8803
  if (result.result.ok && result.result.data.id !== taskId)
@@ -8804,7 +8831,7 @@ class TaskStore {
8804
8831
  });
8805
8832
  const tasks = [];
8806
8833
  for (const name of names.sort()) {
8807
- const item = this.readRecord(path3.join(this.tasksDir, name), taskRecordSchema);
8834
+ const item = this.readRecord(path4.join(this.tasksDir, name), taskRecordSchema);
8808
8835
  if (!item.exists)
8809
8836
  continue;
8810
8837
  if (!item.result.ok)
@@ -8838,7 +8865,7 @@ class TaskStore {
8838
8865
  if (!current.ok)
8839
8866
  return current;
8840
8867
  if (current.data ? value.expectedWorkspaceRevision !== current.data.revision : value.expectedWorkspaceRevision !== null) {
8841
- return failure("revision_conflict", "workspace revision does not match", {
8868
+ return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
8842
8869
  expectedWorkspaceRevision: value.expectedWorkspaceRevision,
8843
8870
  actualWorkspaceRevision: current.data?.revision ?? null
8844
8871
  });
@@ -8850,11 +8877,17 @@ class TaskStore {
8850
8877
  const previousWorkspaceBytes = current.data ? this.snapshotBytes(this.workspacePath) : null;
8851
8878
  if (current.data && !previousWorkspaceBytes)
8852
8879
  return failure("storage_error", "workspace snapshot disappeared during creation");
8853
- const workspace = current.data ? { ...current.data, revision: newRevision(), root: this.root } : {
8880
+ const workspace = current.data ? {
8881
+ ...current.data,
8882
+ revision: newRevision(),
8883
+ root: this.root,
8884
+ runtime: stampNew(current.data)
8885
+ } : {
8854
8886
  schemaVersion: SCHEMA_VERSION,
8855
8887
  id: newId(),
8856
8888
  revision: newRevision(),
8857
8889
  root: this.root,
8890
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
8858
8891
  writer: null
8859
8892
  };
8860
8893
  const timestamp = value.now ?? now();
@@ -8876,6 +8909,7 @@ class TaskStore {
8876
8909
  status: "active",
8877
8910
  closure: null,
8878
8911
  progress: { summary: "", nextAction: null, blockers: [] },
8912
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
8879
8913
  assessments: [],
8880
8914
  policy: null,
8881
8915
  policyChanges: [],
@@ -8906,7 +8940,7 @@ class TaskStore {
8906
8940
  if (!current.ok)
8907
8941
  return current;
8908
8942
  if (current.data ? input.expectedWorkspaceRevision !== current.data.revision : input.expectedWorkspaceRevision !== null)
8909
- return failure("revision_conflict", "workspace revision does not match", {
8943
+ return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
8910
8944
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
8911
8945
  actualWorkspaceRevision: current.data?.revision ?? null
8912
8946
  });
@@ -8914,11 +8948,17 @@ class TaskStore {
8914
8948
  if (current.data && !previousWorkspaceBytes)
8915
8949
  return failure("storage_error", "workspace snapshot disappeared during import");
8916
8950
  const timestamp = input.now ?? now();
8917
- const workspace = current.data ? { ...current.data, revision: newRevision(), root: this.root } : {
8951
+ const workspace = current.data ? {
8952
+ ...current.data,
8953
+ revision: newRevision(),
8954
+ root: this.root,
8955
+ runtime: stampNew(current.data)
8956
+ } : {
8918
8957
  schemaVersion: SCHEMA_VERSION,
8919
8958
  id: input.workspaceId ?? newId(),
8920
8959
  revision: newRevision(),
8921
8960
  root: this.root,
8961
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
8922
8962
  writer: null
8923
8963
  };
8924
8964
  const task = {
@@ -8926,7 +8966,11 @@ class TaskStore {
8926
8966
  workspaceId: workspace.id,
8927
8967
  revision: newRevision(),
8928
8968
  createdAt: timestamp,
8929
- updatedAt: timestamp
8969
+ updatedAt: timestamp,
8970
+ runtime: {
8971
+ createdWith: input.task.runtime?.createdWith ?? null,
8972
+ updatedWith: runtimeVersion()
8973
+ }
8930
8974
  };
8931
8975
  const validTask = taskRecordSchema.safeParse(task);
8932
8976
  if (!validTask.success)
@@ -8975,7 +9019,8 @@ class TaskStore {
8975
9019
  workspaceId: current.data.workspaceId,
8976
9020
  createdAt: current.data.createdAt,
8977
9021
  revision: context.revision,
8978
- updatedAt: context.now
9022
+ updatedAt: context.now,
9023
+ runtime: stampNew(current.data)
8979
9024
  };
8980
9025
  const valid = taskRecordSchema.safeParse(record);
8981
9026
  if (!valid.success)
@@ -9009,7 +9054,8 @@ class TaskStore {
9009
9054
  ...changed.data,
9010
9055
  id: current.data.id,
9011
9056
  root: this.root,
9012
- revision: context.revision
9057
+ revision: context.revision,
9058
+ runtime: stampNew(current.data)
9013
9059
  };
9014
9060
  const valid = workspaceRecordSchema.safeParse(record);
9015
9061
  if (!valid.success)
@@ -9052,7 +9098,8 @@ class TaskStore {
9052
9098
  ...changedWorkspace.data,
9053
9099
  id: workspace.data.id,
9054
9100
  root: this.root,
9055
- revision: workspaceContext.revision
9101
+ revision: workspaceContext.revision,
9102
+ runtime: stampNew(workspace.data)
9056
9103
  });
9057
9104
  if (!nextWorkspace.success)
9058
9105
  return failure("invalid_input", "workspace mutation produced an invalid record");
@@ -9077,7 +9124,8 @@ class TaskStore {
9077
9124
  workspaceId: task.data.workspaceId,
9078
9125
  createdAt: task.data.createdAt,
9079
9126
  revision: taskContext.revision,
9080
- updatedAt: taskContext.now
9127
+ updatedAt: taskContext.now,
9128
+ runtime: stampNew(task.data)
9081
9129
  });
9082
9130
  if (!nextTask.success) {
9083
9131
  this.markUncertain(nextWorkspace.data);
@@ -9104,7 +9152,7 @@ class TaskStore {
9104
9152
  if (match)
9105
9153
  candidates.push({
9106
9154
  target: match[1],
9107
- path: path3.join(this.recoveryDir, name),
9155
+ path: path4.join(this.recoveryDir, name),
9108
9156
  digest: match[3]
9109
9157
  });
9110
9158
  }
@@ -9297,7 +9345,8 @@ class TaskStore {
9297
9345
  const value = {
9298
9346
  ...workspace,
9299
9347
  writer: { ...workspace.writer, state: "uncertain" },
9300
- revision: newRevision()
9348
+ revision: newRevision(),
9349
+ runtime: stampNew(workspace)
9301
9350
  };
9302
9351
  this.replaceSnapshot(this.workspacePath, value, workspace);
9303
9352
  }
@@ -9375,7 +9424,7 @@ class TaskStore {
9375
9424
  }
9376
9425
  fs4.renameSync(temporary, file);
9377
9426
  temporary = undefined;
9378
- this.fsyncDirectory(path3.dirname(file));
9427
+ this.fsyncDirectory(path4.dirname(file));
9379
9428
  return success(null, null, value);
9380
9429
  } catch (error) {
9381
9430
  return failure("storage_error", `snapshot replacement failed: ${String(error)}`, {
@@ -9389,9 +9438,9 @@ class TaskStore {
9389
9438
  }
9390
9439
  }
9391
9440
  saveRecovery(file, bytes) {
9392
- const target = path3.basename(file) === "workspace.json" ? "workspace" : "task";
9393
- const id = target === "task" ? path3.basename(file, ".json") : "workspace";
9394
- const destination = path3.join(this.recoveryDir, `${target}.${id}.${digestBytes(bytes)}.json`);
9441
+ const target = path4.basename(file) === "workspace.json" ? "workspace" : "task";
9442
+ const id = target === "task" ? path4.basename(file, ".json") : "workspace";
9443
+ const destination = path4.join(this.recoveryDir, `${target}.${id}.${digestBytes(bytes)}.json`);
9395
9444
  if (fs4.existsSync(destination)) {
9396
9445
  if (digestBytes(fs4.readFileSync(destination)) === digestBytes(bytes))
9397
9446
  return;
@@ -9469,7 +9518,7 @@ class TaskStore {
9469
9518
  for (const name of fs4.readdirSync(this.tasksDir)) {
9470
9519
  if (!name.endsWith(".json") || !validId(name.slice(0, -5)))
9471
9520
  return null;
9472
- const parsed = this.parseBytes(fs4.readFileSync(path3.join(this.tasksDir, name)), taskRecordSchema);
9521
+ const parsed = this.parseBytes(fs4.readFileSync(path4.join(this.tasksDir, name)), taskRecordSchema);
9473
9522
  if (!parsed.ok || parsed.data.id !== name.slice(0, -5))
9474
9523
  return null;
9475
9524
  ids.add(parsed.data.workspaceId);
@@ -9511,7 +9560,12 @@ class TaskStore {
9511
9560
  if (isObject2(value) && "schemaVersion" in value && value.schemaVersion !== SCHEMA_VERSION)
9512
9561
  return failure("unsupported_version", "unsupported snapshot schema version");
9513
9562
  const parsed = schema.safeParse(value);
9514
- return parsed.success ? success(null, null, parsed.data) : failure("recovery_required", "snapshot does not satisfy its schema");
9563
+ if (parsed.success)
9564
+ return success(null, null, parsed.data);
9565
+ const writerVersion = isObject2(value) && isObject2(value.runtime) ? value.runtime.updatedWith : null;
9566
+ if (typeof writerVersion === "string" && isNewerVersion(writerVersion, runtimeVersion()))
9567
+ return failure("recovery_required", `snapshot was written by workit ${writerVersion}; upgrade Workit before mutating this checkout`);
9568
+ return failure("recovery_required", "snapshot does not satisfy its schema");
9515
9569
  }
9516
9570
  processStart(pid) {
9517
9571
  try {
@@ -9521,34 +9575,57 @@ class TaskStore {
9521
9575
  }
9522
9576
  }
9523
9577
  conflict(expected, actual) {
9524
- return failure("revision_conflict", "snapshot revision does not match", {
9578
+ return failure("revision_conflict", "snapshot revision does not match; omit expectedRevision to use the current record", {
9525
9579
  expectedRevision: expected,
9526
9580
  actualRevision: actual
9527
9581
  });
9528
9582
  }
9529
9583
  taskPath(taskId) {
9530
- return path3.join(this.tasksDir, `${taskId}.json`);
9584
+ return path4.join(this.tasksDir, `${taskId}.json`);
9531
9585
  }
9532
9586
  get workitDir() {
9533
- return path3.join(this.root, ".workit");
9587
+ return path4.join(this.root, ".workit");
9534
9588
  }
9535
9589
  get tasksDir() {
9536
- return path3.join(this.workitDir, "tasks");
9590
+ return path4.join(this.workitDir, "tasks");
9537
9591
  }
9538
9592
  get recoveryDir() {
9539
- return path3.join(this.workitDir, "recovery");
9593
+ return path4.join(this.workitDir, "recovery");
9540
9594
  }
9541
9595
  get workspacePath() {
9542
- return path3.join(this.workitDir, "workspace.json");
9596
+ return path4.join(this.workitDir, "workspace.json");
9543
9597
  }
9544
9598
  get lockPath() {
9545
- return path3.join(this.workitDir, "metadata.lock");
9599
+ return path4.join(this.workitDir, "metadata.lock");
9546
9600
  }
9547
9601
  get gitignorePath() {
9548
- return path3.join(this.workitDir, ".gitignore");
9602
+ return path4.join(this.workitDir, ".gitignore");
9549
9603
  }
9550
9604
  }
9551
- var processEvidenceSchema, metadataLockSchema, now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"), jsonBytes = (value) => `${canonicalJson(value)}
9605
+ var processEvidenceSchema, metadataLockSchema, now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"), cachedRuntimeVersion = null, runtimeVersion = () => {
9606
+ if (cachedRuntimeVersion === null) {
9607
+ try {
9608
+ const pkg = JSON.parse(fs4.readFileSync(path4.join(packageRoot(), "package.json"), "utf8"));
9609
+ cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
9610
+ } catch {
9611
+ cachedRuntimeVersion = "0.0.0";
9612
+ }
9613
+ }
9614
+ return cachedRuntimeVersion;
9615
+ }, stampNew = (record) => ({
9616
+ createdWith: record.runtime?.createdWith ?? null,
9617
+ updatedWith: runtimeVersion()
9618
+ }), isNewerVersion = (candidate, current) => {
9619
+ const parts = (value) => value.split("-")[0].split(".").map((item) => Number.parseInt(item, 10) || 0);
9620
+ const [next, now] = [parts(candidate), parts(current)];
9621
+ for (let index = 0;index < 3; index += 1) {
9622
+ const left = next[index] ?? 0;
9623
+ const right = now[index] ?? 0;
9624
+ if (left !== right)
9625
+ return left > right;
9626
+ }
9627
+ return false;
9628
+ }, jsonBytes = (value) => `${canonicalJson(value)}
9552
9629
  `, digestBytes = (value) => createHash2("sha256").update(value).digest("hex"), isObject2 = (value) => typeof value === "object" && value !== null, validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value), validDigest = (value) => /^[0-9a-f]{64}$/.test(value), parseMetadataLock = (raw) => {
9553
9630
  let value;
9554
9631
  try {
@@ -9566,6 +9643,7 @@ var processEvidenceSchema, metadataLockSchema, now = () => new Date().toISOStrin
9566
9643
  };
9567
9644
  var init_task_store = __esm(() => {
9568
9645
  init_zod();
9646
+ init_package_root();
9569
9647
  init_file_lock();
9570
9648
  init_task_contract();
9571
9649
  processEvidenceSchema = object({
@@ -9582,10 +9660,63 @@ var init_task_store = __esm(() => {
9582
9660
  }).strict();
9583
9661
  });
9584
9662
 
9663
+ // packages/workit-core/src/core/workers.ts
9664
+ function assertProductWriteAllowed(input) {
9665
+ if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
9666
+ return failure("invalid_input", "invalid product write path");
9667
+ if (!input.store)
9668
+ return failure("permission_denied", "write authorization requires core state");
9669
+ const task = input.store.readTask(input.task.id);
9670
+ if (!task.ok)
9671
+ return task;
9672
+ const workspace = input.store.readWorkspace();
9673
+ if (!workspace.ok)
9674
+ return workspace;
9675
+ if (!workspace.data)
9676
+ return failure("not_found", "workspace not found");
9677
+ const workspaceRecord = workspace.data;
9678
+ if (task.data.status !== "active")
9679
+ return failure("invalid_transition", "paused or closed tasks cannot own product writes");
9680
+ if (workspaceRecord.id !== task.data.workspaceId || workspaceRecord.root.length === 0)
9681
+ return failure("recovery_required", "task and workspace bindings are invalid");
9682
+ const writer = workspaceRecord.writer;
9683
+ if (!writer)
9684
+ return failure("permission_denied", "checkout has no writer owner");
9685
+ if (writer.state === "uncertain")
9686
+ return failure("recovery_required", "checkout writer requires recovery", {
9687
+ owner: writer.owner
9688
+ });
9689
+ const caller = callerValue(input.caller);
9690
+ const workerId = workerIdOf(input.caller);
9691
+ const owner = writer.owner;
9692
+ if (owner.taskId !== task.data.id || owner.workerId !== workerId || owner.session.host !== caller.host || owner.session.handle !== caller.actor)
9693
+ return failure("writer_conflict", "checkout is owned by another validated actor", { owner });
9694
+ const worker = workerId === null ? null : task.data.workers.find((entry) => entry.id === workerId);
9695
+ if (workerId !== null && (!worker || worker.data.assignment.role !== "implementer"))
9696
+ return failure("permission_denied", "only implementers can own product writes");
9697
+ 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))))
9698
+ return failure("recovery_required", "worker session is not an active writer");
9699
+ return success(null, workspaceRecord.revision, owner);
9700
+ }
9701
+ var same = (left, right) => {
9702
+ try {
9703
+ return canonicalJson(left) === canonicalJson(right);
9704
+ } catch {
9705
+ return false;
9706
+ }
9707
+ }, UNCERTAIN_WORKER_STATES, isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state), callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor }, validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
9708
+ const code = char.charCodeAt(0);
9709
+ return code < 32 || code === 127;
9710
+ }) && !value.split(/[\\/]/).includes(".."), callerValue = (value) => ({ host: value.host, actor: value.actor }), workerIdOf = (value) => value.workerId ?? null;
9711
+ var init_workers = __esm(() => {
9712
+ init_task_contract();
9713
+ UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
9714
+ });
9715
+
9585
9716
  // packages/workit-core/src/core/authority.ts
9586
9717
  import { createHash as createHash3 } from "node:crypto";
9587
9718
  import * as fs5 from "node:fs";
9588
- import path4 from "node:path";
9719
+ import path5 from "node:path";
9589
9720
  function applicableDecision(task, purpose, binding, checkoutRoot) {
9590
9721
  if (binding.taskId !== task.id || binding.workspaceId !== task.workspaceId)
9591
9722
  return [];
@@ -9969,20 +10100,31 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
9969
10100
  continue;
9970
10101
  if (!reference.digest)
9971
10102
  return failure("invalid_input", "document references require a byte digest");
9972
- const target = path4.resolve(root, reference.path);
9973
- if (target !== root && !target.startsWith(`${root}${path4.sep}`))
9974
- return failure("invalid_input", "document reference escapes checkout");
10103
+ const target = path5.resolve(root, reference.path);
10104
+ if (target !== root && !target.startsWith(`${root}${path5.sep}`))
10105
+ return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
10106
+ fields: [
10107
+ { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
10108
+ ]
10109
+ });
9975
10110
  try {
9976
- const relative = path4.relative(root, target);
10111
+ const relative = path5.relative(root, target);
9977
10112
  let current = root;
9978
- for (const segment of relative.split(path4.sep)) {
10113
+ for (const segment of relative.split(path5.sep)) {
9979
10114
  if (!segment)
9980
10115
  continue;
9981
- current = path4.join(current, segment);
10116
+ current = path5.join(current, segment);
9982
10117
  const stat = fs5.lstatSync(current);
9983
10118
  const real = fs5.realpathSync(current);
9984
- if (real !== root && !real.startsWith(`${root}${path4.sep}`))
9985
- return failure("invalid_input", "document reference escapes checkout");
10119
+ if (real !== root && !real.startsWith(`${root}${path5.sep}`))
10120
+ return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
10121
+ fields: [
10122
+ {
10123
+ path: "contentRefs.path",
10124
+ reason: `${reference.path} resolves outside the checkout`
10125
+ }
10126
+ ]
10127
+ });
9986
10128
  if (stat.isSymbolicLink())
9987
10129
  return failure("invalid_input", "document reference uses a symlink");
9988
10130
  if (current === target && !stat.isFile())
@@ -10059,7 +10201,7 @@ var init_authority = __esm(() => {
10059
10201
  import { createHash as createHash4 } from "node:crypto";
10060
10202
  import * as fs6 from "node:fs";
10061
10203
  import { spawnSync } from "node:child_process";
10062
- import path5 from "node:path";
10204
+ import path6 from "node:path";
10063
10205
  function captureCandidate(root, scope, environment = []) {
10064
10206
  let checkout;
10065
10207
  try {
@@ -10069,7 +10211,7 @@ function captureCandidate(root, scope, environment = []) {
10069
10211
  }
10070
10212
  const normalizedScope = canonicalScope(scope);
10071
10213
  if (!normalizedScope)
10072
- return failure("invalid_input", "candidate scope is invalid");
10214
+ 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" }] });
10073
10215
  const roots = scopeRoots(checkout, normalizedScope);
10074
10216
  if (!roots.ok)
10075
10217
  return roots;
@@ -10101,7 +10243,7 @@ function captureCandidate(root, scope, environment = []) {
10101
10243
  for (const item of [...names].sort()) {
10102
10244
  if (!scopeMatches(item, normalizedScope))
10103
10245
  continue;
10104
- const target = path5.join(checkout, item);
10246
+ const target = path6.join(checkout, item);
10105
10247
  try {
10106
10248
  const stat = fs6.lstatSync(target);
10107
10249
  if (stat.isSymbolicLink()) {
@@ -10220,7 +10362,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
10220
10362
  return true;
10221
10363
  const reviewSession = sessionFromRef(entry.data.reviewContext);
10222
10364
  const sameImplementation = sameSession(reviewSession, task.intent.provenance.session);
10223
- const sameEvidenceSession = task.evidence.some((other) => other.id !== entry.id && sameSession(reviewSession, other.provenance.session));
10365
+ const sameEvidenceSession = task.evidence.some((other) => other.id !== entry.id && other.data.kind === "review" && sameSession(reviewSession, other.provenance.session));
10224
10366
  return entry.data.kind === "review" && reviewSession !== null && sameSession(reviewSession, entry.provenance.session) && (requirement.ruleId === "self-review" || !sameImplementation && !sameEvidenceSession);
10225
10367
  });
10226
10368
  if (passed.length) {
@@ -10316,7 +10458,17 @@ function evaluateClosure(requestedOutcome, view) {
10316
10458
  const blocking = evaluations.filter((item) => item.status === "unsatisfied" || item.status === "unavailable");
10317
10459
  if (requestedOutcome !== "stopped" && blocking.length)
10318
10460
  return failure("requirements_unsatisfied", "applicable requirements are unsatisfied", {
10319
- requirementIds: blocking.map((item) => item.requirementId)
10461
+ requirementIds: blocking.map((item) => item.requirementId),
10462
+ requirements: blocking.map((item) => {
10463
+ const requirement = view.task.policy?.requirements.find((candidate) => candidate.id === item.requirementId);
10464
+ return {
10465
+ requirementId: item.requirementId,
10466
+ ruleId: requirement?.ruleId ?? "unknown",
10467
+ reason: item.reason,
10468
+ satisfaction: requirement?.satisfaction ?? "",
10469
+ dependentAction: requirement?.dependentAction ?? null
10470
+ };
10471
+ })
10320
10472
  });
10321
10473
  const accepted = evaluations.filter((item) => item.status === "accepted_limitation");
10322
10474
  if (requestedOutcome === "verified" && accepted.length)
@@ -10342,12 +10494,12 @@ function evaluateClosure(requestedOutcome, view) {
10342
10494
  requirementIds: evaluations.map((item) => item.requirementId)
10343
10495
  });
10344
10496
  }
10345
- var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path5.sep}`), relative = (root, candidate) => path5.relative(root, candidate).split(path5.sep).join("/") || ".", canonicalPath = (value) => {
10497
+ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path6.sep}`), relative = (root, candidate) => path6.relative(root, candidate).split(path6.sep).join("/") || ".", canonicalPath = (value) => {
10346
10498
  if (typeof value !== "string" || !value)
10347
10499
  return null;
10348
10500
  if (value.includes("\\") || value.split("/").includes(".."))
10349
10501
  return null;
10350
- const normalized = path5.posix.normalize(value);
10502
+ const normalized = path6.posix.normalize(value);
10351
10503
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
10352
10504
  return null;
10353
10505
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -10389,14 +10541,14 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
10389
10541
  git: /not a git repository/i.test(stderr) ? false : true
10390
10542
  };
10391
10543
  }
10392
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path5.sep).join("/")) : [];
10544
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path6.sep).join("/")) : [];
10393
10545
  const stagedDeleted = spawnSync("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
10394
10546
  if (stagedDeleted.status !== 0) {
10395
10547
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
10396
10548
  if (!/not a git repository/i.test(stderr))
10397
10549
  return { paths, uncertain: true, git: true };
10398
10550
  } else if (stagedDeleted.stdout) {
10399
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path5.sep).join("/")));
10551
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path6.sep).join("/")));
10400
10552
  }
10401
10553
  return { paths: [...new Set(paths)], uncertain: false, git: true };
10402
10554
  }, walk = (root, directory, output) => {
@@ -10410,7 +10562,7 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
10410
10562
  for (const name of names) {
10411
10563
  if (name === ".git" || name === ".workit")
10412
10564
  continue;
10413
- const target = path5.join(directory, name);
10565
+ const target = path6.join(directory, name);
10414
10566
  let stat;
10415
10567
  try {
10416
10568
  stat = fs6.lstatSync(target);
@@ -10427,17 +10579,17 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
10427
10579
  }, scopeRoots = (root, scope) => {
10428
10580
  const roots = [];
10429
10581
  for (const item of scope.paths.length ? scope.paths : ["."]) {
10430
- const target = path5.resolve(root, item);
10582
+ const target = path6.resolve(root, item);
10431
10583
  if (!inside(root, target))
10432
- return failure("invalid_input", "candidate scope escapes checkout");
10584
+ 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` }] });
10433
10585
  let ancestor = target;
10434
10586
  while (!fs6.existsSync(ancestor) && ancestor !== root)
10435
- ancestor = path5.dirname(ancestor);
10587
+ ancestor = path6.dirname(ancestor);
10436
10588
  try {
10437
10589
  if (!inside(root, fs6.realpathSync(ancestor)))
10438
- return failure("invalid_input", "candidate scope escapes checkout");
10590
+ 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` }] });
10439
10591
  } catch {
10440
- return failure("invalid_input", "candidate scope cannot be inspected");
10592
+ return failure("invalid_input", `candidate scope cannot be inspected: ${item}`);
10441
10593
  }
10442
10594
  roots.push(target);
10443
10595
  }
@@ -10532,7 +10684,18 @@ decision.record {binding with taskId/workspaceId from inspect plus presented and
10532
10684
  approvedContent} only through a native approval question asked receipt-shaped:
10533
10685
  header Workit decision: <purpose>, the same label repeated in the question
10534
10686
  text (some host UIs do not render headers), exactly two options approved and
10535
- rejected; ask once, never re-ask to mint a receipt. task.close {outcome,
10687
+ rejected; ask once, never re-ask to mint a receipt. Before asking, present the
10688
+ item in the conversation (durable artifacts: complete digest plus exact path;
10689
+ inline plans: the content) and keep the question to one short scoped sentence;
10690
+ never ask to approve something unseen. An answer with custom text is steering,
10691
+ not approval: adjust, re-present, then ask a new scoped question. After the user
10692
+ approves a spec or plan, execute the remaining tasks continuously with one
10693
+ atomic commit per task and do not ask whether to continue; stop only for a new
10694
+ product decision, a failed safety or verification gate, a conflicting edit, or
10695
+ missing authority. Record the plan's commit list once through the plan-scoped
10696
+ action approval (git.commit with plan_steps and plan_branch) so each listed
10697
+ commit executes without a new question; an unlisted message needs a fresh
10698
+ approval. task.close {outcome,
10536
10699
  summary, decisionIds}. A completed native subagent run stops its bound worker
10537
10700
  by itself; if a worker strands in cancelling with its run verifiably over,
10538
10701
  repeat worker.cancel {reason} to confirm the stop. Check and review evidence
@@ -10558,7 +10721,7 @@ var init_methods = __esm(() => {
10558
10721
  METHODS = {
10559
10722
  "workit-challenge": { dimensions: ["challenge", "decisions"] },
10560
10723
  "workit-behavioral-tdd": { dimensions: ["testing"] },
10561
- "workit-review": { dimensions: ["review"] },
10724
+ "workit-review": { dimensions: ["review"], ruleIds: ["fresh-context-review"] },
10562
10725
  "workit-plan": { dimensions: ["artifacts", "continuity"] },
10563
10726
  "workit-implement": { dimensions: ["delegation"] },
10564
10727
  "workit-debug": { ruleIds: ["root-cause-investigation"] },
@@ -10576,7 +10739,7 @@ function reconcileResume(view, observations = []) {
10576
10739
  return candidate;
10577
10740
  const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
10578
10741
  const blockers = [...view.task.progress.blockers];
10579
- if (view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
10742
+ if (view.task.workers.some((entry) => isUncertainWorker(entry.data.state)))
10580
10743
  blockers.push({
10581
10744
  reason: "worker state requires reconciliation",
10582
10745
  dependentAction: "resume",
@@ -10659,6 +10822,7 @@ var COMPACT_MAX_BYTES = 4096, COMPACT_MAX_ITEMS = 8, COMPACT_TEXT_BYTES = 128, s
10659
10822
  task: bundle.task
10660
10823
  });
10661
10824
  var init_task_context = __esm(() => {
10825
+ init_workers();
10662
10826
  init_task_contract();
10663
10827
  init_task_evaluation();
10664
10828
  init_methods();
@@ -10687,7 +10851,11 @@ var init_external_action = __esm(() => {
10687
10851
  }).strict(),
10688
10852
  object({
10689
10853
  operation: literal("git.commit"),
10690
- payload: object({ message: string2().min(1) }).strict()
10854
+ payload: object({
10855
+ message: string2().min(1).optional(),
10856
+ plan_steps: array(string2().min(1)).optional(),
10857
+ plan_branch: string2().min(1).optional()
10858
+ }).strict()
10691
10859
  }).strict(),
10692
10860
  object({
10693
10861
  operation: literal("git.push"),
@@ -10819,7 +10987,7 @@ function resolveRequirements(input) {
10819
10987
  ].join(", ")}`,
10820
10988
  satisfaction: "An observed or inferred fact resolves the uncertainty and records its supporting references.",
10821
10989
  before: "dependent_action",
10822
- dependentAction: "dependent-action",
10990
+ dependentAction: null,
10823
10991
  acceptanceAllowed: false
10824
10992
  }));
10825
10993
  if (signals.productChoiceOpen.value === true)
@@ -10830,7 +10998,7 @@ function resolveRequirements(input) {
10830
10998
  reason: `A consequential product choice remains open: ${signalReason(input, "productChoiceOpen")}`,
10831
10999
  satisfaction: "Present the material alternatives and record the user's decision before the dependent action.",
10832
11000
  before: "dependent_action",
10833
- dependentAction: "dependent-action",
11001
+ dependentAction: null,
10834
11002
  acceptanceAllowed: false
10835
11003
  }));
10836
11004
  if (signals.behaviorChange.value === true)
@@ -11082,58 +11250,6 @@ var init_policy_resolver = __esm(() => {
11082
11250
  }).strict();
11083
11251
  });
11084
11252
 
11085
- // packages/workit-core/src/core/workers.ts
11086
- function assertProductWriteAllowed(input) {
11087
- if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
11088
- return failure("invalid_input", "invalid product write path");
11089
- if (!input.store)
11090
- return failure("permission_denied", "write authorization requires core state");
11091
- const task = input.store.readTask(input.task.id);
11092
- if (!task.ok)
11093
- return task;
11094
- const workspace = input.store.readWorkspace();
11095
- if (!workspace.ok)
11096
- return workspace;
11097
- if (!workspace.data)
11098
- return failure("not_found", "workspace not found");
11099
- const workspaceRecord = workspace.data;
11100
- if (task.data.status !== "active")
11101
- return failure("invalid_transition", "paused or closed tasks cannot own product writes");
11102
- if (workspaceRecord.id !== task.data.workspaceId || workspaceRecord.root.length === 0)
11103
- return failure("recovery_required", "task and workspace bindings are invalid");
11104
- const writer = workspaceRecord.writer;
11105
- if (!writer)
11106
- return failure("permission_denied", "checkout has no writer owner");
11107
- if (writer.state === "uncertain")
11108
- return failure("recovery_required", "checkout writer requires recovery", {
11109
- owner: writer.owner
11110
- });
11111
- const caller = callerValue(input.caller);
11112
- const workerId = workerIdOf(input.caller);
11113
- const owner = writer.owner;
11114
- if (owner.taskId !== task.data.id || owner.workerId !== workerId || owner.session.host !== caller.host || owner.session.handle !== caller.actor)
11115
- return failure("writer_conflict", "checkout is owned by another validated actor", { owner });
11116
- const worker = workerId === null ? null : task.data.workers.find((entry) => entry.id === workerId);
11117
- if (workerId !== null && (!worker || worker.data.assignment.role !== "implementer"))
11118
- return failure("permission_denied", "only implementers can own product writes");
11119
- 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))))
11120
- return failure("recovery_required", "worker session is not an active writer");
11121
- return success(null, workspaceRecord.revision, owner);
11122
- }
11123
- var same = (left, right) => {
11124
- try {
11125
- return canonicalJson(left) === canonicalJson(right);
11126
- } catch {
11127
- return false;
11128
- }
11129
- }, callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor }, validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
11130
- const code = char.charCodeAt(0);
11131
- return code < 32 || code === 127;
11132
- }) && !value.split(/[\\/]/).includes(".."), callerValue = (value) => ({ host: value.host, actor: value.actor }), workerIdOf = (value) => value.workerId ?? null;
11133
- var init_workers = __esm(() => {
11134
- init_task_contract();
11135
- });
11136
-
11137
11253
  // packages/workit-core/src/core/task-engine.ts
11138
11254
  import { realpathSync as realpathSync4 } from "node:fs";
11139
11255
 
@@ -11195,7 +11311,7 @@ class WorkitCore {
11195
11311
  activeWorkerBlocker(task, workspace) {
11196
11312
  if (workspace.writer)
11197
11313
  return failure("recovery_required", "writer ownership must be released first");
11198
- if (task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
11314
+ if (task.workers.some((entry) => isUncertainWorker(entry.data.state)))
11199
11315
  return failure("recovery_required", "worker state requires reconciliation");
11200
11316
  return null;
11201
11317
  }
@@ -11432,11 +11548,6 @@ class WorkitCore {
11432
11548
  if (helper && !helper.ok)
11433
11549
  return helper;
11434
11550
  const evidence = input.evidence;
11435
- if (helper?.ok) {
11436
- const assignment = helper.data.data.assignment;
11437
- 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))
11438
- return failure("permission_denied", "evidence is outside the worker assignment");
11439
- }
11440
11551
  if ((evidence.result === "missing" || evidence.result === "skipped") && !evidence.summary.trim())
11441
11552
  return failure("invalid_input", "missing or skipped evidence requires a reason");
11442
11553
  if (evidence.kind === "review") {
@@ -11453,6 +11564,11 @@ class WorkitCore {
11453
11564
  evidence.beforeCandidateId ??= null;
11454
11565
  evidence.candidateId ??= null;
11455
11566
  }
11567
+ if (helper?.ok) {
11568
+ const assignment = helper.data.data.assignment;
11569
+ 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))
11570
+ return failure("permission_denied", "evidence is outside the worker assignment");
11571
+ }
11456
11572
  const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
11457
11573
  for (const candidateId of [evidence.beforeCandidateId, evidence.candidateId]) {
11458
11574
  if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
@@ -12348,6 +12464,9 @@ class WorkitCore {
12348
12464
  id: task.id,
12349
12465
  revision: task.revision,
12350
12466
  workspaceRevision: workspace.data.revision,
12467
+ createdAt: task.createdAt,
12468
+ updatedAt: task.updatedAt,
12469
+ runtime: task.runtime ?? null,
12351
12470
  objective: task.intent.data.objective,
12352
12471
  status: task.status,
12353
12472
  closure: task.closure,
@@ -12433,7 +12552,7 @@ class WorkitCore {
12433
12552
  const base = reconcileResume(effectiveView);
12434
12553
  if (!base.ok)
12435
12554
  return base;
12436
- const needsReconciliation = view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
12555
+ const needsReconciliation = view.task.workers.some((entry) => isUncertainWorker(observedStates.get(entry.id) ?? entry.data.state));
12437
12556
  const blockers = [...base.data.blockers];
12438
12557
  if (needsReconciliation && !blockers.some((entry) => entry.reason === "worker state requires reconciliation"))
12439
12558
  blockers.push({
@@ -12500,7 +12619,8 @@ class WorkitCore {
12500
12619
  ...current,
12501
12620
  status,
12502
12621
  candidates: resumeCandidate && !current.candidates.some((candidate) => candidate.id === resumeCandidate.id) ? [...current.candidates, resumeCandidate] : current.candidates,
12503
- progress: status === "paused" ? { ...current.progress, summary: input.reason } : current.progress
12622
+ progress: current.progress,
12623
+ pauseReason: status === "paused" ? input.reason ?? null : null
12504
12624
  })
12505
12625
  });
12506
12626
  if (!changed.ok)
@@ -12536,7 +12656,7 @@ class WorkitCore {
12536
12656
  return workspace;
12537
12657
  if (!workspace.data)
12538
12658
  return failure("not_found", "workspace not found");
12539
- if (workspace.data.writer || task.data.workers.some((entry) => entry.data.state !== "stopped"))
12659
+ if (workspace.data.writer || task.data.workers.some((entry) => isUncertainWorker(entry.data.state)))
12540
12660
  return failure("recovery_required", "scope revision requires worker ownership reconciliation");
12541
12661
  this.fillRevisions(input, task.data, workspace.data);
12542
12662
  const changed = this.store.mutateTaskAndWorkspace({
@@ -12736,15 +12856,20 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
12736
12856
  return failure("invalid_transition", "worker has not started");
12737
12857
  if (input.state === "running" && entry.data.state === "stopped")
12738
12858
  return failure("invalid_transition", "stopped worker cannot run again");
12859
+ const terminalCancel = entry.data.state === "cancelling" && input.state === "running";
12739
12860
  const nextEntry = {
12740
12861
  ...entry,
12741
12862
  recordedAt: input.now ?? entry.recordedAt,
12742
12863
  provenance: authority.provenance,
12743
- data: { ...entry.data, state: input.state, session: input.session }
12864
+ data: {
12865
+ ...entry.data,
12866
+ state: terminalCancel ? "cancelling" : input.state,
12867
+ session: input.session
12868
+ }
12744
12869
  };
12745
12870
  const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
12746
12871
  const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
12747
- if (entry.data.state === input.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
12872
+ if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
12748
12873
  return success(task.data.revision, workspace.data.revision, entry);
12749
12874
  const changed = input.store.mutateTaskAndWorkspace({
12750
12875
  taskId: task.data.id,
@@ -13072,8 +13197,8 @@ var init_core3 = __esm(() => {
13072
13197
  });
13073
13198
 
13074
13199
  // packages/workit-cursor/hooks/workit-hook.ts
13075
- import { existsSync as existsSync3, realpathSync as realpathSync5 } from "node:fs";
13076
- import path6 from "node:path";
13200
+ import { existsSync as existsSync4, realpathSync as realpathSync5 } from "node:fs";
13201
+ import path7 from "node:path";
13077
13202
  var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapabilities = (availability = {}) => {
13078
13203
  const has = (name) => availability[name] === true;
13079
13204
  return [
@@ -13105,6 +13230,13 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
13105
13230
  reason: has("subagentStart") ? "Cursor subagentStart enforces explicit reviewer/investigator markers; implementer delegation is unavailable" : "Cursor subagentStart is absent",
13106
13231
  refs: [hostRef("subagentStart")]
13107
13232
  },
13233
+ {
13234
+ name: "fresh-context-review",
13235
+ surface: "subagentStart",
13236
+ assurance: has("subagentStart") ? "agent_guided" : "unavailable",
13237
+ reason: has("subagentStart") ? "independent review runs as a bounded reviewer subagent; stops lack stable identity, so reviewer exclusivity is evaluated from recorded evidence" : "Cursor subagentStart is unavailable for independent review",
13238
+ refs: [hostRef("subagentStart")]
13239
+ },
13108
13240
  {
13109
13241
  name: "arbitrary_shell_write",
13110
13242
  surface: "unobservable_shell",
@@ -13127,7 +13259,7 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
13127
13259
  if (!Array.isArray(roots) || roots.length !== 1 || !roots.every(nonEmpty2))
13128
13260
  return { ok: false, error: "exactly one workspace root is required" };
13129
13261
  const root = roots[0];
13130
- if (!path6.isAbsolute(root) || !existsSync3(root))
13262
+ if (!path7.isAbsolute(root) || !existsSync4(root))
13131
13263
  return { ok: false, error: "workspace root must be an existing absolute path" };
13132
13264
  const event = value.hook_event_name;
13133
13265
  const conversationId = nonEmpty2(value.conversation_id) ? value.conversation_id : undefined;