@brainervirus/workit-mcp 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.
Files changed (3) hide show
  1. package/dist/index.js +347 -216
  2. package/package.json +2 -2
  3. package/src/server.ts +51 -8
package/dist/index.js CHANGED
@@ -12477,8 +12477,8 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
12477
12477
 
12478
12478
  // packages/workit-core/src/core.ts
12479
12479
  import { spawnSync as spawnSync2 } from "node:child_process";
12480
- import { existsSync as existsSync3, realpathSync as realpathSync5 } from "node:fs";
12481
- import path6 from "node:path";
12480
+ import { existsSync as existsSync4, realpathSync as realpathSync5 } from "node:fs";
12481
+ import path7 from "node:path";
12482
12482
 
12483
12483
  // packages/workit-core/src/core/task-contract.ts
12484
12484
  import { createHash, randomUUID } from "node:crypto";
@@ -20270,6 +20270,7 @@ var decisionSchema = object4({
20270
20270
  scope: scopeSchema,
20271
20271
  presented: text,
20272
20272
  approvedContent: text,
20273
+ displayed: text.optional(),
20273
20274
  contentRefs: array3(refSchema)
20274
20275
  }).strict(),
20275
20276
  digest,
@@ -20348,6 +20349,10 @@ var actionProgressListSchema = array3(actionProgressSchema).check((ctx) => {
20348
20349
  path: ["decisionId"]
20349
20350
  });
20350
20351
  });
20352
+ var runtimeSchema = object4({
20353
+ createdWith: text.nullable(),
20354
+ updatedWith: text
20355
+ }).strict();
20351
20356
  var taskRecordSchema = object4({
20352
20357
  schemaVersion: literal3(1),
20353
20358
  id,
@@ -20361,6 +20366,8 @@ var taskRecordSchema = object4({
20361
20366
  status: _enum2(["active", "paused", "closed"]),
20362
20367
  closure: closureSchema.nullable(),
20363
20368
  progress: progressSchema,
20369
+ pauseReason: text.nullable().optional(),
20370
+ runtime: runtimeSchema.optional(),
20364
20371
  assessments: array3(entrySchema(assessmentSchema)),
20365
20372
  policy: policySchema.nullable(),
20366
20373
  policyChanges: array3(policyChangeSchema),
@@ -20376,6 +20383,7 @@ var workspaceRecordSchema = object4({
20376
20383
  id,
20377
20384
  revision,
20378
20385
  root: nonEmpty,
20386
+ runtime: runtimeSchema.optional(),
20379
20387
  writer: object4({ state: _enum2(["held", "uncertain"]), owner: ownerSchema, acquiredAt: utc }).strict().nullable()
20380
20388
  }).strict();
20381
20389
  var capabilitySchema = object4({
@@ -20403,6 +20411,9 @@ var taskSummarySchema = object4({
20403
20411
  id,
20404
20412
  revision,
20405
20413
  workspaceRevision: revision,
20414
+ createdAt: utc,
20415
+ updatedAt: utc,
20416
+ runtime: runtimeSchema.nullable(),
20406
20417
  objective: text,
20407
20418
  status: _enum2(["active", "paused", "closed"]),
20408
20419
  closure: closureSchema.nullable(),
@@ -20473,7 +20484,7 @@ var taskOperations = {
20473
20484
  expectedWorkspaceRevision: revision.optional(),
20474
20485
  outcome: outcomeSchema,
20475
20486
  summary: text,
20476
- decisionIds: array3(id)
20487
+ decisionIds: array3(id).optional()
20477
20488
  })
20478
20489
  };
20479
20490
  var policyOperations = {
@@ -20565,14 +20576,15 @@ var writerOperations = {
20565
20576
  ...taskId,
20566
20577
  expectedRevision: revision.optional(),
20567
20578
  expectedWorkspaceRevision: revision.optional(),
20568
- workerId: nullableId.optional()
20579
+ workerId: nullableId.optional(),
20580
+ reason: text.optional()
20569
20581
  }),
20570
20582
  release: operation({
20571
20583
  action: literal3("release"),
20572
20584
  ...taskId,
20573
20585
  expectedRevision: revision.optional(),
20574
20586
  expectedWorkspaceRevision: revision.optional(),
20575
- reason: text
20587
+ reason: text.optional()
20576
20588
  })
20577
20589
  };
20578
20590
  var stateOperations = {
@@ -20779,7 +20791,26 @@ function requirementId(input) {
20779
20791
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
20780
20792
  import * as fs4 from "node:fs";
20781
20793
  import { hostname as hostname2 } from "node:os";
20782
- import path3 from "node:path";
20794
+ import path4 from "node:path";
20795
+
20796
+ // packages/workit-core/src/core/package-root.ts
20797
+ import { existsSync } from "node:fs";
20798
+ import path from "node:path";
20799
+ import { fileURLToPath } from "node:url";
20800
+ var packageRoot = () => {
20801
+ let dir = path.dirname(fileURLToPath(import.meta.url));
20802
+ while (true) {
20803
+ const parent = path.dirname(dir);
20804
+ if (existsSync(path.join(dir, "package.json")) || parent === dir)
20805
+ return dir;
20806
+ dir = parent;
20807
+ }
20808
+ };
20809
+ var assetRoot = () => {
20810
+ const root = packageRoot();
20811
+ const assets = path.join(root, "assets");
20812
+ return existsSync(assets) ? assets : root;
20813
+ };
20783
20814
 
20784
20815
  // node_modules/@openclaw/fs-safe/dist/errors.js
20785
20816
  var OPERATIONAL_CODES = new Set([
@@ -20830,7 +20861,7 @@ function sameFileIdentity(left, right, platform = process.platform) {
20830
20861
  // node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
20831
20862
  import { randomBytes } from "node:crypto";
20832
20863
  import fsSync from "node:fs";
20833
- import path from "node:path";
20864
+ import path2 from "node:path";
20834
20865
 
20835
20866
  // node_modules/@openclaw/fs-safe/dist/bounded-read.js
20836
20867
  import fs from "node:fs";
@@ -20920,13 +20951,13 @@ ${ownershipToken}
20920
20951
  return { raw, ownershipToken };
20921
20952
  }
20922
20953
  function relativeSidecarLockPath(lockRoot, lockPath) {
20923
- const resolved = path.resolve(lockPath);
20924
- const lexicalRelative = path.relative(lockRoot.rootDir, resolved);
20925
- const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path.sep}`) && !path.isAbsolute(lexicalRelative) ? lexicalRelative : path.relative(lockRoot.rootReal, resolved);
20926
- if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
20954
+ const resolved = path2.resolve(lockPath);
20955
+ const lexicalRelative = path2.relative(lockRoot.rootDir, resolved);
20956
+ const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(lexicalRelative) ? lexicalRelative : path2.relative(lockRoot.rootReal, resolved);
20957
+ if (!relative || relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative)) {
20927
20958
  throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
20928
20959
  }
20929
- return relative.split(path.sep).join(path.posix.sep);
20960
+ return relative.split(path2.sep).join(path2.posix.sep);
20930
20961
  }
20931
20962
  function parseSidecarLockPayload(raw, parser) {
20932
20963
  if (parser) {
@@ -21073,7 +21104,7 @@ function getFsSafeLockConfig() {
21073
21104
 
21074
21105
  // node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
21075
21106
  import fs3 from "node:fs";
21076
- import path2 from "node:path";
21107
+ import path3 from "node:path";
21077
21108
 
21078
21109
  // node_modules/@openclaw/fs-safe/dist/timing.js
21079
21110
  function sleepSync(ms) {
@@ -21164,26 +21195,26 @@ function canonicalLockParentSync(parent) {
21164
21195
  return process.platform === "win32" ? fs3.realpathSync.native(parent) : fs3.realpathSync(parent);
21165
21196
  }
21166
21197
  function normalizeTargetPath(targetPath) {
21167
- const resolved = path2.resolve(targetPath);
21168
- fs3.mkdirSync(path2.dirname(resolved), { recursive: true });
21198
+ const resolved = path3.resolve(targetPath);
21199
+ fs3.mkdirSync(path3.dirname(resolved), { recursive: true });
21169
21200
  try {
21170
- return path2.join(canonicalLockParentSync(path2.dirname(resolved)), path2.basename(resolved));
21201
+ return path3.join(canonicalLockParentSync(path3.dirname(resolved)), path3.basename(resolved));
21171
21202
  } catch {
21172
21203
  return resolved;
21173
21204
  }
21174
21205
  }
21175
21206
  function boundedLockPath(lockPath, lockRoot) {
21176
- const resolved = path2.resolve(lockPath);
21207
+ const resolved = path3.resolve(lockPath);
21177
21208
  if (!lockRoot)
21178
21209
  return resolved;
21179
21210
  relativeSidecarLockPath(lockRoot, resolved);
21180
- const parent = path2.dirname(resolved);
21211
+ const parent = path3.dirname(resolved);
21181
21212
  const parentReal = canonicalLockParentSync(parent);
21182
- const parentRelative = path2.relative(lockRoot.rootReal, parentReal);
21183
- if (parentRelative === ".." || parentRelative.startsWith(`..${path2.sep}`) || path2.isAbsolute(parentRelative)) {
21213
+ const parentRelative = path3.relative(lockRoot.rootReal, parentReal);
21214
+ if (parentRelative === ".." || parentRelative.startsWith(`..${path3.sep}`) || path3.isAbsolute(parentRelative)) {
21184
21215
  throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
21185
21216
  }
21186
- return path2.join(parentReal, path2.basename(resolved));
21217
+ return path3.join(parentReal, path3.basename(resolved));
21187
21218
  }
21188
21219
  function defaultShouldReclaim(snapshot, staleMs, nowMs) {
21189
21220
  const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
@@ -21396,6 +21427,33 @@ var metadataLockSchema = object4({
21396
21427
  nonce: string5().min(1)
21397
21428
  }).strict();
21398
21429
  var now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
21430
+ var cachedRuntimeVersion = null;
21431
+ var runtimeVersion = () => {
21432
+ if (cachedRuntimeVersion === null) {
21433
+ try {
21434
+ const pkg = JSON.parse(fs4.readFileSync(path4.join(packageRoot(), "package.json"), "utf8"));
21435
+ cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
21436
+ } catch {
21437
+ cachedRuntimeVersion = "0.0.0";
21438
+ }
21439
+ }
21440
+ return cachedRuntimeVersion;
21441
+ };
21442
+ var stampNew = (record) => ({
21443
+ createdWith: record.runtime?.createdWith ?? null,
21444
+ updatedWith: runtimeVersion()
21445
+ });
21446
+ var isNewerVersion = (candidate, current) => {
21447
+ const parts = (value) => value.split("-")[0].split(".").map((item) => Number.parseInt(item, 10) || 0);
21448
+ const [next, now] = [parts(candidate), parts(current)];
21449
+ for (let index = 0;index < 3; index += 1) {
21450
+ const left = next[index] ?? 0;
21451
+ const right = now[index] ?? 0;
21452
+ if (left !== right)
21453
+ return left > right;
21454
+ }
21455
+ return false;
21456
+ };
21399
21457
  var jsonBytes = (value) => `${canonicalJson(value)}
21400
21458
  `;
21401
21459
  var digestBytes = (value) => createHash2("sha256").update(value).digest("hex");
@@ -21422,12 +21480,12 @@ var sameMetadataLock = (left, right) => {
21422
21480
  class TaskStore {
21423
21481
  root;
21424
21482
  constructor(root) {
21425
- this.root = fs4.existsSync(root) ? fs4.realpathSync(root) : path3.resolve(root);
21483
+ this.root = fs4.existsSync(root) ? fs4.realpathSync(root) : path4.resolve(root);
21426
21484
  }
21427
21485
  readTask(taskId) {
21428
21486
  if (!validId(taskId))
21429
21487
  return failure("invalid_input", "task ID is invalid", { taskId });
21430
- const result = this.readRecord(path3.join(this.tasksDir, `${taskId}.json`), taskRecordSchema);
21488
+ const result = this.readRecord(path4.join(this.tasksDir, `${taskId}.json`), taskRecordSchema);
21431
21489
  if (!result.exists)
21432
21490
  return failure("not_found", "task not found", { taskId });
21433
21491
  if (result.result.ok && result.result.data.id !== taskId)
@@ -21461,7 +21519,7 @@ class TaskStore {
21461
21519
  });
21462
21520
  const tasks = [];
21463
21521
  for (const name of names.sort()) {
21464
- const item = this.readRecord(path3.join(this.tasksDir, name), taskRecordSchema);
21522
+ const item = this.readRecord(path4.join(this.tasksDir, name), taskRecordSchema);
21465
21523
  if (!item.exists)
21466
21524
  continue;
21467
21525
  if (!item.result.ok)
@@ -21495,7 +21553,7 @@ class TaskStore {
21495
21553
  if (!current.ok)
21496
21554
  return current;
21497
21555
  if (current.data ? value.expectedWorkspaceRevision !== current.data.revision : value.expectedWorkspaceRevision !== null) {
21498
- return failure("revision_conflict", "workspace revision does not match", {
21556
+ return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
21499
21557
  expectedWorkspaceRevision: value.expectedWorkspaceRevision,
21500
21558
  actualWorkspaceRevision: current.data?.revision ?? null
21501
21559
  });
@@ -21507,11 +21565,17 @@ class TaskStore {
21507
21565
  const previousWorkspaceBytes = current.data ? this.snapshotBytes(this.workspacePath) : null;
21508
21566
  if (current.data && !previousWorkspaceBytes)
21509
21567
  return failure("storage_error", "workspace snapshot disappeared during creation");
21510
- const workspace = current.data ? { ...current.data, revision: newRevision(), root: this.root } : {
21568
+ const workspace = current.data ? {
21569
+ ...current.data,
21570
+ revision: newRevision(),
21571
+ root: this.root,
21572
+ runtime: stampNew(current.data)
21573
+ } : {
21511
21574
  schemaVersion: SCHEMA_VERSION,
21512
21575
  id: newId(),
21513
21576
  revision: newRevision(),
21514
21577
  root: this.root,
21578
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
21515
21579
  writer: null
21516
21580
  };
21517
21581
  const timestamp = value.now ?? now();
@@ -21533,6 +21597,7 @@ class TaskStore {
21533
21597
  status: "active",
21534
21598
  closure: null,
21535
21599
  progress: { summary: "", nextAction: null, blockers: [] },
21600
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
21536
21601
  assessments: [],
21537
21602
  policy: null,
21538
21603
  policyChanges: [],
@@ -21563,7 +21628,7 @@ class TaskStore {
21563
21628
  if (!current.ok)
21564
21629
  return current;
21565
21630
  if (current.data ? input.expectedWorkspaceRevision !== current.data.revision : input.expectedWorkspaceRevision !== null)
21566
- return failure("revision_conflict", "workspace revision does not match", {
21631
+ return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
21567
21632
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
21568
21633
  actualWorkspaceRevision: current.data?.revision ?? null
21569
21634
  });
@@ -21571,11 +21636,17 @@ class TaskStore {
21571
21636
  if (current.data && !previousWorkspaceBytes)
21572
21637
  return failure("storage_error", "workspace snapshot disappeared during import");
21573
21638
  const timestamp = input.now ?? now();
21574
- const workspace = current.data ? { ...current.data, revision: newRevision(), root: this.root } : {
21639
+ const workspace = current.data ? {
21640
+ ...current.data,
21641
+ revision: newRevision(),
21642
+ root: this.root,
21643
+ runtime: stampNew(current.data)
21644
+ } : {
21575
21645
  schemaVersion: SCHEMA_VERSION,
21576
21646
  id: input.workspaceId ?? newId(),
21577
21647
  revision: newRevision(),
21578
21648
  root: this.root,
21649
+ runtime: { createdWith: runtimeVersion(), updatedWith: runtimeVersion() },
21579
21650
  writer: null
21580
21651
  };
21581
21652
  const task = {
@@ -21583,7 +21654,11 @@ class TaskStore {
21583
21654
  workspaceId: workspace.id,
21584
21655
  revision: newRevision(),
21585
21656
  createdAt: timestamp,
21586
- updatedAt: timestamp
21657
+ updatedAt: timestamp,
21658
+ runtime: {
21659
+ createdWith: input.task.runtime?.createdWith ?? null,
21660
+ updatedWith: runtimeVersion()
21661
+ }
21587
21662
  };
21588
21663
  const validTask = taskRecordSchema.safeParse(task);
21589
21664
  if (!validTask.success)
@@ -21632,7 +21707,8 @@ class TaskStore {
21632
21707
  workspaceId: current.data.workspaceId,
21633
21708
  createdAt: current.data.createdAt,
21634
21709
  revision: context.revision,
21635
- updatedAt: context.now
21710
+ updatedAt: context.now,
21711
+ runtime: stampNew(current.data)
21636
21712
  };
21637
21713
  const valid = taskRecordSchema.safeParse(record);
21638
21714
  if (!valid.success)
@@ -21666,7 +21742,8 @@ class TaskStore {
21666
21742
  ...changed.data,
21667
21743
  id: current.data.id,
21668
21744
  root: this.root,
21669
- revision: context.revision
21745
+ revision: context.revision,
21746
+ runtime: stampNew(current.data)
21670
21747
  };
21671
21748
  const valid = workspaceRecordSchema.safeParse(record);
21672
21749
  if (!valid.success)
@@ -21709,7 +21786,8 @@ class TaskStore {
21709
21786
  ...changedWorkspace.data,
21710
21787
  id: workspace.data.id,
21711
21788
  root: this.root,
21712
- revision: workspaceContext.revision
21789
+ revision: workspaceContext.revision,
21790
+ runtime: stampNew(workspace.data)
21713
21791
  });
21714
21792
  if (!nextWorkspace.success)
21715
21793
  return failure("invalid_input", "workspace mutation produced an invalid record");
@@ -21734,7 +21812,8 @@ class TaskStore {
21734
21812
  workspaceId: task.data.workspaceId,
21735
21813
  createdAt: task.data.createdAt,
21736
21814
  revision: taskContext.revision,
21737
- updatedAt: taskContext.now
21815
+ updatedAt: taskContext.now,
21816
+ runtime: stampNew(task.data)
21738
21817
  });
21739
21818
  if (!nextTask.success) {
21740
21819
  this.markUncertain(nextWorkspace.data);
@@ -21761,7 +21840,7 @@ class TaskStore {
21761
21840
  if (match)
21762
21841
  candidates.push({
21763
21842
  target: match[1],
21764
- path: path3.join(this.recoveryDir, name),
21843
+ path: path4.join(this.recoveryDir, name),
21765
21844
  digest: match[3]
21766
21845
  });
21767
21846
  }
@@ -21954,7 +22033,8 @@ class TaskStore {
21954
22033
  const value = {
21955
22034
  ...workspace,
21956
22035
  writer: { ...workspace.writer, state: "uncertain" },
21957
- revision: newRevision()
22036
+ revision: newRevision(),
22037
+ runtime: stampNew(workspace)
21958
22038
  };
21959
22039
  this.replaceSnapshot(this.workspacePath, value, workspace);
21960
22040
  }
@@ -22032,7 +22112,7 @@ class TaskStore {
22032
22112
  }
22033
22113
  fs4.renameSync(temporary, file);
22034
22114
  temporary = undefined;
22035
- this.fsyncDirectory(path3.dirname(file));
22115
+ this.fsyncDirectory(path4.dirname(file));
22036
22116
  return success(null, null, value);
22037
22117
  } catch (error) {
22038
22118
  return failure("storage_error", `snapshot replacement failed: ${String(error)}`, {
@@ -22046,9 +22126,9 @@ class TaskStore {
22046
22126
  }
22047
22127
  }
22048
22128
  saveRecovery(file, bytes) {
22049
- const target = path3.basename(file) === "workspace.json" ? "workspace" : "task";
22050
- const id = target === "task" ? path3.basename(file, ".json") : "workspace";
22051
- const destination = path3.join(this.recoveryDir, `${target}.${id}.${digestBytes(bytes)}.json`);
22129
+ const target = path4.basename(file) === "workspace.json" ? "workspace" : "task";
22130
+ const id = target === "task" ? path4.basename(file, ".json") : "workspace";
22131
+ const destination = path4.join(this.recoveryDir, `${target}.${id}.${digestBytes(bytes)}.json`);
22052
22132
  if (fs4.existsSync(destination)) {
22053
22133
  if (digestBytes(fs4.readFileSync(destination)) === digestBytes(bytes))
22054
22134
  return;
@@ -22126,7 +22206,7 @@ class TaskStore {
22126
22206
  for (const name of fs4.readdirSync(this.tasksDir)) {
22127
22207
  if (!name.endsWith(".json") || !validId(name.slice(0, -5)))
22128
22208
  return null;
22129
- const parsed = this.parseBytes(fs4.readFileSync(path3.join(this.tasksDir, name)), taskRecordSchema);
22209
+ const parsed = this.parseBytes(fs4.readFileSync(path4.join(this.tasksDir, name)), taskRecordSchema);
22130
22210
  if (!parsed.ok || parsed.data.id !== name.slice(0, -5))
22131
22211
  return null;
22132
22212
  ids.add(parsed.data.workspaceId);
@@ -22168,7 +22248,12 @@ class TaskStore {
22168
22248
  if (isObject3(value) && "schemaVersion" in value && value.schemaVersion !== SCHEMA_VERSION)
22169
22249
  return failure("unsupported_version", "unsupported snapshot schema version");
22170
22250
  const parsed = schema.safeParse(value);
22171
- return parsed.success ? success(null, null, parsed.data) : failure("recovery_required", "snapshot does not satisfy its schema");
22251
+ if (parsed.success)
22252
+ return success(null, null, parsed.data);
22253
+ const writerVersion = isObject3(value) && isObject3(value.runtime) ? value.runtime.updatedWith : null;
22254
+ if (typeof writerVersion === "string" && isNewerVersion(writerVersion, runtimeVersion()))
22255
+ return failure("recovery_required", `snapshot was written by workit ${writerVersion}; upgrade Workit before mutating this checkout`);
22256
+ return failure("recovery_required", "snapshot does not satisfy its schema");
22172
22257
  }
22173
22258
  processStart(pid) {
22174
22259
  try {
@@ -22178,43 +22263,98 @@ class TaskStore {
22178
22263
  }
22179
22264
  }
22180
22265
  conflict(expected, actual) {
22181
- return failure("revision_conflict", "snapshot revision does not match", {
22266
+ return failure("revision_conflict", "snapshot revision does not match; omit expectedRevision to use the current record", {
22182
22267
  expectedRevision: expected,
22183
22268
  actualRevision: actual
22184
22269
  });
22185
22270
  }
22186
22271
  taskPath(taskId) {
22187
- return path3.join(this.tasksDir, `${taskId}.json`);
22272
+ return path4.join(this.tasksDir, `${taskId}.json`);
22188
22273
  }
22189
22274
  get workitDir() {
22190
- return path3.join(this.root, ".workit");
22275
+ return path4.join(this.root, ".workit");
22191
22276
  }
22192
22277
  get tasksDir() {
22193
- return path3.join(this.workitDir, "tasks");
22278
+ return path4.join(this.workitDir, "tasks");
22194
22279
  }
22195
22280
  get recoveryDir() {
22196
- return path3.join(this.workitDir, "recovery");
22281
+ return path4.join(this.workitDir, "recovery");
22197
22282
  }
22198
22283
  get workspacePath() {
22199
- return path3.join(this.workitDir, "workspace.json");
22284
+ return path4.join(this.workitDir, "workspace.json");
22200
22285
  }
22201
22286
  get lockPath() {
22202
- return path3.join(this.workitDir, "metadata.lock");
22287
+ return path4.join(this.workitDir, "metadata.lock");
22203
22288
  }
22204
22289
  get gitignorePath() {
22205
- return path3.join(this.workitDir, ".gitignore");
22290
+ return path4.join(this.workitDir, ".gitignore");
22206
22291
  }
22207
22292
  }
22293
+ // packages/workit-core/src/core/workers.ts
22294
+ var same = (left, right) => {
22295
+ try {
22296
+ return canonicalJson(left) === canonicalJson(right);
22297
+ } catch {
22298
+ return false;
22299
+ }
22300
+ };
22301
+ var UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
22302
+ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
22303
+ var callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor };
22304
+ var validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
22305
+ const code = char.charCodeAt(0);
22306
+ return code < 32 || code === 127;
22307
+ }) && !value.split(/[\\/]/).includes("..");
22308
+ var callerValue = (value) => ({ host: value.host, actor: value.actor });
22309
+ var workerIdOf = (value) => value.workerId ?? null;
22310
+ function assertProductWriteAllowed(input) {
22311
+ if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
22312
+ return failure("invalid_input", "invalid product write path");
22313
+ if (!input.store)
22314
+ return failure("permission_denied", "write authorization requires core state");
22315
+ const task = input.store.readTask(input.task.id);
22316
+ if (!task.ok)
22317
+ return task;
22318
+ const workspace = input.store.readWorkspace();
22319
+ if (!workspace.ok)
22320
+ return workspace;
22321
+ if (!workspace.data)
22322
+ return failure("not_found", "workspace not found");
22323
+ const workspaceRecord = workspace.data;
22324
+ if (task.data.status !== "active")
22325
+ return failure("invalid_transition", "paused or closed tasks cannot own product writes");
22326
+ if (workspaceRecord.id !== task.data.workspaceId || workspaceRecord.root.length === 0)
22327
+ return failure("recovery_required", "task and workspace bindings are invalid");
22328
+ const writer = workspaceRecord.writer;
22329
+ if (!writer)
22330
+ return failure("permission_denied", "checkout has no writer owner");
22331
+ if (writer.state === "uncertain")
22332
+ return failure("recovery_required", "checkout writer requires recovery", {
22333
+ owner: writer.owner
22334
+ });
22335
+ const caller = callerValue(input.caller);
22336
+ const workerId = workerIdOf(input.caller);
22337
+ const owner = writer.owner;
22338
+ if (owner.taskId !== task.data.id || owner.workerId !== workerId || owner.session.host !== caller.host || owner.session.handle !== caller.actor)
22339
+ return failure("writer_conflict", "checkout is owned by another validated actor", { owner });
22340
+ const worker = workerId === null ? null : task.data.workers.find((entry) => entry.id === workerId);
22341
+ if (workerId !== null && (!worker || worker.data.assignment.role !== "implementer"))
22342
+ return failure("permission_denied", "only implementers can own product writes");
22343
+ 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))))
22344
+ return failure("recovery_required", "worker session is not an active writer");
22345
+ return success(null, workspaceRecord.revision, owner);
22346
+ }
22347
+
22208
22348
  // packages/workit-core/src/core/task-evaluation.ts
22209
22349
  import { createHash as createHash4 } from "node:crypto";
22210
22350
  import * as fs6 from "node:fs";
22211
22351
  import { spawnSync } from "node:child_process";
22212
- import path5 from "node:path";
22352
+ import path6 from "node:path";
22213
22353
 
22214
22354
  // packages/workit-core/src/core/authority.ts
22215
22355
  import { createHash as createHash3 } from "node:crypto";
22216
22356
  import * as fs5 from "node:fs";
22217
- import path4 from "node:path";
22357
+ import path5 from "node:path";
22218
22358
  var verifiedAuthorities = new WeakMap;
22219
22359
  var trustedAuthority = (kind, provenance, expected, binding, caller) => {
22220
22360
  const token = {};
@@ -22341,20 +22481,31 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
22341
22481
  continue;
22342
22482
  if (!reference.digest)
22343
22483
  return failure("invalid_input", "document references require a byte digest");
22344
- const target = path4.resolve(root, reference.path);
22345
- if (target !== root && !target.startsWith(`${root}${path4.sep}`))
22346
- return failure("invalid_input", "document reference escapes checkout");
22484
+ const target = path5.resolve(root, reference.path);
22485
+ if (target !== root && !target.startsWith(`${root}${path5.sep}`))
22486
+ return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
22487
+ fields: [
22488
+ { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
22489
+ ]
22490
+ });
22347
22491
  try {
22348
- const relative = path4.relative(root, target);
22492
+ const relative = path5.relative(root, target);
22349
22493
  let current = root;
22350
- for (const segment of relative.split(path4.sep)) {
22494
+ for (const segment of relative.split(path5.sep)) {
22351
22495
  if (!segment)
22352
22496
  continue;
22353
- current = path4.join(current, segment);
22497
+ current = path5.join(current, segment);
22354
22498
  const stat = fs5.lstatSync(current);
22355
22499
  const real = fs5.realpathSync(current);
22356
- if (real !== root && !real.startsWith(`${root}${path4.sep}`))
22357
- return failure("invalid_input", "document reference escapes checkout");
22500
+ if (real !== root && !real.startsWith(`${root}${path5.sep}`))
22501
+ return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
22502
+ fields: [
22503
+ {
22504
+ path: "contentRefs.path",
22505
+ reason: `${reference.path} resolves outside the checkout`
22506
+ }
22507
+ ]
22508
+ });
22358
22509
  if (stat.isSymbolicLink())
22359
22510
  return failure("invalid_input", "document reference uses a symlink");
22360
22511
  if (current === target && !stat.isFile())
@@ -22701,14 +22852,14 @@ var verifyDecisionContent = verifyContentRefs;
22701
22852
 
22702
22853
  // packages/workit-core/src/core/task-evaluation.ts
22703
22854
  var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex");
22704
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path5.sep}`);
22705
- var relative = (root, candidate) => path5.relative(root, candidate).split(path5.sep).join("/") || ".";
22855
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path6.sep}`);
22856
+ var relative = (root, candidate) => path6.relative(root, candidate).split(path6.sep).join("/") || ".";
22706
22857
  var canonicalPath = (value) => {
22707
22858
  if (typeof value !== "string" || !value)
22708
22859
  return null;
22709
22860
  if (value.includes("\\") || value.split("/").includes(".."))
22710
22861
  return null;
22711
- const normalized = path5.posix.normalize(value);
22862
+ const normalized = path6.posix.normalize(value);
22712
22863
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
22713
22864
  return null;
22714
22865
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -22756,14 +22907,14 @@ var gitPaths = (root) => {
22756
22907
  git: /not a git repository/i.test(stderr) ? false : true
22757
22908
  };
22758
22909
  }
22759
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path5.sep).join("/")) : [];
22910
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path6.sep).join("/")) : [];
22760
22911
  const stagedDeleted = spawnSync("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
22761
22912
  if (stagedDeleted.status !== 0) {
22762
22913
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
22763
22914
  if (!/not a git repository/i.test(stderr))
22764
22915
  return { paths, uncertain: true, git: true };
22765
22916
  } else if (stagedDeleted.stdout) {
22766
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path5.sep).join("/")));
22917
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path6.sep).join("/")));
22767
22918
  }
22768
22919
  return { paths: [...new Set(paths)], uncertain: false, git: true };
22769
22920
  };
@@ -22778,7 +22929,7 @@ var walk = (root, directory, output) => {
22778
22929
  for (const name of names) {
22779
22930
  if (name === ".git" || name === ".workit")
22780
22931
  continue;
22781
- const target = path5.join(directory, name);
22932
+ const target = path6.join(directory, name);
22782
22933
  let stat;
22783
22934
  try {
22784
22935
  stat = fs6.lstatSync(target);
@@ -22796,17 +22947,17 @@ var walk = (root, directory, output) => {
22796
22947
  var scopeRoots = (root, scope) => {
22797
22948
  const roots = [];
22798
22949
  for (const item of scope.paths.length ? scope.paths : ["."]) {
22799
- const target = path5.resolve(root, item);
22950
+ const target = path6.resolve(root, item);
22800
22951
  if (!inside(root, target))
22801
- return failure("invalid_input", "candidate scope escapes checkout");
22952
+ 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` }] });
22802
22953
  let ancestor = target;
22803
22954
  while (!fs6.existsSync(ancestor) && ancestor !== root)
22804
- ancestor = path5.dirname(ancestor);
22955
+ ancestor = path6.dirname(ancestor);
22805
22956
  try {
22806
22957
  if (!inside(root, fs6.realpathSync(ancestor)))
22807
- return failure("invalid_input", "candidate scope escapes checkout");
22958
+ 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` }] });
22808
22959
  } catch {
22809
- return failure("invalid_input", "candidate scope cannot be inspected");
22960
+ return failure("invalid_input", `candidate scope cannot be inspected: ${item}`);
22810
22961
  }
22811
22962
  roots.push(target);
22812
22963
  }
@@ -22821,7 +22972,7 @@ function captureCandidate(root, scope, environment = []) {
22821
22972
  }
22822
22973
  const normalizedScope = canonicalScope(scope);
22823
22974
  if (!normalizedScope)
22824
- return failure("invalid_input", "candidate scope is invalid");
22975
+ 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" }] });
22825
22976
  const roots = scopeRoots(checkout, normalizedScope);
22826
22977
  if (!roots.ok)
22827
22978
  return roots;
@@ -22853,7 +23004,7 @@ function captureCandidate(root, scope, environment = []) {
22853
23004
  for (const item of [...names].sort()) {
22854
23005
  if (!scopeMatches(item, normalizedScope))
22855
23006
  continue;
22856
- const target = path5.join(checkout, item);
23007
+ const target = path6.join(checkout, item);
22857
23008
  try {
22858
23009
  const stat = fs6.lstatSync(target);
22859
23010
  if (stat.isSymbolicLink()) {
@@ -23010,7 +23161,7 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
23010
23161
  return true;
23011
23162
  const reviewSession = sessionFromRef(entry.data.reviewContext);
23012
23163
  const sameImplementation = sameSession(reviewSession, task.intent.provenance.session);
23013
- const sameEvidenceSession = task.evidence.some((other) => other.id !== entry.id && sameSession(reviewSession, other.provenance.session));
23164
+ const sameEvidenceSession = task.evidence.some((other) => other.id !== entry.id && other.data.kind === "review" && sameSession(reviewSession, other.provenance.session));
23014
23165
  return entry.data.kind === "review" && reviewSession !== null && sameSession(reviewSession, entry.provenance.session) && (requirement.ruleId === "self-review" || !sameImplementation && !sameEvidenceSession);
23015
23166
  });
23016
23167
  if (passed.length) {
@@ -23106,7 +23257,17 @@ function evaluateClosure(requestedOutcome, view) {
23106
23257
  const blocking = evaluations.filter((item) => item.status === "unsatisfied" || item.status === "unavailable");
23107
23258
  if (requestedOutcome !== "stopped" && blocking.length)
23108
23259
  return failure("requirements_unsatisfied", "applicable requirements are unsatisfied", {
23109
- requirementIds: blocking.map((item) => item.requirementId)
23260
+ requirementIds: blocking.map((item) => item.requirementId),
23261
+ requirements: blocking.map((item) => {
23262
+ const requirement = view.task.policy?.requirements.find((candidate) => candidate.id === item.requirementId);
23263
+ return {
23264
+ requirementId: item.requirementId,
23265
+ ruleId: requirement?.ruleId ?? "unknown",
23266
+ reason: item.reason,
23267
+ satisfaction: requirement?.satisfaction ?? "",
23268
+ dependentAction: requirement?.dependentAction ?? null
23269
+ };
23270
+ })
23110
23271
  });
23111
23272
  const accepted = evaluations.filter((item) => item.status === "accepted_limitation");
23112
23273
  if (requestedOutcome === "verified" && accepted.length)
@@ -23142,7 +23303,7 @@ function reconcileResume(view, observations = []) {
23142
23303
  return candidate;
23143
23304
  const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
23144
23305
  const blockers = [...view.task.progress.blockers];
23145
- if (view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
23306
+ if (view.task.workers.some((entry) => isUncertainWorker(entry.data.state)))
23146
23307
  blockers.push({
23147
23308
  reason: "worker state requires reconciliation",
23148
23309
  dependentAction: "resume",
@@ -23175,7 +23336,11 @@ var externalActionSchema = discriminatedUnion2("operation", [
23175
23336
  }).strict(),
23176
23337
  object4({
23177
23338
  operation: literal3("git.commit"),
23178
- payload: object4({ message: string5().min(1) }).strict()
23339
+ payload: object4({
23340
+ message: string5().min(1).optional(),
23341
+ plan_steps: array3(string5().min(1)).optional(),
23342
+ plan_branch: string5().min(1).optional()
23343
+ }).strict()
23179
23344
  }).strict(),
23180
23345
  object4({
23181
23346
  operation: literal3("git.push"),
@@ -23410,7 +23575,7 @@ function resolveRequirements(input) {
23410
23575
  ].join(", ")}`,
23411
23576
  satisfaction: "An observed or inferred fact resolves the uncertainty and records its supporting references.",
23412
23577
  before: "dependent_action",
23413
- dependentAction: "dependent-action",
23578
+ dependentAction: null,
23414
23579
  acceptanceAllowed: false
23415
23580
  }));
23416
23581
  if (signals.productChoiceOpen.value === true)
@@ -23421,7 +23586,7 @@ function resolveRequirements(input) {
23421
23586
  reason: `A consequential product choice remains open: ${signalReason(input, "productChoiceOpen")}`,
23422
23587
  satisfaction: "Present the material alternatives and record the user's decision before the dependent action.",
23423
23588
  before: "dependent_action",
23424
- dependentAction: "dependent-action",
23589
+ dependentAction: null,
23425
23590
  acceptanceAllowed: false
23426
23591
  }));
23427
23592
  if (signals.behaviorChange.value === true)
@@ -23579,59 +23744,6 @@ function diffPolicy(previous, next, reason, now) {
23579
23744
  return parsed.data;
23580
23745
  }
23581
23746
 
23582
- // packages/workit-core/src/core/workers.ts
23583
- var same = (left, right) => {
23584
- try {
23585
- return canonicalJson(left) === canonicalJson(right);
23586
- } catch {
23587
- return false;
23588
- }
23589
- };
23590
- var callerSession = (caller) => caller.session ?? { kind: "host", host: caller.host, handle: caller.actor };
23591
- var validPath = (value) => typeof value === "string" && value.length > 0 && ![...value].some((char) => {
23592
- const code = char.charCodeAt(0);
23593
- return code < 32 || code === 127;
23594
- }) && !value.split(/[\\/]/).includes("..");
23595
- var callerValue = (value) => ({ host: value.host, actor: value.actor });
23596
- var workerIdOf = (value) => value.workerId ?? null;
23597
- function assertProductWriteAllowed(input) {
23598
- if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
23599
- return failure("invalid_input", "invalid product write path");
23600
- if (!input.store)
23601
- return failure("permission_denied", "write authorization requires core state");
23602
- const task = input.store.readTask(input.task.id);
23603
- if (!task.ok)
23604
- return task;
23605
- const workspace = input.store.readWorkspace();
23606
- if (!workspace.ok)
23607
- return workspace;
23608
- if (!workspace.data)
23609
- return failure("not_found", "workspace not found");
23610
- const workspaceRecord = workspace.data;
23611
- if (task.data.status !== "active")
23612
- return failure("invalid_transition", "paused or closed tasks cannot own product writes");
23613
- if (workspaceRecord.id !== task.data.workspaceId || workspaceRecord.root.length === 0)
23614
- return failure("recovery_required", "task and workspace bindings are invalid");
23615
- const writer = workspaceRecord.writer;
23616
- if (!writer)
23617
- return failure("permission_denied", "checkout has no writer owner");
23618
- if (writer.state === "uncertain")
23619
- return failure("recovery_required", "checkout writer requires recovery", {
23620
- owner: writer.owner
23621
- });
23622
- const caller = callerValue(input.caller);
23623
- const workerId = workerIdOf(input.caller);
23624
- const owner = writer.owner;
23625
- if (owner.taskId !== task.data.id || owner.workerId !== workerId || owner.session.host !== caller.host || owner.session.handle !== caller.actor)
23626
- return failure("writer_conflict", "checkout is owned by another validated actor", { owner });
23627
- const worker = workerId === null ? null : task.data.workers.find((entry) => entry.id === workerId);
23628
- if (workerId !== null && (!worker || worker.data.assignment.role !== "implementer"))
23629
- return failure("permission_denied", "only implementers can own product writes");
23630
- 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))))
23631
- return failure("recovery_required", "worker session is not an active writer");
23632
- return success(null, workspaceRecord.revision, owner);
23633
- }
23634
-
23635
23747
  // packages/workit-core/src/core/task-engine.ts
23636
23748
  var provenance = (context, kind = context.provenanceKind ?? "host_observed") => ({
23637
23749
  kind,
@@ -23762,15 +23874,20 @@ var applyWorkerLifecycle = (input) => {
23762
23874
  return failure("invalid_transition", "worker has not started");
23763
23875
  if (input.state === "running" && entry.data.state === "stopped")
23764
23876
  return failure("invalid_transition", "stopped worker cannot run again");
23877
+ const terminalCancel = entry.data.state === "cancelling" && input.state === "running";
23765
23878
  const nextEntry = {
23766
23879
  ...entry,
23767
23880
  recordedAt: input.now ?? entry.recordedAt,
23768
23881
  provenance: authority.provenance,
23769
- data: { ...entry.data, state: input.state, session: input.session }
23882
+ data: {
23883
+ ...entry.data,
23884
+ state: terminalCancel ? "cancelling" : input.state,
23885
+ session: input.session
23886
+ }
23770
23887
  };
23771
23888
  const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
23772
23889
  const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
23773
- if (entry.data.state === input.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
23890
+ if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
23774
23891
  return success(task.data.revision, workspace.data.revision, entry);
23775
23892
  const changed = input.store.mutateTaskAndWorkspace({
23776
23893
  taskId: task.data.id,
@@ -24140,7 +24257,7 @@ class WorkitCore {
24140
24257
  activeWorkerBlocker(task, workspace) {
24141
24258
  if (workspace.writer)
24142
24259
  return failure("recovery_required", "writer ownership must be released first");
24143
- if (task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
24260
+ if (task.workers.some((entry) => isUncertainWorker(entry.data.state)))
24144
24261
  return failure("recovery_required", "worker state requires reconciliation");
24145
24262
  return null;
24146
24263
  }
@@ -24377,11 +24494,6 @@ class WorkitCore {
24377
24494
  if (helper && !helper.ok)
24378
24495
  return helper;
24379
24496
  const evidence = input.evidence;
24380
- if (helper?.ok) {
24381
- const assignment = helper.data.data.assignment;
24382
- 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))
24383
- return failure("permission_denied", "evidence is outside the worker assignment");
24384
- }
24385
24497
  if ((evidence.result === "missing" || evidence.result === "skipped") && !evidence.summary.trim())
24386
24498
  return failure("invalid_input", "missing or skipped evidence requires a reason");
24387
24499
  if (evidence.kind === "review") {
@@ -24398,6 +24510,11 @@ class WorkitCore {
24398
24510
  evidence.beforeCandidateId ??= null;
24399
24511
  evidence.candidateId ??= null;
24400
24512
  }
24513
+ if (helper?.ok) {
24514
+ const assignment = helper.data.data.assignment;
24515
+ 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))
24516
+ return failure("permission_denied", "evidence is outside the worker assignment");
24517
+ }
24401
24518
  const knownCandidates = new Set(task.data.candidates.map((candidate) => candidate.id));
24402
24519
  for (const candidateId of [evidence.beforeCandidateId, evidence.candidateId]) {
24403
24520
  if (candidateId && candidateId !== currentCandidate.data.id && !knownCandidates.has(candidateId))
@@ -25293,6 +25410,9 @@ class WorkitCore {
25293
25410
  id: task.id,
25294
25411
  revision: task.revision,
25295
25412
  workspaceRevision: workspace.data.revision,
25413
+ createdAt: task.createdAt,
25414
+ updatedAt: task.updatedAt,
25415
+ runtime: task.runtime ?? null,
25296
25416
  objective: task.intent.data.objective,
25297
25417
  status: task.status,
25298
25418
  closure: task.closure,
@@ -25378,7 +25498,7 @@ class WorkitCore {
25378
25498
  const base = reconcileResume(effectiveView);
25379
25499
  if (!base.ok)
25380
25500
  return base;
25381
- const needsReconciliation = view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
25501
+ const needsReconciliation = view.task.workers.some((entry) => isUncertainWorker(observedStates.get(entry.id) ?? entry.data.state));
25382
25502
  const blockers = [...base.data.blockers];
25383
25503
  if (needsReconciliation && !blockers.some((entry) => entry.reason === "worker state requires reconciliation"))
25384
25504
  blockers.push({
@@ -25445,7 +25565,8 @@ class WorkitCore {
25445
25565
  ...current,
25446
25566
  status,
25447
25567
  candidates: resumeCandidate && !current.candidates.some((candidate) => candidate.id === resumeCandidate.id) ? [...current.candidates, resumeCandidate] : current.candidates,
25448
- progress: status === "paused" ? { ...current.progress, summary: input.reason } : current.progress
25568
+ progress: current.progress,
25569
+ pauseReason: status === "paused" ? input.reason ?? null : null
25449
25570
  })
25450
25571
  });
25451
25572
  if (!changed.ok)
@@ -25481,7 +25602,7 @@ class WorkitCore {
25481
25602
  return workspace;
25482
25603
  if (!workspace.data)
25483
25604
  return failure("not_found", "workspace not found");
25484
- if (workspace.data.writer || task.data.workers.some((entry) => entry.data.state !== "stopped"))
25605
+ if (workspace.data.writer || task.data.workers.some((entry) => isUncertainWorker(entry.data.state)))
25485
25606
  return failure("recovery_required", "scope revision requires worker ownership reconciliation");
25486
25607
  this.fillRevisions(input, task.data, workspace.data);
25487
25608
  const changed = this.store.mutateTaskAndWorkspace({
@@ -25592,15 +25713,15 @@ function resolveGitRevision(root, value) {
25592
25713
  }
25593
25714
  function resolveInside(root, candidate) {
25594
25715
  const base = realpathSync5(root);
25595
- const target = path6.resolve(base, candidate);
25596
- if (target !== base && !target.startsWith(base + path6.sep)) {
25716
+ const target = path7.resolve(base, candidate);
25717
+ if (target !== base && !target.startsWith(base + path7.sep)) {
25597
25718
  throw new Error("path must stay inside repository root");
25598
25719
  }
25599
25720
  let ancestor = target;
25600
- while (!existsSync3(ancestor))
25601
- ancestor = path6.dirname(ancestor);
25721
+ while (!existsSync4(ancestor))
25722
+ ancestor = path7.dirname(ancestor);
25602
25723
  const canonicalAncestor = realpathSync5(ancestor);
25603
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path6.sep)) {
25724
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path7.sep)) {
25604
25725
  throw new Error("path must stay inside repository root");
25605
25726
  }
25606
25727
  return target;
@@ -25785,14 +25906,14 @@ var gitContext = (workspaceRoot, paths = []) => {
25785
25906
  import {
25786
25907
  copyFileSync,
25787
25908
  cpSync,
25788
- existsSync as existsSync4,
25909
+ existsSync as existsSync5,
25789
25910
  mkdirSync as mkdirSync2,
25790
25911
  readFileSync as readFileSync4,
25791
25912
  readdirSync as readdirSync3,
25792
25913
  writeFileSync as writeFileSync2
25793
25914
  } from "node:fs";
25794
25915
  import os2 from "node:os";
25795
- import path7 from "node:path";
25916
+ import path8 from "node:path";
25796
25917
  var diagnosticLogger;
25797
25918
  var PRESETS = {
25798
25919
  gitflow: {
@@ -25813,7 +25934,7 @@ var mergePreset = (preset, input = {}, current = {
25813
25934
  protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
25814
25935
  };
25815
25936
  };
25816
- var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path7.join(process.env.XDG_CONFIG_HOME || path7.join(os2.homedir(), ".config"), "workit");
25937
+ var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path8.join(process.env.XDG_CONFIG_HOME || path8.join(os2.homedir(), ".config"), "workit");
25817
25938
  var migratedDir = null;
25818
25939
  var migrationFailed = false;
25819
25940
  var ensureConfigDir = (dir = resolveConfigDir()) => {
@@ -25823,12 +25944,12 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
25823
25944
  migratedDir = dir;
25824
25945
  return dir;
25825
25946
  }
25826
- const legacy = path7.join(process.env.XDG_CONFIG_HOME || path7.join(os2.homedir(), ".config"), "workflow-toolkit");
25827
- if (!existsSync4(legacy)) {
25947
+ const legacy = path8.join(process.env.XDG_CONFIG_HOME || path8.join(os2.homedir(), ".config"), "workflow-toolkit");
25948
+ if (!existsSync5(legacy)) {
25828
25949
  migratedDir = dir;
25829
25950
  return dir;
25830
25951
  }
25831
- if (!migrationFailed && existsSync4(dir)) {
25952
+ if (!migrationFailed && existsSync5(dir)) {
25832
25953
  migratedDir = dir;
25833
25954
  return dir;
25834
25955
  }
@@ -25836,9 +25957,9 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
25836
25957
  mkdirSync2(dir, { recursive: true });
25837
25958
  diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
25838
25959
  for (const entry of readdirSync3(legacy, { withFileTypes: true })) {
25839
- const src = path7.join(legacy, entry.name);
25840
- const dest = path7.join(dir, entry.name);
25841
- if (existsSync4(dest))
25960
+ const src = path8.join(legacy, entry.name);
25961
+ const dest = path8.join(dir, entry.name);
25962
+ if (existsSync5(dest))
25842
25963
  continue;
25843
25964
  try {
25844
25965
  if (entry.isDirectory())
@@ -25911,7 +26032,7 @@ var parseConfigResult = (raw, file) => {
25911
26032
  };
25912
26033
  };
25913
26034
  var readConfigTyped = (dir) => {
25914
- const file = path7.join(dir ?? configDir(), "config.json");
26035
+ const file = path8.join(dir ?? configDir(), "config.json");
25915
26036
  return parseConfigResult(readSafe(file), file);
25916
26037
  };
25917
26038
  var readConfig = () => {
@@ -25947,9 +26068,9 @@ var COMMIT_PRESETS = [
25947
26068
 
25948
26069
  // packages/workit-core/src/core/workspaces.ts
25949
26070
  import { readFileSync as readFileSync5, realpathSync as realpathSync6 } from "node:fs";
25950
- import path8 from "node:path";
26071
+ import path9 from "node:path";
25951
26072
  var readWorkspacesResult = (dir = configDir()) => {
25952
- const file = path8.join(dir, "workspaces.json");
26073
+ const file = path9.join(dir, "workspaces.json");
25953
26074
  let raw;
25954
26075
  try {
25955
26076
  raw = readFileSync5(file, "utf8");
@@ -26047,10 +26168,10 @@ var resolveWorkspace = (cwd) => resolveWorkspaceFrom(cwd, configDir());
26047
26168
 
26048
26169
  // packages/workit-core/src/core/vcs-config.ts
26049
26170
  import fs7 from "node:fs";
26050
- import path9 from "node:path";
26171
+ import path10 from "node:path";
26051
26172
  import { spawnSync as spawnSync3 } from "node:child_process";
26052
26173
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
26053
- var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path9.join(configDir(), "vcs.json");
26174
+ var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path10.join(configDir(), "vcs.json");
26054
26175
  var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
26055
26176
  var remoteProvider = (cwd) => {
26056
26177
  const r = spawnSync3("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
@@ -26133,8 +26254,8 @@ function vcsConfig(mode, cwd) {
26133
26254
  }
26134
26255
  const prov = cfg[provider] ?? {};
26135
26256
  const wsTokenFile = typeof wsVcs.tokenFile === "string" && wsVcs.tokenFile.trim() !== "" ? wsVcs.tokenFile : null;
26136
- const tokenFile = String(wsTokenFile ?? prov.tokenFile ?? path9.join(configDir(), `${provider}.token`));
26137
- const tokenPath = path9.resolve(tokenFile);
26257
+ const tokenFile = String(wsTokenFile ?? prov.tokenFile ?? path10.join(configDir(), `${provider}.token`));
26258
+ const tokenPath = path10.resolve(tokenFile);
26138
26259
  let tokenOk = false;
26139
26260
  if (fs7.existsSync(tokenPath)) {
26140
26261
  const token = fs7.readFileSync(tokenPath, "utf8").trim();
@@ -26143,7 +26264,7 @@ function vcsConfig(mode, cwd) {
26143
26264
  }
26144
26265
  const out = {
26145
26266
  ok: true,
26146
- configPath: path9.resolve(cfgPath),
26267
+ configPath: path10.resolve(cfgPath),
26147
26268
  provider,
26148
26269
  defaultTargetBranch: defaultTarget,
26149
26270
  pr: cfg.pr ?? {},
@@ -26236,18 +26357,18 @@ var resolveBranchPolicyFor = (workspaceRoot) => resolveBranchPolicy(readConfig()
26236
26357
 
26237
26358
  // packages/workit-core/src/core/pr-create.ts
26238
26359
  import fs8 from "node:fs";
26239
- import path10 from "node:path";
26360
+ import path11 from "node:path";
26240
26361
  function parseGhIssue(value) {
26241
26362
  const m = /issues\/(\d+)/.exec(value);
26242
26363
  return m ? m[1] : String(value).trim().replace(/^#/, "");
26243
26364
  }
26244
26365
  function whichOnPath(tool) {
26245
26366
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
26246
- for (const dir of (process.env.PATH ?? "").split(path10.delimiter)) {
26367
+ for (const dir of (process.env.PATH ?? "").split(path11.delimiter)) {
26247
26368
  if (!dir)
26248
26369
  continue;
26249
26370
  for (const name of names) {
26250
- const candidate = path10.join(dir, name);
26371
+ const candidate = path11.join(dir, name);
26251
26372
  try {
26252
26373
  fs8.accessSync(candidate, fs8.constants.X_OK);
26253
26374
  return candidate;
@@ -26260,8 +26381,8 @@ var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "gla
26260
26381
 
26261
26382
  // packages/workit-core/src/core/repo-context.ts
26262
26383
  import { spawnSync as spawnSync4 } from "node:child_process";
26263
- import { existsSync as existsSync5, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
26264
- import path11 from "node:path";
26384
+ import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
26385
+ import path12 from "node:path";
26265
26386
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
26266
26387
  try {
26267
26388
  gitRevisionParts(value);
@@ -26395,7 +26516,7 @@ var findPrTemplate = (cwd) => {
26395
26516
  ]) {
26396
26517
  const parts = rel.split("/");
26397
26518
  const base = parts[parts.length - 1];
26398
- const dir = parts.length > 1 ? path11.join(cwd, ...parts.slice(0, -1)) : cwd;
26519
+ const dir = parts.length > 1 ? path12.join(cwd, ...parts.slice(0, -1)) : cwd;
26399
26520
  const actual = probeCaseInsensitive(dir, base);
26400
26521
  if (actual)
26401
26522
  return [...parts.slice(0, -1), actual].join("/");
@@ -26426,8 +26547,8 @@ var diffStatForRange = (cwd, range) => {
26426
26547
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
26427
26548
  };
26428
26549
  var packageScripts = (cwd, keys) => {
26429
- const pkgPath = path11.join(cwd, "package.json");
26430
- if (!existsSync5(pkgPath))
26550
+ const pkgPath = path12.join(cwd, "package.json");
26551
+ if (!existsSync6(pkgPath))
26431
26552
  return [];
26432
26553
  try {
26433
26554
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
@@ -26452,9 +26573,9 @@ var documentationFiles = (cwd) => {
26452
26573
  if (entry.isDirectory()) {
26453
26574
  if (excluded.has(entry.name))
26454
26575
  continue;
26455
- walk(path11.join(dir, entry.name), depth + 1);
26576
+ walk(path12.join(dir, entry.name), depth + 1);
26456
26577
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
26457
- matches.push("./" + path11.relative(cwd, path11.join(dir, entry.name)).replaceAll("\\", "/"));
26578
+ matches.push("./" + path12.relative(cwd, path12.join(dir, entry.name)).replaceAll("\\", "/"));
26458
26579
  }
26459
26580
  }
26460
26581
  };
@@ -26462,7 +26583,7 @@ var documentationFiles = (cwd) => {
26462
26583
  return matches.sort().slice(0, 200);
26463
26584
  };
26464
26585
  var readTrimmed = (file, maxLines) => {
26465
- if (!existsSync5(file))
26586
+ if (!existsSync6(file))
26466
26587
  return "";
26467
26588
  const lines = readFileSync6(file, "utf8").split(`
26468
26589
  `);
@@ -26470,7 +26591,7 @@ var readTrimmed = (file, maxLines) => {
26470
26591
  `);
26471
26592
  };
26472
26593
  function prReadyContext(root, range) {
26473
- const cwd = path11.resolve(repoRoot(root));
26594
+ const cwd = path12.resolve(repoRoot(root));
26474
26595
  if (range !== undefined && !isResolvableContextRange(cwd, range))
26475
26596
  return invalidRange(cwd);
26476
26597
  let stdout = "";
@@ -26528,7 +26649,7 @@ ${status.stderr}` : "");
26528
26649
  stdout += `template_path: ${template}
26529
26650
 
26530
26651
  `;
26531
- stdout += readTrimmed(path11.join(cwd, template), 220);
26652
+ stdout += readTrimmed(path12.join(cwd, template), 220);
26532
26653
  } else {
26533
26654
  stdout += `template_path: none
26534
26655
 
@@ -26538,7 +26659,7 @@ ${status.stderr}` : "");
26538
26659
  stdout += `
26539
26660
  `;
26540
26661
  stdout += printSection("Recent Validation Signals");
26541
- if (existsSync5(path11.join(cwd, "package.json"))) {
26662
+ if (existsSync6(path12.join(cwd, "package.json"))) {
26542
26663
  stdout += `package.json detected. Common scripts:
26543
26664
  `;
26544
26665
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -26547,7 +26668,7 @@ ${status.stderr}` : "");
26547
26668
  `) + `
26548
26669
  `;
26549
26670
  }
26550
- if (existsSync5(path11.join(cwd, "Cargo.toml")) || existsSync5(path11.join(cwd, "src-tauri/Cargo.toml"))) {
26671
+ if (existsSync6(path12.join(cwd, "Cargo.toml")) || existsSync6(path12.join(cwd, "src-tauri/Cargo.toml"))) {
26551
26672
  stdout += `Rust project detected.
26552
26673
  `;
26553
26674
  }
@@ -26580,7 +26701,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
26580
26701
  - Apply with the native workit_changelog_apply tool only (not hand-edits under Unreleased).
26581
26702
  - If Unreleased already has duplicate category headings, normalize_only first.`;
26582
26703
  function changelogContext(root, range) {
26583
- const cwd = path11.resolve(repoRoot(root));
26704
+ const cwd = path12.resolve(repoRoot(root));
26584
26705
  if (range !== undefined && !isResolvableContextRange(cwd, range))
26585
26706
  return invalidRange(cwd);
26586
26707
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -26598,8 +26719,8 @@ function changelogContext(root, range) {
26598
26719
  stdout += CHANGELOG_RULES + `
26599
26720
  `;
26600
26721
  stdout += printSection("Existing CHANGELOG.md");
26601
- const changelogPath = path11.join(cwd, "CHANGELOG.md");
26602
- if (existsSync5(changelogPath)) {
26722
+ const changelogPath = path12.join(cwd, "CHANGELOG.md");
26723
+ if (existsSync6(changelogPath)) {
26603
26724
  stdout += readTrimmed(changelogPath, 260) + `
26604
26725
  `;
26605
26726
  } else {
@@ -26618,7 +26739,7 @@ function changelogContext(root, range) {
26618
26739
  return { stdout, stderr: "", exitCode: 0, cwd };
26619
26740
  }
26620
26741
  function docsRefreshContext(root, range) {
26621
- const cwd = path11.resolve(repoRoot(root));
26742
+ const cwd = path12.resolve(repoRoot(root));
26622
26743
  if (range !== undefined && !isResolvableContextRange(cwd, range))
26623
26744
  return invalidRange(cwd);
26624
26745
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -26640,8 +26761,8 @@ function docsRefreshContext(root, range) {
26640
26761
  `) + `
26641
26762
  `;
26642
26763
  stdout += printSection("README Preview");
26643
- const readme = path11.join(cwd, "README.md");
26644
- if (existsSync5(readme)) {
26764
+ const readme = path12.join(cwd, "README.md");
26765
+ if (existsSync6(readme)) {
26645
26766
  stdout += readTrimmed(readme, 220) + `
26646
26767
  `;
26647
26768
  } else {
@@ -26649,8 +26770,8 @@ function docsRefreshContext(root, range) {
26649
26770
  `;
26650
26771
  }
26651
26772
  stdout += printSection("Package Scripts");
26652
- const pkgPath = path11.join(cwd, "package.json");
26653
- if (existsSync5(pkgPath)) {
26773
+ const pkgPath = path12.join(cwd, "package.json");
26774
+ if (existsSync6(pkgPath)) {
26654
26775
  try {
26655
26776
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
26656
26777
  stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
@@ -26663,7 +26784,7 @@ function docsRefreshContext(root, range) {
26663
26784
  return { stdout, stderr: "", exitCode: 0, cwd };
26664
26785
  }
26665
26786
  function releaseNotesContext(root, rangeOrTag) {
26666
- const cwd = path11.resolve(repoRoot(root));
26787
+ const cwd = path12.resolve(repoRoot(root));
26667
26788
  if (!rangeOrTag) {
26668
26789
  return { stdout: "", stderr: `ERROR: release tag or range required
26669
26790
  `, exitCode: 1, cwd };
@@ -26701,7 +26822,7 @@ function releaseNotesContext(root, rangeOrTag) {
26701
26822
  `;
26702
26823
  stdout += printSection("Existing Release Files");
26703
26824
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
26704
- if (existsSync5(path11.join(cwd, rel)))
26825
+ if (existsSync6(path12.join(cwd, rel)))
26705
26826
  stdout += rel + `
26706
26827
  `;
26707
26828
  }
@@ -26892,25 +27013,6 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
26892
27013
  import fs10 from "node:fs";
26893
27014
  import path13 from "node:path";
26894
27015
 
26895
- // packages/workit-core/src/core/package-root.ts
26896
- import { existsSync as existsSync6 } from "node:fs";
26897
- import path12 from "node:path";
26898
- import { fileURLToPath } from "node:url";
26899
- var packageRoot = () => {
26900
- let dir = path12.dirname(fileURLToPath(import.meta.url));
26901
- while (true) {
26902
- const parent = path12.dirname(dir);
26903
- if (existsSync6(path12.join(dir, "package.json")) || parent === dir)
26904
- return dir;
26905
- dir = parent;
26906
- }
26907
- };
26908
- var assetRoot = () => {
26909
- const root = packageRoot();
26910
- const assets = path12.join(root, "assets");
26911
- return existsSync6(assets) ? assets : root;
26912
- };
26913
-
26914
27016
  // packages/workit-core/src/core/templates.ts
26915
27017
  var repoRoot2 = assetRoot();
26916
27018
 
@@ -27414,6 +27516,32 @@ var toolInputSchema = (family) => {
27414
27516
  const schema = boundedOperationJsonSchema(family);
27415
27517
  return { type: "object", ...schema };
27416
27518
  };
27519
+ var schemaBranches = (schema) => Array.isArray(schema.oneOf) ? schema.oneOf : Array.isArray(schema.anyOf) ? schema.anyOf : [schema];
27520
+ var readOnlyBranches = (family) => schemaBranches(boundedOperationJsonSchema(family)).filter((branch) => {
27521
+ const action = branch.properties?.action?.const;
27522
+ return typeof action === "string" && READ_ONLY_ACTIONS.has(action);
27523
+ });
27524
+ var advertisedToolSchemas = (attested) => {
27525
+ if (attested)
27526
+ return OPERATION_FAMILIES.map((family) => ({
27527
+ name: `workit_${family}`,
27528
+ description: operationDescription(family),
27529
+ inputSchema: toolInputSchema(family)
27530
+ }));
27531
+ return OPERATION_FAMILIES.flatMap((family) => {
27532
+ const branches = readOnlyBranches(family);
27533
+ if (!branches.length)
27534
+ return [];
27535
+ const schema = boundedOperationJsonSchema(family);
27536
+ return [
27537
+ {
27538
+ name: `workit_${family}`,
27539
+ description: `${operationDescription(family)} Unattested MCP callers can only run read-only actions; mutating operations require an attested host session.`,
27540
+ inputSchema: { type: "object", ...schema, oneOf: branches }
27541
+ }
27542
+ ];
27543
+ });
27544
+ };
27417
27545
  var sanitizeFailure = (result, workspaceRoot) => {
27418
27546
  if (result.ok)
27419
27547
  return result;
@@ -27468,13 +27596,16 @@ function createMcpServer(host, contextProvider) {
27468
27596
  ].filter((file) => existsSync7(file)));
27469
27597
  let staleWarned = false;
27470
27598
  const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
27471
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
27472
- tools: OPERATION_FAMILIES.map((family) => ({
27473
- name: `workit_${family}`,
27474
- description: operationDescription(family),
27475
- inputSchema: toolInputSchema(family)
27476
- }))
27477
- }));
27599
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
27600
+ let attested = false;
27601
+ try {
27602
+ const context = await contextProvider.current();
27603
+ attested = context.callerAttested === true;
27604
+ } catch {
27605
+ attested = false;
27606
+ }
27607
+ return { tools: advertisedToolSchemas(attested) };
27608
+ });
27478
27609
  server.setRequestHandler(ListResourcesRequestSchema, async () => ({
27479
27610
  resources: CONTEXT_KINDS.map((kind) => ({
27480
27611
  uri: `workit://context/${kind}`,
@@ -27589,7 +27720,7 @@ function createMcpServer(host, contextProvider) {
27589
27720
  const parsed = parseOperation(family, request.params.arguments);
27590
27721
  if (!parsed.ok)
27591
27722
  return resultForClient(parsed, workspaceRoot);
27592
- if (context.callerAttested === false && requiresCallerIdentity(parsed.data))
27723
+ if (context.callerAttested !== true && requiresCallerIdentity(parsed.data))
27593
27724
  return resultForClient({
27594
27725
  ok: false,
27595
27726
  schemaVersion: 1,