@brainervirus/workit-cursor 1.0.9 → 1.0.11

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,10 +7794,12 @@ var init_task_contract = __esm(() => {
7794
7794
  scope: scopeSchema,
7795
7795
  presented: text,
7796
7796
  approvedContent: text,
7797
- contentRefs: array(refSchema)
7797
+ displayed: text.optional(),
7798
+ contentRefs: array(refSchema),
7799
+ statedChoice: object({ ref: text, text }).strict().optional()
7798
7800
  }).strict(),
7799
7801
  digest,
7800
- response: _enum(["approved", "rejected"]),
7802
+ response: _enum(["approved", "rejected", "stated"]),
7801
7803
  requirementIds: array(digest),
7802
7804
  revoked: object({ at: utc, reason: text }).strict().nullable(),
7803
7805
  consumption: object({
@@ -7872,6 +7874,10 @@ var init_task_contract = __esm(() => {
7872
7874
  path: ["decisionId"]
7873
7875
  });
7874
7876
  });
7877
+ runtimeSchema = object({
7878
+ createdWith: text.nullable(),
7879
+ updatedWith: text
7880
+ }).strict();
7875
7881
  taskRecordSchema = object({
7876
7882
  schemaVersion: literal(1),
7877
7883
  id,
@@ -7885,6 +7891,8 @@ var init_task_contract = __esm(() => {
7885
7891
  status: _enum(["active", "paused", "closed"]),
7886
7892
  closure: closureSchema.nullable(),
7887
7893
  progress: progressSchema,
7894
+ pauseReason: text.nullable().optional(),
7895
+ runtime: runtimeSchema.optional(),
7888
7896
  assessments: array(entrySchema(assessmentSchema)),
7889
7897
  policy: policySchema.nullable(),
7890
7898
  policyChanges: array(policyChangeSchema),
@@ -7900,6 +7908,7 @@ var init_task_contract = __esm(() => {
7900
7908
  id,
7901
7909
  revision,
7902
7910
  root: nonEmpty,
7911
+ runtime: runtimeSchema.optional(),
7903
7912
  writer: object({ state: _enum(["held", "uncertain"]), owner: ownerSchema, acquiredAt: utc }).strict().nullable()
7904
7913
  }).strict();
7905
7914
  capabilitySchema = object({
@@ -7927,6 +7936,9 @@ var init_task_contract = __esm(() => {
7927
7936
  id,
7928
7937
  revision,
7929
7938
  workspaceRevision: revision,
7939
+ createdAt: utc,
7940
+ updatedAt: utc,
7941
+ runtime: runtimeSchema.nullable(),
7930
7942
  objective: text,
7931
7943
  status: _enum(["active", "paused", "closed"]),
7932
7944
  closure: closureSchema.nullable(),
@@ -7996,7 +8008,7 @@ var init_task_contract = __esm(() => {
7996
8008
  expectedWorkspaceRevision: revision.optional(),
7997
8009
  outcome: outcomeSchema,
7998
8010
  summary: text,
7999
- decisionIds: array(id)
8011
+ decisionIds: array(id).optional()
8000
8012
  })
8001
8013
  };
8002
8014
  policyOperations = {
@@ -8088,14 +8100,15 @@ var init_task_contract = __esm(() => {
8088
8100
  ...taskId,
8089
8101
  expectedRevision: revision.optional(),
8090
8102
  expectedWorkspaceRevision: revision.optional(),
8091
- workerId: nullableId.optional()
8103
+ workerId: nullableId.optional(),
8104
+ reason: text.optional()
8092
8105
  }),
8093
8106
  release: operation({
8094
8107
  action: literal("release"),
8095
8108
  ...taskId,
8096
8109
  expectedRevision: revision.optional(),
8097
8110
  expectedWorkspaceRevision: revision.optional(),
8098
- reason: text
8111
+ reason: text.optional()
8099
8112
  })
8100
8113
  };
8101
8114
  stateOperations = {
@@ -8130,6 +8143,21 @@ var init_task_contract = __esm(() => {
8130
8143
  compiledOperationSchemas = Object.fromEntries(OPERATION_FAMILIES.map((family) => [family, compile(operationSchemas[family])]));
8131
8144
  });
8132
8145
 
8146
+ // packages/workit-core/src/core/package-root.ts
8147
+ import { existsSync } from "node:fs";
8148
+ import path from "node:path";
8149
+ import { fileURLToPath } from "node:url";
8150
+ var packageRoot = () => {
8151
+ let dir = path.dirname(fileURLToPath(import.meta.url));
8152
+ while (true) {
8153
+ const parent = path.dirname(dir);
8154
+ if (existsSync(path.join(dir, "package.json")) || parent === dir)
8155
+ return dir;
8156
+ dir = parent;
8157
+ }
8158
+ };
8159
+ var init_package_root = () => {};
8160
+
8133
8161
  // node_modules/@openclaw/fs-safe/dist/errors.js
8134
8162
  function categorizeFsSafeError(code) {
8135
8163
  return OPERATIONAL_CODES.has(code) ? "operational" : "policy";
@@ -8240,7 +8268,7 @@ var init_read_open_flags = () => {};
8240
8268
  // node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
8241
8269
  import { randomBytes } from "node:crypto";
8242
8270
  import fsSync from "node:fs";
8243
- import path from "node:path";
8271
+ import path2 from "node:path";
8244
8272
  function parseSidecarLockSnapshot(snapshot, parser) {
8245
8273
  return snapshot && { ...snapshot, payload: parseSidecarLockPayload(snapshot.raw, parser) };
8246
8274
  }
@@ -8268,13 +8296,13 @@ ${ownershipToken}
8268
8296
  return { raw, ownershipToken };
8269
8297
  }
8270
8298
  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)) {
8299
+ const resolved = path2.resolve(lockPath);
8300
+ const lexicalRelative = path2.relative(lockRoot.rootDir, resolved);
8301
+ const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(lexicalRelative) ? lexicalRelative : path2.relative(lockRoot.rootReal, resolved);
8302
+ if (!relative || relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative)) {
8275
8303
  throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
8276
8304
  }
8277
- return relative.split(path.sep).join(path.posix.sep);
8305
+ return relative.split(path2.sep).join(path2.posix.sep);
8278
8306
  }
8279
8307
  function parseSidecarLockPayload(raw, parser) {
8280
8308
  if (parser) {
@@ -8444,7 +8472,7 @@ function sleepSync(ms) {
8444
8472
 
8445
8473
  // node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
8446
8474
  import fs3 from "node:fs";
8447
- import path2 from "node:path";
8475
+ import path3 from "node:path";
8448
8476
  function getSyncHeldLocks() {
8449
8477
  const globalWithState = globalThis;
8450
8478
  if (!globalWithState[SYNC_HELD_LOCKS_KEY]) {
@@ -8522,26 +8550,26 @@ function canonicalLockParentSync(parent) {
8522
8550
  return process.platform === "win32" ? fs3.realpathSync.native(parent) : fs3.realpathSync(parent);
8523
8551
  }
8524
8552
  function normalizeTargetPath(targetPath) {
8525
- const resolved = path2.resolve(targetPath);
8526
- fs3.mkdirSync(path2.dirname(resolved), { recursive: true });
8553
+ const resolved = path3.resolve(targetPath);
8554
+ fs3.mkdirSync(path3.dirname(resolved), { recursive: true });
8527
8555
  try {
8528
- return path2.join(canonicalLockParentSync(path2.dirname(resolved)), path2.basename(resolved));
8556
+ return path3.join(canonicalLockParentSync(path3.dirname(resolved)), path3.basename(resolved));
8529
8557
  } catch {
8530
8558
  return resolved;
8531
8559
  }
8532
8560
  }
8533
8561
  function boundedLockPath(lockPath, lockRoot) {
8534
- const resolved = path2.resolve(lockPath);
8562
+ const resolved = path3.resolve(lockPath);
8535
8563
  if (!lockRoot)
8536
8564
  return resolved;
8537
8565
  relativeSidecarLockPath(lockRoot, resolved);
8538
- const parent = path2.dirname(resolved);
8566
+ const parent = path3.dirname(resolved);
8539
8567
  const parentReal = canonicalLockParentSync(parent);
8540
- const parentRelative = path2.relative(lockRoot.rootReal, parentReal);
8541
- if (parentRelative === ".." || parentRelative.startsWith(`..${path2.sep}`) || path2.isAbsolute(parentRelative)) {
8568
+ const parentRelative = path3.relative(lockRoot.rootReal, parentReal);
8569
+ if (parentRelative === ".." || parentRelative.startsWith(`..${path3.sep}`) || path3.isAbsolute(parentRelative)) {
8542
8570
  throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
8543
8571
  }
8544
- return path2.join(parentReal, path2.basename(resolved));
8572
+ return path3.join(parentReal, path3.basename(resolved));
8545
8573
  }
8546
8574
  function defaultShouldReclaim(snapshot, staleMs, nowMs) {
8547
8575
  const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
@@ -8760,17 +8788,17 @@ var init_file_lock = __esm(() => {
8760
8788
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
8761
8789
  import * as fs4 from "node:fs";
8762
8790
  import { hostname } from "node:os";
8763
- import path3 from "node:path";
8791
+ import path4 from "node:path";
8764
8792
 
8765
8793
  class TaskStore {
8766
8794
  root;
8767
8795
  constructor(root) {
8768
- this.root = fs4.existsSync(root) ? fs4.realpathSync(root) : path3.resolve(root);
8796
+ this.root = fs4.existsSync(root) ? fs4.realpathSync(root) : path4.resolve(root);
8769
8797
  }
8770
8798
  readTask(taskId) {
8771
8799
  if (!validId(taskId))
8772
8800
  return failure("invalid_input", "task ID is invalid", { taskId });
8773
- const result = this.readRecord(path3.join(this.tasksDir, `${taskId}.json`), taskRecordSchema);
8801
+ const result = this.readRecord(path4.join(this.tasksDir, `${taskId}.json`), taskRecordSchema);
8774
8802
  if (!result.exists)
8775
8803
  return failure("not_found", "task not found", { taskId });
8776
8804
  if (result.result.ok && result.result.data.id !== taskId)
@@ -8804,7 +8832,7 @@ class TaskStore {
8804
8832
  });
8805
8833
  const tasks = [];
8806
8834
  for (const name of names.sort()) {
8807
- const item = this.readRecord(path3.join(this.tasksDir, name), taskRecordSchema);
8835
+ const item = this.readRecord(path4.join(this.tasksDir, name), taskRecordSchema);
8808
8836
  if (!item.exists)
8809
8837
  continue;
8810
8838
  if (!item.result.ok)
@@ -8838,7 +8866,7 @@ class TaskStore {
8838
8866
  if (!current.ok)
8839
8867
  return current;
8840
8868
  if (current.data ? value.expectedWorkspaceRevision !== current.data.revision : value.expectedWorkspaceRevision !== null) {
8841
- return failure("revision_conflict", "workspace revision does not match", {
8869
+ return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
8842
8870
  expectedWorkspaceRevision: value.expectedWorkspaceRevision,
8843
8871
  actualWorkspaceRevision: current.data?.revision ?? null
8844
8872
  });
@@ -8850,11 +8878,17 @@ class TaskStore {
8850
8878
  const previousWorkspaceBytes = current.data ? this.snapshotBytes(this.workspacePath) : null;
8851
8879
  if (current.data && !previousWorkspaceBytes)
8852
8880
  return failure("storage_error", "workspace snapshot disappeared during creation");
8853
- const workspace = current.data ? { ...current.data, revision: newRevision(), root: this.root } : {
8881
+ const workspace = current.data ? {
8882
+ ...current.data,
8883
+ revision: newRevision(),
8884
+ root: this.root,
8885
+ runtime: stampNew(current.data)
8886
+ } : {
8854
8887
  schemaVersion: SCHEMA_VERSION,
8855
8888
  id: newId(),
8856
8889
  revision: newRevision(),
8857
8890
  root: this.root,
8891
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
8858
8892
  writer: null
8859
8893
  };
8860
8894
  const timestamp = value.now ?? now();
@@ -8876,6 +8910,7 @@ class TaskStore {
8876
8910
  status: "active",
8877
8911
  closure: null,
8878
8912
  progress: { summary: "", nextAction: null, blockers: [] },
8913
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
8879
8914
  assessments: [],
8880
8915
  policy: null,
8881
8916
  policyChanges: [],
@@ -8906,7 +8941,7 @@ class TaskStore {
8906
8941
  if (!current.ok)
8907
8942
  return current;
8908
8943
  if (current.data ? input.expectedWorkspaceRevision !== current.data.revision : input.expectedWorkspaceRevision !== null)
8909
- return failure("revision_conflict", "workspace revision does not match", {
8944
+ return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
8910
8945
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
8911
8946
  actualWorkspaceRevision: current.data?.revision ?? null
8912
8947
  });
@@ -8914,11 +8949,17 @@ class TaskStore {
8914
8949
  if (current.data && !previousWorkspaceBytes)
8915
8950
  return failure("storage_error", "workspace snapshot disappeared during import");
8916
8951
  const timestamp = input.now ?? now();
8917
- const workspace = current.data ? { ...current.data, revision: newRevision(), root: this.root } : {
8952
+ const workspace = current.data ? {
8953
+ ...current.data,
8954
+ revision: newRevision(),
8955
+ root: this.root,
8956
+ runtime: stampNew(current.data)
8957
+ } : {
8918
8958
  schemaVersion: SCHEMA_VERSION,
8919
8959
  id: input.workspaceId ?? newId(),
8920
8960
  revision: newRevision(),
8921
8961
  root: this.root,
8962
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
8922
8963
  writer: null
8923
8964
  };
8924
8965
  const task = {
@@ -8926,7 +8967,11 @@ class TaskStore {
8926
8967
  workspaceId: workspace.id,
8927
8968
  revision: newRevision(),
8928
8969
  createdAt: timestamp,
8929
- updatedAt: timestamp
8970
+ updatedAt: timestamp,
8971
+ runtime: {
8972
+ createdWith: input.task.runtime?.createdWith ?? null,
8973
+ updatedWith: runtimeVersion()
8974
+ }
8930
8975
  };
8931
8976
  const validTask = taskRecordSchema.safeParse(task);
8932
8977
  if (!validTask.success)
@@ -8975,7 +9020,8 @@ class TaskStore {
8975
9020
  workspaceId: current.data.workspaceId,
8976
9021
  createdAt: current.data.createdAt,
8977
9022
  revision: context.revision,
8978
- updatedAt: context.now
9023
+ updatedAt: context.now,
9024
+ runtime: stampNew(current.data)
8979
9025
  };
8980
9026
  const valid = taskRecordSchema.safeParse(record);
8981
9027
  if (!valid.success)
@@ -9009,7 +9055,8 @@ class TaskStore {
9009
9055
  ...changed.data,
9010
9056
  id: current.data.id,
9011
9057
  root: this.root,
9012
- revision: context.revision
9058
+ revision: context.revision,
9059
+ runtime: stampNew(current.data)
9013
9060
  };
9014
9061
  const valid = workspaceRecordSchema.safeParse(record);
9015
9062
  if (!valid.success)
@@ -9052,7 +9099,8 @@ class TaskStore {
9052
9099
  ...changedWorkspace.data,
9053
9100
  id: workspace.data.id,
9054
9101
  root: this.root,
9055
- revision: workspaceContext.revision
9102
+ revision: workspaceContext.revision,
9103
+ runtime: stampNew(workspace.data)
9056
9104
  });
9057
9105
  if (!nextWorkspace.success)
9058
9106
  return failure("invalid_input", "workspace mutation produced an invalid record");
@@ -9077,7 +9125,8 @@ class TaskStore {
9077
9125
  workspaceId: task.data.workspaceId,
9078
9126
  createdAt: task.data.createdAt,
9079
9127
  revision: taskContext.revision,
9080
- updatedAt: taskContext.now
9128
+ updatedAt: taskContext.now,
9129
+ runtime: stampNew(task.data)
9081
9130
  });
9082
9131
  if (!nextTask.success) {
9083
9132
  this.markUncertain(nextWorkspace.data);
@@ -9104,7 +9153,7 @@ class TaskStore {
9104
9153
  if (match)
9105
9154
  candidates.push({
9106
9155
  target: match[1],
9107
- path: path3.join(this.recoveryDir, name),
9156
+ path: path4.join(this.recoveryDir, name),
9108
9157
  digest: match[3]
9109
9158
  });
9110
9159
  }
@@ -9297,7 +9346,8 @@ class TaskStore {
9297
9346
  const value = {
9298
9347
  ...workspace,
9299
9348
  writer: { ...workspace.writer, state: "uncertain" },
9300
- revision: newRevision()
9349
+ revision: newRevision(),
9350
+ runtime: stampNew(workspace)
9301
9351
  };
9302
9352
  this.replaceSnapshot(this.workspacePath, value, workspace);
9303
9353
  }
@@ -9375,7 +9425,7 @@ class TaskStore {
9375
9425
  }
9376
9426
  fs4.renameSync(temporary, file);
9377
9427
  temporary = undefined;
9378
- this.fsyncDirectory(path3.dirname(file));
9428
+ this.fsyncDirectory(path4.dirname(file));
9379
9429
  return success(null, null, value);
9380
9430
  } catch (error) {
9381
9431
  return failure("storage_error", `snapshot replacement failed: ${String(error)}`, {
@@ -9389,9 +9439,9 @@ class TaskStore {
9389
9439
  }
9390
9440
  }
9391
9441
  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`);
9442
+ const target = path4.basename(file) === "workspace.json" ? "workspace" : "task";
9443
+ const id = target === "task" ? path4.basename(file, ".json") : "workspace";
9444
+ const destination = path4.join(this.recoveryDir, `${target}.${id}.${digestBytes(bytes)}.json`);
9395
9445
  if (fs4.existsSync(destination)) {
9396
9446
  if (digestBytes(fs4.readFileSync(destination)) === digestBytes(bytes))
9397
9447
  return;
@@ -9469,7 +9519,7 @@ class TaskStore {
9469
9519
  for (const name of fs4.readdirSync(this.tasksDir)) {
9470
9520
  if (!name.endsWith(".json") || !validId(name.slice(0, -5)))
9471
9521
  return null;
9472
- const parsed = this.parseBytes(fs4.readFileSync(path3.join(this.tasksDir, name)), taskRecordSchema);
9522
+ const parsed = this.parseBytes(fs4.readFileSync(path4.join(this.tasksDir, name)), taskRecordSchema);
9473
9523
  if (!parsed.ok || parsed.data.id !== name.slice(0, -5))
9474
9524
  return null;
9475
9525
  ids.add(parsed.data.workspaceId);
@@ -9511,7 +9561,12 @@ class TaskStore {
9511
9561
  if (isObject2(value) && "schemaVersion" in value && value.schemaVersion !== SCHEMA_VERSION)
9512
9562
  return failure("unsupported_version", "unsupported snapshot schema version");
9513
9563
  const parsed = schema.safeParse(value);
9514
- return parsed.success ? success(null, null, parsed.data) : failure("recovery_required", "snapshot does not satisfy its schema");
9564
+ if (parsed.success)
9565
+ return success(null, null, parsed.data);
9566
+ const writerVersion = isObject2(value) && isObject2(value.runtime) ? value.runtime.updatedWith : null;
9567
+ if (typeof writerVersion === "string" && isNewerVersion(writerVersion, runtimeVersion()))
9568
+ return failure("recovery_required", `snapshot was written by workit ${writerVersion}; upgrade Workit before mutating this checkout`);
9569
+ return failure("recovery_required", "snapshot does not satisfy its schema");
9515
9570
  }
9516
9571
  processStart(pid) {
9517
9572
  try {
@@ -9521,34 +9576,57 @@ class TaskStore {
9521
9576
  }
9522
9577
  }
9523
9578
  conflict(expected, actual) {
9524
- return failure("revision_conflict", "snapshot revision does not match", {
9579
+ return failure("revision_conflict", "snapshot revision does not match; omit expectedRevision to use the current record", {
9525
9580
  expectedRevision: expected,
9526
9581
  actualRevision: actual
9527
9582
  });
9528
9583
  }
9529
9584
  taskPath(taskId) {
9530
- return path3.join(this.tasksDir, `${taskId}.json`);
9585
+ return path4.join(this.tasksDir, `${taskId}.json`);
9531
9586
  }
9532
9587
  get workitDir() {
9533
- return path3.join(this.root, ".workit");
9588
+ return path4.join(this.root, ".workit");
9534
9589
  }
9535
9590
  get tasksDir() {
9536
- return path3.join(this.workitDir, "tasks");
9591
+ return path4.join(this.workitDir, "tasks");
9537
9592
  }
9538
9593
  get recoveryDir() {
9539
- return path3.join(this.workitDir, "recovery");
9594
+ return path4.join(this.workitDir, "recovery");
9540
9595
  }
9541
9596
  get workspacePath() {
9542
- return path3.join(this.workitDir, "workspace.json");
9597
+ return path4.join(this.workitDir, "workspace.json");
9543
9598
  }
9544
9599
  get lockPath() {
9545
- return path3.join(this.workitDir, "metadata.lock");
9600
+ return path4.join(this.workitDir, "metadata.lock");
9546
9601
  }
9547
9602
  get gitignorePath() {
9548
- return path3.join(this.workitDir, ".gitignore");
9603
+ return path4.join(this.workitDir, ".gitignore");
9549
9604
  }
9550
9605
  }
9551
- var processEvidenceSchema, metadataLockSchema, now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"), jsonBytes = (value) => `${canonicalJson(value)}
9606
+ var processEvidenceSchema, metadataLockSchema, now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"), cachedRuntimeVersion = null, runtimeVersion = () => {
9607
+ if (cachedRuntimeVersion === null) {
9608
+ try {
9609
+ const pkg = JSON.parse(fs4.readFileSync(path4.join(packageRoot(), "package.json"), "utf8"));
9610
+ cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
9611
+ } catch {
9612
+ cachedRuntimeVersion = "0.0.0";
9613
+ }
9614
+ }
9615
+ return cachedRuntimeVersion;
9616
+ }, stampNew = (record) => ({
9617
+ createdWith: record.runtime?.createdWith ?? null,
9618
+ updatedWith: runtimeVersion()
9619
+ }), isNewerVersion = (candidate, current) => {
9620
+ const parts = (value) => value.split("-")[0].split(".").map((item) => Number.parseInt(item, 10) || 0);
9621
+ const [next, now] = [parts(candidate), parts(current)];
9622
+ for (let index = 0;index < 3; index += 1) {
9623
+ const left = next[index] ?? 0;
9624
+ const right = now[index] ?? 0;
9625
+ if (left !== right)
9626
+ return left > right;
9627
+ }
9628
+ return false;
9629
+ }, jsonBytes = (value) => `${canonicalJson(value)}
9552
9630
  `, 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
9631
  let value;
9554
9632
  try {
@@ -9566,6 +9644,7 @@ var processEvidenceSchema, metadataLockSchema, now = () => new Date().toISOStrin
9566
9644
  };
9567
9645
  var init_task_store = __esm(() => {
9568
9646
  init_zod();
9647
+ init_package_root();
9569
9648
  init_file_lock();
9570
9649
  init_task_contract();
9571
9650
  processEvidenceSchema = object({
@@ -9582,10 +9661,63 @@ var init_task_store = __esm(() => {
9582
9661
  }).strict();
9583
9662
  });
9584
9663
 
9664
+ // packages/workit-core/src/core/workers.ts
9665
+ function assertProductWriteAllowed(input) {
9666
+ if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
9667
+ return failure("invalid_input", "invalid product write path");
9668
+ if (!input.store)
9669
+ return failure("permission_denied", "write authorization requires core state");
9670
+ const task = input.store.readTask(input.task.id);
9671
+ if (!task.ok)
9672
+ return task;
9673
+ const workspace = input.store.readWorkspace();
9674
+ if (!workspace.ok)
9675
+ return workspace;
9676
+ if (!workspace.data)
9677
+ return failure("not_found", "workspace not found");
9678
+ const workspaceRecord = workspace.data;
9679
+ if (task.data.status !== "active")
9680
+ return failure("invalid_transition", "paused or closed tasks cannot own product writes");
9681
+ if (workspaceRecord.id !== task.data.workspaceId || workspaceRecord.root.length === 0)
9682
+ return failure("recovery_required", "task and workspace bindings are invalid");
9683
+ const writer = workspaceRecord.writer;
9684
+ if (!writer)
9685
+ return failure("permission_denied", "checkout has no writer owner");
9686
+ if (writer.state === "uncertain")
9687
+ return failure("recovery_required", "checkout writer requires recovery", {
9688
+ owner: writer.owner
9689
+ });
9690
+ const caller = callerValue(input.caller);
9691
+ const workerId = workerIdOf(input.caller);
9692
+ const owner = writer.owner;
9693
+ if (owner.taskId !== task.data.id || owner.workerId !== workerId || owner.session.host !== caller.host || owner.session.handle !== caller.actor)
9694
+ return failure("writer_conflict", "checkout is owned by another validated actor", { owner });
9695
+ const worker = workerId === null ? null : task.data.workers.find((entry) => entry.id === workerId);
9696
+ if (workerId !== null && (!worker || worker.data.assignment.role !== "implementer"))
9697
+ return failure("permission_denied", "only implementers can own product writes");
9698
+ 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))))
9699
+ return failure("recovery_required", "worker session is not an active writer");
9700
+ return success(null, workspaceRecord.revision, owner);
9701
+ }
9702
+ var same = (left, right) => {
9703
+ try {
9704
+ return canonicalJson(left) === canonicalJson(right);
9705
+ } catch {
9706
+ return false;
9707
+ }
9708
+ }, 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) => {
9709
+ const code = char.charCodeAt(0);
9710
+ return code < 32 || code === 127;
9711
+ }) && !value.split(/[\\/]/).includes(".."), callerValue = (value) => ({ host: value.host, actor: value.actor }), workerIdOf = (value) => value.workerId ?? null;
9712
+ var init_workers = __esm(() => {
9713
+ init_task_contract();
9714
+ UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
9715
+ });
9716
+
9585
9717
  // packages/workit-core/src/core/authority.ts
9586
9718
  import { createHash as createHash3 } from "node:crypto";
9587
9719
  import * as fs5 from "node:fs";
9588
- import path4 from "node:path";
9720
+ import path5 from "node:path";
9589
9721
  function applicableDecision(task, purpose, binding, checkoutRoot) {
9590
9722
  if (binding.taskId !== task.id || binding.workspaceId !== task.workspaceId)
9591
9723
  return [];
@@ -9969,20 +10101,31 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
9969
10101
  continue;
9970
10102
  if (!reference.digest)
9971
10103
  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");
10104
+ const target = path5.resolve(root, reference.path);
10105
+ if (target !== root && !target.startsWith(`${root}${path5.sep}`))
10106
+ return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
10107
+ fields: [
10108
+ { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
10109
+ ]
10110
+ });
9975
10111
  try {
9976
- const relative = path4.relative(root, target);
10112
+ const relative = path5.relative(root, target);
9977
10113
  let current = root;
9978
- for (const segment of relative.split(path4.sep)) {
10114
+ for (const segment of relative.split(path5.sep)) {
9979
10115
  if (!segment)
9980
10116
  continue;
9981
- current = path4.join(current, segment);
10117
+ current = path5.join(current, segment);
9982
10118
  const stat = fs5.lstatSync(current);
9983
10119
  const real = fs5.realpathSync(current);
9984
- if (real !== root && !real.startsWith(`${root}${path4.sep}`))
9985
- return failure("invalid_input", "document reference escapes checkout");
10120
+ if (real !== root && !real.startsWith(`${root}${path5.sep}`))
10121
+ return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
10122
+ fields: [
10123
+ {
10124
+ path: "contentRefs.path",
10125
+ reason: `${reference.path} resolves outside the checkout`
10126
+ }
10127
+ ]
10128
+ });
9986
10129
  if (stat.isSymbolicLink())
9987
10130
  return failure("invalid_input", "document reference uses a symlink");
9988
10131
  if (current === target && !stat.isFile())
@@ -10059,7 +10202,7 @@ var init_authority = __esm(() => {
10059
10202
  import { createHash as createHash4 } from "node:crypto";
10060
10203
  import * as fs6 from "node:fs";
10061
10204
  import { spawnSync } from "node:child_process";
10062
- import path5 from "node:path";
10205
+ import path6 from "node:path";
10063
10206
  function captureCandidate(root, scope, environment = []) {
10064
10207
  let checkout;
10065
10208
  try {
@@ -10069,7 +10212,7 @@ function captureCandidate(root, scope, environment = []) {
10069
10212
  }
10070
10213
  const normalizedScope = canonicalScope(scope);
10071
10214
  if (!normalizedScope)
10072
- return failure("invalid_input", "candidate scope is invalid");
10215
+ 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
10216
  const roots = scopeRoots(checkout, normalizedScope);
10074
10217
  if (!roots.ok)
10075
10218
  return roots;
@@ -10101,7 +10244,7 @@ function captureCandidate(root, scope, environment = []) {
10101
10244
  for (const item of [...names].sort()) {
10102
10245
  if (!scopeMatches(item, normalizedScope))
10103
10246
  continue;
10104
- const target = path5.join(checkout, item);
10247
+ const target = path6.join(checkout, item);
10105
10248
  try {
10106
10249
  const stat = fs6.lstatSync(target);
10107
10250
  if (stat.isSymbolicLink()) {
@@ -10220,7 +10363,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
10220
10363
  return true;
10221
10364
  const reviewSession = sessionFromRef(entry.data.reviewContext);
10222
10365
  const sameImplementation = sameSession(reviewSession, task.intent.provenance.session);
10223
- const sameEvidenceSession = task.evidence.some((other) => other.id !== entry.id && sameSession(reviewSession, other.provenance.session));
10366
+ const sameEvidenceSession = task.evidence.some((other) => other.id !== entry.id && other.data.kind === "review" && sameSession(reviewSession, other.provenance.session));
10224
10367
  return entry.data.kind === "review" && reviewSession !== null && sameSession(reviewSession, entry.provenance.session) && (requirement.ruleId === "self-review" || !sameImplementation && !sameEvidenceSession);
10225
10368
  });
10226
10369
  if (passed.length) {
@@ -10261,7 +10404,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
10261
10404
  status: "satisfied",
10262
10405
  evidenceIds: [],
10263
10406
  decisionIds: decisions.map((decision) => decision.id),
10264
- reason: "an applicable approved decision satisfies the requirement"
10407
+ reason: "an applicable approved or stated decision satisfies the requirement"
10265
10408
  };
10266
10409
  const limitations = requirement.acceptanceAllowed ? applicableDecision2(task, workspace, requirement, checkoutRoot) : [];
10267
10410
  if (limitations.length)
@@ -10316,7 +10459,17 @@ function evaluateClosure(requestedOutcome, view) {
10316
10459
  const blocking = evaluations.filter((item) => item.status === "unsatisfied" || item.status === "unavailable");
10317
10460
  if (requestedOutcome !== "stopped" && blocking.length)
10318
10461
  return failure("requirements_unsatisfied", "applicable requirements are unsatisfied", {
10319
- requirementIds: blocking.map((item) => item.requirementId)
10462
+ requirementIds: blocking.map((item) => item.requirementId),
10463
+ requirements: blocking.map((item) => {
10464
+ const requirement = view.task.policy?.requirements.find((candidate) => candidate.id === item.requirementId);
10465
+ return {
10466
+ requirementId: item.requirementId,
10467
+ ruleId: requirement?.ruleId ?? "unknown",
10468
+ reason: item.reason,
10469
+ satisfaction: requirement?.satisfaction ?? "",
10470
+ dependentAction: requirement?.dependentAction ?? null
10471
+ };
10472
+ })
10320
10473
  });
10321
10474
  const accepted = evaluations.filter((item) => item.status === "accepted_limitation");
10322
10475
  if (requestedOutcome === "verified" && accepted.length)
@@ -10342,12 +10495,12 @@ function evaluateClosure(requestedOutcome, view) {
10342
10495
  requirementIds: evaluations.map((item) => item.requirementId)
10343
10496
  });
10344
10497
  }
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) => {
10498
+ 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
10499
  if (typeof value !== "string" || !value)
10347
10500
  return null;
10348
10501
  if (value.includes("\\") || value.split("/").includes(".."))
10349
10502
  return null;
10350
- const normalized = path5.posix.normalize(value);
10503
+ const normalized = path6.posix.normalize(value);
10351
10504
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
10352
10505
  return null;
10353
10506
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -10389,14 +10542,14 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
10389
10542
  git: /not a git repository/i.test(stderr) ? false : true
10390
10543
  };
10391
10544
  }
10392
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path5.sep).join("/")) : [];
10545
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path6.sep).join("/")) : [];
10393
10546
  const stagedDeleted = spawnSync("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
10394
10547
  if (stagedDeleted.status !== 0) {
10395
10548
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
10396
10549
  if (!/not a git repository/i.test(stderr))
10397
10550
  return { paths, uncertain: true, git: true };
10398
10551
  } else if (stagedDeleted.stdout) {
10399
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path5.sep).join("/")));
10552
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path6.sep).join("/")));
10400
10553
  }
10401
10554
  return { paths: [...new Set(paths)], uncertain: false, git: true };
10402
10555
  }, walk = (root, directory, output) => {
@@ -10410,7 +10563,7 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
10410
10563
  for (const name of names) {
10411
10564
  if (name === ".git" || name === ".workit")
10412
10565
  continue;
10413
- const target = path5.join(directory, name);
10566
+ const target = path6.join(directory, name);
10414
10567
  let stat;
10415
10568
  try {
10416
10569
  stat = fs6.lstatSync(target);
@@ -10427,17 +10580,17 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
10427
10580
  }, scopeRoots = (root, scope) => {
10428
10581
  const roots = [];
10429
10582
  for (const item of scope.paths.length ? scope.paths : ["."]) {
10430
- const target = path5.resolve(root, item);
10583
+ const target = path6.resolve(root, item);
10431
10584
  if (!inside(root, target))
10432
- return failure("invalid_input", "candidate scope escapes checkout");
10585
+ 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
10586
  let ancestor = target;
10434
10587
  while (!fs6.existsSync(ancestor) && ancestor !== root)
10435
- ancestor = path5.dirname(ancestor);
10588
+ ancestor = path6.dirname(ancestor);
10436
10589
  try {
10437
10590
  if (!inside(root, fs6.realpathSync(ancestor)))
10438
- return failure("invalid_input", "candidate scope escapes checkout");
10591
+ 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
10592
  } catch {
10440
- return failure("invalid_input", "candidate scope cannot be inspected");
10593
+ return failure("invalid_input", `candidate scope cannot be inspected: ${item}`);
10441
10594
  }
10442
10595
  roots.push(target);
10443
10596
  }
@@ -10459,7 +10612,7 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
10459
10612
  }, sameSession = (left, right) => {
10460
10613
  const other = sessionFromRef(right);
10461
10614
  return left !== null && other !== null && left.host === other.host && left.handle === other.handle;
10462
- }, evidenceScopes = (task, ids) => (task.policy?.requirements ?? []).filter((requirement) => ids.includes(requirement.id)).map((requirement) => requirement.scope), scopeCovers2, applicableDecision2 = (task, workspace, requirement, checkoutRoot) => task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data).filter((decision) => decision.purpose === "limitation" && decision.response === "approved" && decision.revoked === null && decision.binding.taskId === task.id && decision.binding.workspaceId === workspace.id && decision.requirementIds.includes(requirement.id) && decision.binding.scope && scopeCovers2(decision.binding.scope, requirement.scope) && decision.digest === decisionDigest(decision) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, decision.binding).ok : decision.binding.contentRefs.every((reference) => reference.kind !== "file"))), applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && data.response === "approved" && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id })), evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
10615
+ }, evidenceScopes = (task, ids) => (task.policy?.requirements ?? []).filter((requirement) => ids.includes(requirement.id)).map((requirement) => requirement.scope), scopeCovers2, applicableDecision2 = (task, workspace, requirement, checkoutRoot) => task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data).filter((decision) => decision.purpose === "limitation" && decision.response === "approved" && decision.revoked === null && decision.binding.taskId === task.id && decision.binding.workspaceId === workspace.id && decision.requirementIds.includes(requirement.id) && decision.binding.scope && scopeCovers2(decision.binding.scope, requirement.scope) && decision.digest === decisionDigest(decision) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, decision.binding).ok : decision.binding.contentRefs.every((reference) => reference.kind !== "file"))), applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && (data.response === "approved" || data.response === "stated") && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id })), evidenceMatchesRequirement = (kind, evidenceKind, dimension) => {
10463
10616
  if (kind !== "passed")
10464
10617
  return false;
10465
10618
  if (dimension === "testing" || dimension === "verification")
@@ -10532,7 +10685,18 @@ decision.record {binding with taskId/workspaceId from inspect plus presented and
10532
10685
  approvedContent} only through a native approval question asked receipt-shaped:
10533
10686
  header Workit decision: <purpose>, the same label repeated in the question
10534
10687
  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,
10688
+ rejected; ask once, never re-ask to mint a receipt. Before asking, present the
10689
+ item in the conversation (durable artifacts: complete digest plus exact path;
10690
+ inline plans: the content) and keep the question to one short scoped sentence;
10691
+ never ask to approve something unseen. An answer with custom text is steering,
10692
+ not approval: adjust, re-present, then ask a new scoped question. After the user
10693
+ approves a spec or plan, execute the remaining tasks continuously with one
10694
+ atomic commit per task and do not ask whether to continue; stop only for a new
10695
+ product decision, a failed safety or verification gate, a conflicting edit, or
10696
+ missing authority. Record the plan's commit list once through the plan-scoped
10697
+ action approval (git.commit with plan_steps and plan_branch) so each listed
10698
+ commit executes without a new question; an unlisted message needs a fresh
10699
+ approval. task.close {outcome,
10536
10700
  summary, decisionIds}. A completed native subagent run stops its bound worker
10537
10701
  by itself; if a worker strands in cancelling with its run verifiably over,
10538
10702
  repeat worker.cancel {reason} to confirm the stop. Check and review evidence
@@ -10558,7 +10722,7 @@ var init_methods = __esm(() => {
10558
10722
  METHODS = {
10559
10723
  "workit-challenge": { dimensions: ["challenge", "decisions"] },
10560
10724
  "workit-behavioral-tdd": { dimensions: ["testing"] },
10561
- "workit-review": { dimensions: ["review"] },
10725
+ "workit-review": { dimensions: ["review"], ruleIds: ["fresh-context-review"] },
10562
10726
  "workit-plan": { dimensions: ["artifacts", "continuity"] },
10563
10727
  "workit-implement": { dimensions: ["delegation"] },
10564
10728
  "workit-debug": { ruleIds: ["root-cause-investigation"] },
@@ -10576,7 +10740,7 @@ function reconcileResume(view, observations = []) {
10576
10740
  return candidate;
10577
10741
  const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
10578
10742
  const blockers = [...view.task.progress.blockers];
10579
- if (view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
10743
+ if (view.task.workers.some((entry) => isUncertainWorker(entry.data.state)))
10580
10744
  blockers.push({
10581
10745
  reason: "worker state requires reconciliation",
10582
10746
  dependentAction: "resume",
@@ -10659,6 +10823,7 @@ var COMPACT_MAX_BYTES = 4096, COMPACT_MAX_ITEMS = 8, COMPACT_TEXT_BYTES = 128, s
10659
10823
  task: bundle.task
10660
10824
  });
10661
10825
  var init_task_context = __esm(() => {
10826
+ init_workers();
10662
10827
  init_task_contract();
10663
10828
  init_task_evaluation();
10664
10829
  init_methods();
@@ -10668,7 +10833,9 @@ var init_task_context = __esm(() => {
10668
10833
  });
10669
10834
 
10670
10835
  // packages/workit-core/src/core/route-intent.ts
10671
- var init_route_intent = () => {};
10836
+ var init_route_intent = __esm(() => {
10837
+ init_task_store();
10838
+ });
10672
10839
 
10673
10840
  // packages/workit-core/src/core/external-action.ts
10674
10841
  var externalActionSchema;
@@ -10687,7 +10854,11 @@ var init_external_action = __esm(() => {
10687
10854
  }).strict(),
10688
10855
  object({
10689
10856
  operation: literal("git.commit"),
10690
- payload: object({ message: string2().min(1) }).strict()
10857
+ payload: object({
10858
+ message: string2().min(1).optional(),
10859
+ plan_steps: array(string2().min(1)).optional(),
10860
+ plan_branch: string2().min(1).optional()
10861
+ }).strict()
10691
10862
  }).strict(),
10692
10863
  object({
10693
10864
  operation: literal("git.push"),
@@ -10819,7 +10990,7 @@ function resolveRequirements(input) {
10819
10990
  ].join(", ")}`,
10820
10991
  satisfaction: "An observed or inferred fact resolves the uncertainty and records its supporting references.",
10821
10992
  before: "dependent_action",
10822
- dependentAction: "dependent-action",
10993
+ dependentAction: null,
10823
10994
  acceptanceAllowed: false
10824
10995
  }));
10825
10996
  if (signals.productChoiceOpen.value === true)
@@ -10830,7 +11001,7 @@ function resolveRequirements(input) {
10830
11001
  reason: `A consequential product choice remains open: ${signalReason(input, "productChoiceOpen")}`,
10831
11002
  satisfaction: "Present the material alternatives and record the user's decision before the dependent action.",
10832
11003
  before: "dependent_action",
10833
- dependentAction: "dependent-action",
11004
+ dependentAction: null,
10834
11005
  acceptanceAllowed: false
10835
11006
  }));
10836
11007
  if (signals.behaviorChange.value === true)
@@ -11082,58 +11253,6 @@ var init_policy_resolver = __esm(() => {
11082
11253
  }).strict();
11083
11254
  });
11084
11255
 
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
11256
  // packages/workit-core/src/core/task-engine.ts
11138
11257
  import { realpathSync as realpathSync4 } from "node:fs";
11139
11258
 
@@ -11195,7 +11314,7 @@ class WorkitCore {
11195
11314
  activeWorkerBlocker(task, workspace) {
11196
11315
  if (workspace.writer)
11197
11316
  return failure("recovery_required", "writer ownership must be released first");
11198
- if (task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
11317
+ if (task.workers.some((entry) => isUncertainWorker(entry.data.state)))
11199
11318
  return failure("recovery_required", "worker state requires reconciliation");
11200
11319
  return null;
11201
11320
  }
@@ -11432,11 +11551,6 @@ class WorkitCore {
11432
11551
  if (helper && !helper.ok)
11433
11552
  return helper;
11434
11553
  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
11554
  if ((evidence.result === "missing" || evidence.result === "skipped") && !evidence.summary.trim())
11441
11555
  return failure("invalid_input", "missing or skipped evidence requires a reason");
11442
11556
  if (evidence.kind === "review") {
@@ -11453,6 +11567,11 @@ class WorkitCore {
11453
11567
  evidence.beforeCandidateId ??= null;
11454
11568
  evidence.candidateId ??= null;
11455
11569
  }
11570
+ if (helper?.ok) {
11571
+ const assignment = helper.data.data.assignment;
11572
+ 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))
11573
+ return failure("permission_denied", "evidence is outside the worker assignment");
11574
+ }
11456
11575
  const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
11457
11576
  for (const candidateId of [evidence.beforeCandidateId, evidence.candidateId]) {
11458
11577
  if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
@@ -11506,7 +11625,12 @@ class WorkitCore {
11506
11625
  const input = parsed.data;
11507
11626
  if (nativeRequired && input.action !== "record")
11508
11627
  return failure("permission_denied", "native observation is only valid for recording decisions");
11509
- if (nativeRequired && nativeObservation === undefined)
11628
+ const stated = input.action === "record" && input.response === "stated";
11629
+ if (stated && input.purpose === "action")
11630
+ return failure("permission_denied", "stated choices cannot authorize mutating actions");
11631
+ if (stated && !input.binding?.statedChoice)
11632
+ return failure("invalid_input", "stated choices require binding.statedChoice");
11633
+ if (nativeRequired && nativeObservation === undefined && !stated)
11510
11634
  return failure("permission_denied", "native decision observation is required");
11511
11635
  const task = this.store.readTask(input.taskId);
11512
11636
  if (!task.ok)
@@ -11539,7 +11663,7 @@ class WorkitCore {
11539
11663
  consumption: null
11540
11664
  };
11541
11665
  const data = { ...base, digest: decisionDigest(base) };
11542
- const native = nativeRequired ? verifyNativeDecision(this.context.nativeAuthority, {
11666
+ const native = nativeRequired && !stated ? verifyNativeDecision(this.context.nativeAuthority, {
11543
11667
  observation: nativeObservation,
11544
11668
  expected: {
11545
11669
  taskId: task.data.id,
@@ -12348,6 +12472,9 @@ class WorkitCore {
12348
12472
  id: task.id,
12349
12473
  revision: task.revision,
12350
12474
  workspaceRevision: workspace.data.revision,
12475
+ createdAt: task.createdAt,
12476
+ updatedAt: task.updatedAt,
12477
+ runtime: task.runtime ?? null,
12351
12478
  objective: task.intent.data.objective,
12352
12479
  status: task.status,
12353
12480
  closure: task.closure,
@@ -12433,7 +12560,7 @@ class WorkitCore {
12433
12560
  const base = reconcileResume(effectiveView);
12434
12561
  if (!base.ok)
12435
12562
  return base;
12436
- const needsReconciliation = view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
12563
+ const needsReconciliation = view.task.workers.some((entry) => isUncertainWorker(observedStates.get(entry.id) ?? entry.data.state));
12437
12564
  const blockers = [...base.data.blockers];
12438
12565
  if (needsReconciliation && !blockers.some((entry) => entry.reason === "worker state requires reconciliation"))
12439
12566
  blockers.push({
@@ -12500,7 +12627,8 @@ class WorkitCore {
12500
12627
  ...current,
12501
12628
  status,
12502
12629
  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
12630
+ progress: current.progress,
12631
+ pauseReason: status === "paused" ? input.reason ?? null : null
12504
12632
  })
12505
12633
  });
12506
12634
  if (!changed.ok)
@@ -12536,7 +12664,7 @@ class WorkitCore {
12536
12664
  return workspace;
12537
12665
  if (!workspace.data)
12538
12666
  return failure("not_found", "workspace not found");
12539
- if (workspace.data.writer || task.data.workers.some((entry) => entry.data.state !== "stopped"))
12667
+ if (workspace.data.writer || task.data.workers.some((entry) => isUncertainWorker(entry.data.state)))
12540
12668
  return failure("recovery_required", "scope revision requires worker ownership reconciliation");
12541
12669
  this.fillRevisions(input, task.data, workspace.data);
12542
12670
  const changed = this.store.mutateTaskAndWorkspace({
@@ -12736,15 +12864,20 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
12736
12864
  return failure("invalid_transition", "worker has not started");
12737
12865
  if (input.state === "running" && entry.data.state === "stopped")
12738
12866
  return failure("invalid_transition", "stopped worker cannot run again");
12867
+ const terminalCancel = entry.data.state === "cancelling" && input.state === "running";
12739
12868
  const nextEntry = {
12740
12869
  ...entry,
12741
12870
  recordedAt: input.now ?? entry.recordedAt,
12742
12871
  provenance: authority.provenance,
12743
- data: { ...entry.data, state: input.state, session: input.session }
12872
+ data: {
12873
+ ...entry.data,
12874
+ state: terminalCancel ? "cancelling" : input.state,
12875
+ session: input.session
12876
+ }
12744
12877
  };
12745
12878
  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
12879
  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)
12880
+ if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
12748
12881
  return success(task.data.revision, workspace.data.revision, entry);
12749
12882
  const changed = input.store.mutateTaskAndWorkspace({
12750
12883
  taskId: task.data.id,
@@ -13072,8 +13205,8 @@ var init_core3 = __esm(() => {
13072
13205
  });
13073
13206
 
13074
13207
  // packages/workit-cursor/hooks/workit-hook.ts
13075
- import { existsSync as existsSync3, realpathSync as realpathSync5 } from "node:fs";
13076
- import path6 from "node:path";
13208
+ import { existsSync as existsSync4, realpathSync as realpathSync5 } from "node:fs";
13209
+ import path7 from "node:path";
13077
13210
  var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapabilities = (availability = {}) => {
13078
13211
  const has = (name) => availability[name] === true;
13079
13212
  return [
@@ -13105,6 +13238,13 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
13105
13238
  reason: has("subagentStart") ? "Cursor subagentStart enforces explicit reviewer/investigator markers; implementer delegation is unavailable" : "Cursor subagentStart is absent",
13106
13239
  refs: [hostRef("subagentStart")]
13107
13240
  },
13241
+ {
13242
+ name: "fresh-context-review",
13243
+ surface: "subagentStart",
13244
+ assurance: has("subagentStart") ? "agent_guided" : "unavailable",
13245
+ 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",
13246
+ refs: [hostRef("subagentStart")]
13247
+ },
13108
13248
  {
13109
13249
  name: "arbitrary_shell_write",
13110
13250
  surface: "unobservable_shell",
@@ -13127,7 +13267,7 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
13127
13267
  if (!Array.isArray(roots) || roots.length !== 1 || !roots.every(nonEmpty2))
13128
13268
  return { ok: false, error: "exactly one workspace root is required" };
13129
13269
  const root = roots[0];
13130
- if (!path6.isAbsolute(root) || !existsSync3(root))
13270
+ if (!path7.isAbsolute(root) || !existsSync4(root))
13131
13271
  return { ok: false, error: "workspace root must be an existing absolute path" };
13132
13272
  const event = value.hook_event_name;
13133
13273
  const conversationId = nonEmpty2(value.conversation_id) ? value.conversation_id : undefined;