@brainervirus/workit-cursor 8.9.0 → 8.9.1

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.
@@ -2,7 +2,7 @@
2
2
  "name": "workit",
3
3
  "displayName": "Workit",
4
4
  "description": "Adaptive Workit workflow with shared MCP tools and native Cursor hooks",
5
- "version": "8.9.0",
5
+ "version": "8.9.1",
6
6
  "author": {
7
7
  "name": "Cristhofer Pincetti"
8
8
  },
@@ -13109,6 +13109,66 @@ class TaskStore {
13109
13109
  return bound;
13110
13110
  return success(null, null, { key: key.data, bound: bound.data });
13111
13111
  }
13112
+ taskForWorker(workerId) {
13113
+ if (typeof workerId !== "string" || !validId(workerId))
13114
+ return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
13115
+ const listed = this.listTaskIndex();
13116
+ if (!listed.ok)
13117
+ return listed;
13118
+ for (const entry of listed.data) {
13119
+ if (entry.status === "closed")
13120
+ continue;
13121
+ const loaded = this.load(entry.id);
13122
+ if (!loaded.ok)
13123
+ return loaded;
13124
+ if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
13125
+ return success(null, null, entry.id);
13126
+ }
13127
+ return success(null, null, null);
13128
+ }
13129
+ originTaskEntry() {
13130
+ const key = this.currentKey();
13131
+ const location = this.location();
13132
+ const branch = key.ok ? key.data.branch : null;
13133
+ if (!branch || !location.ok)
13134
+ return success(null, null, null);
13135
+ const common = path13.dirname(location.data.dir);
13136
+ const heads = path13.join(common, "logs", "refs", "heads");
13137
+ const headLog = location.data.git ? path13.join(location.data.git.gitDir, "logs", "HEAD") : "";
13138
+ const signatures = [
13139
+ path13.join(heads, branch),
13140
+ headLog,
13141
+ heads,
13142
+ path13.join(common, "packed-refs")
13143
+ ].map((file) => fileSignature(file) ?? "-");
13144
+ const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
13145
+ let resolved = origins.get(slot);
13146
+ if (resolved?.signature !== signatures.join("|")) {
13147
+ const renames = branchRenames(location.data);
13148
+ const aliases = [branch];
13149
+ for (const rename of renames.toReversed())
13150
+ if (rename.to === aliases.at(-1))
13151
+ aliases.push(rename.from);
13152
+ const start = branchStart(location.data, branch, aliases);
13153
+ const slash = start?.indexOf("/") ?? -1;
13154
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path13.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
13155
+ const name = start && (remote ? start.slice(slash + 1) : start);
13156
+ resolved = {
13157
+ signature: signatures.join("|"),
13158
+ name: name && !aliases.includes(name) ? name : null,
13159
+ renames
13160
+ };
13161
+ origins.set(slot, resolved);
13162
+ }
13163
+ const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
13164
+ if (entry && !entry.ok)
13165
+ return entry;
13166
+ return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
13167
+ }
13168
+ readStoreTask(taskId) {
13169
+ const loaded = this.load(taskId);
13170
+ return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
13171
+ }
13112
13172
  create(input, provenance, expectedWorkspaceRevision) {
13113
13173
  const value = "intent" in input ? input : {
13114
13174
  intent: input,
@@ -13778,12 +13838,12 @@ class TaskStore {
13778
13838
  return 0;
13779
13839
  }
13780
13840
  }
13781
- openTasksForKey(key) {
13841
+ openTasksForKey(key, known) {
13782
13842
  const all = this.listStoreIndex();
13783
13843
  if (!all.ok)
13784
13844
  return all;
13785
- const location = this.location();
13786
- const renames = location.ok ? branchRenames(location.data) : [];
13845
+ const location = known ? null : this.location();
13846
+ const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
13787
13847
  return success(null, null, all.data.filter((entry) => entry.key !== null && entry.status !== "closed").filter((entry) => (renames.length ? followRenames(entry.key, entry.boundAt ?? entry.createdAt, renames) : entry.key) === key).toSorted(oldestFirst));
13788
13848
  }
13789
13849
  openTaskForKey(key) {
@@ -14470,7 +14530,34 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
14470
14530
  return left > right;
14471
14531
  }
14472
14532
  return false;
14473
- }, 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), sameDirectoryIdentity = (left, right) => {
14533
+ }, 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), readText = (file) => {
14534
+ try {
14535
+ return fs10.readFileSync(file, "utf8");
14536
+ } catch {
14537
+ return "";
14538
+ }
14539
+ }, REFLOG_LINE, branchStart = (location, branch, aliases) => {
14540
+ const first = readText(path13.join(path13.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
14541
+ `, 1)[0];
14542
+ const created = REFLOG_LINE.exec(first);
14543
+ const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
14544
+ if (!created || !start)
14545
+ return null;
14546
+ if (start !== "HEAD")
14547
+ return start;
14548
+ if (!location.git)
14549
+ return null;
14550
+ for (const line of readText(path13.join(location.git.gitDir, "logs", "HEAD")).split(`
14551
+ `)) {
14552
+ const entry = REFLOG_LINE.exec(line);
14553
+ const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
14554
+ const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
14555
+ if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
14556
+ return moved[1];
14557
+ }
14558
+ return null;
14559
+ }, refExists = (common, ref) => fs10.existsSync(path13.join(common, ref)) || readText(path13.join(common, "packed-refs")).includes(` ${ref}
14560
+ `), origins, sameDirectoryIdentity = (left, right) => {
14474
14561
  if (!path13.isAbsolute(left) || !path13.isAbsolute(right))
14475
14562
  return false;
14476
14563
  const normalizedLeft = path13.resolve(left);
@@ -14518,6 +14605,8 @@ var init_task_store = __esm(() => {
14518
14605
  AUTO_COMPACT_BYTES = 2 * 1024 * 1024;
14519
14606
  TOMBSTONE = Symbol("tombstone");
14520
14607
  STALE_TEMPORARY_MS = 60 * 60000;
14608
+ REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
14609
+ origins = new Map;
14521
14610
  heldInProcess = new Map;
14522
14611
  });
14523
14612
 
@@ -15507,38 +15596,29 @@ function reconcileResume(view, observations = []) {
15507
15596
  reassessmentRequired: view.task.policy?.policyVersion !== POLICY_VERSION
15508
15597
  });
15509
15598
  }
15510
- function compactTaskContext(view) {
15511
- const decisions = view.task.decisions.slice().toSorted((left, right) => {
15512
- const leftAt = compactTimestamp(left.recordedAt);
15513
- const rightAt = compactTimestamp(right.recordedAt);
15514
- if (leftAt !== rightAt)
15515
- return leftAt > rightAt ? -1 : 1;
15516
- return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
15517
- }).slice(0, COMPACT_MAX_ITEMS).map((entry) => ({
15518
- id: entry.id,
15519
- purpose: entry.data.purpose,
15520
- status: entry.data.response,
15521
- digest: entry.data.digest,
15522
- choice: compactDecisionChoice(entry) || `task.inspect taskId=${view.task.id} view=full; decision=${entry.id}`,
15523
- references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
15524
- }));
15525
- const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
15599
+ function compactTaskContext(view, inherited = null) {
15600
+ const decisions = compactDecisions([
15601
+ view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
15602
+ inherited
15603
+ ]);
15604
+ const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
15526
15605
  id: method.id,
15527
15606
  assurance: method.assurance,
15528
15607
  reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
15529
15608
  })) : [];
15609
+ const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
15530
15610
  const gaps = Array.from(new Set([
15531
- ...view.evidence.filter((entry) => entry.status === "stale").map((entry) => `stale evidence: ${entry.evidenceId}`),
15532
- ...view.requirements.filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
15533
- ...view.task.progress.blockers.map((entry) => entry.reason)
15611
+ ...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
15612
+ ...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
15613
+ ...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
15534
15614
  ])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
15535
15615
  const context = {
15536
- objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
15537
- status: view.task.status,
15616
+ objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
15617
+ status: view?.task.status ?? null,
15538
15618
  decisions,
15539
15619
  methods,
15540
15620
  gaps,
15541
- nextAction: compactText(view.task.progress.nextAction, COMPACT_TEXT_BYTES)
15621
+ nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
15542
15622
  };
15543
15623
  let encoded = JSON.stringify(context);
15544
15624
  while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
@@ -15581,7 +15661,21 @@ var COMPACT_MAX_BYTES = 4096, COMPACT_MAX_ITEMS = 8, COMPACT_TEXT_BYTES = 128, s
15581
15661
  id: ref.id
15582
15662
  };
15583
15663
  return { kind: ref.kind };
15584
- }, exportDigest = (bundle) => sha256({
15664
+ }, newestDecisionFirst = (left, right) => {
15665
+ const leftAt = compactTimestamp(left.recordedAt);
15666
+ const rightAt = compactTimestamp(right.recordedAt);
15667
+ if (leftAt !== rightAt)
15668
+ return leftAt > rightAt ? -1 : 1;
15669
+ return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
15670
+ }, compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
15671
+ id: entry.id,
15672
+ purpose: entry.data.purpose,
15673
+ status: entry.data.response,
15674
+ digest: entry.data.digest,
15675
+ choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
15676
+ references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
15677
+ origin: source.origin ?? undefined
15678
+ })) : []).slice(0, COMPACT_MAX_ITEMS), exportDigest = (bundle) => sha256({
15585
15679
  schemaVersion: bundle.schemaVersion,
15586
15680
  exportedAt: bundle.exportedAt,
15587
15681
  sourceWorkspaceId: bundle.sourceWorkspaceId,
@@ -15644,6 +15738,17 @@ class WorkitCore {
15644
15738
  const root = this.contextRootError();
15645
15739
  if (!root.ok)
15646
15740
  return root;
15741
+ const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
15742
+ const workerId = addressed ? value.workerId : this.context.workerId ?? null;
15743
+ if (workerId !== null) {
15744
+ const owner = this.store.taskForWorker(workerId);
15745
+ if (!owner.ok)
15746
+ return owner;
15747
+ if (owner.data)
15748
+ return success(null, null, { ...request, taskId: owner.data });
15749
+ if (addressed)
15750
+ return failure2("not_found", `worker ${workerId} is on no open task; find its task with \`workit task status --all\`, then run \`${workerCommand("<task-id>", request)}\``);
15751
+ }
15647
15752
  const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
15648
15753
  const found = this.store.implicitTask({
15649
15754
  provenance: provenance(this.context),
@@ -16350,7 +16455,9 @@ class WorkitCore {
16350
16455
  if (!resolved.ok)
16351
16456
  return resolved;
16352
16457
  const value = resolved.data;
16353
- return this.retryOmittedRevisions(value, () => this.workerOnce(value));
16458
+ const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
16459
+ const { action, taskId } = value ?? {};
16460
+ return result.ok || request?.taskId !== undefined || typeof taskId !== "string" || !WORKER_ADDRESSED_ACTIONS.has(`worker.${String(action)}`) ? result : { ...result, error: `${result.error}; run \`${workerCommand(taskId, value)}\`` };
16354
16461
  }
16355
16462
  workerOnce(request) {
16356
16463
  const root = this.contextRootError();
@@ -16555,10 +16662,18 @@ class WorkitCore {
16555
16662
  requirements
16556
16663
  });
16557
16664
  }
16558
- compactContext(taskId, freshness) {
16665
+ compactContext(taskId, freshness, origin = null) {
16559
16666
  const root = this.contextRootError();
16560
16667
  if (!root.ok)
16561
16668
  return root;
16669
+ const base = origin && this.store.readStoreTask(origin.taskId);
16670
+ const inherited = base?.ok && base.data ? {
16671
+ origin: origin.origin,
16672
+ taskId: base.data.id,
16673
+ decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
16674
+ } : null;
16675
+ if (taskId === null)
16676
+ return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
16562
16677
  const task = this.store.readTask(taskId);
16563
16678
  if (!task.ok)
16564
16679
  return task;
@@ -16568,7 +16683,7 @@ class WorkitCore {
16568
16683
  const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
16569
16684
  if (!view.ok)
16570
16685
  return view;
16571
- return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
16686
+ return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
16572
16687
  }
16573
16688
  view(task, capture = true, freshness = treeFreshness(this.store.root)) {
16574
16689
  const workspace = this.store.readWorkspace();
@@ -16979,7 +17094,12 @@ var MAX_JUDGED_REFS = 5, workspaceVerification = (root) => {
16979
17094
  return changed;
16980
17095
  const updated = changed.data.task.workers.find((candidate) => candidate.id === input.workerId);
16981
17096
  return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during lifecycle observation");
16982
- }, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || scopeCovers(scope, { description: "", paths: [ref.path], exclusions: [] })), TASK_SCOPED_ACTIONS, CREATING_ACTIONS, REVISION_RETRY_ATTEMPTS = 8, retryPause, filledRevisions = (request) => {
17097
+ }, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || scopeCovers(scope, { description: "", paths: [ref.path], exclusions: [] })), TASK_SCOPED_ACTIONS, WORKER_ADDRESSED_ACTIONS, workerCommand = (taskId, request) => {
17098
+ const { action, workerId, reason, report } = request;
17099
+ const rest = JSON.stringify({ reason, report });
17100
+ const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
17101
+ return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
17102
+ }, CREATING_ACTIONS, REVISION_RETRY_ATTEMPTS = 8, retryPause, filledRevisions = (request) => {
16983
17103
  const value = typeof request === "object" && request !== null ? request : {};
16984
17104
  return {
16985
17105
  task: value.expectedRevision === undefined,
@@ -17201,6 +17321,7 @@ var init_task_engine = __esm(() => {
17201
17321
  "worker.cancel",
17202
17322
  "state.export"
17203
17323
  ]);
17324
+ WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
17204
17325
  CREATING_ACTIONS = new Set([
17205
17326
  "task.revise",
17206
17327
  "task.progress",
@@ -17248,18 +17369,23 @@ function sessionCompactContext(store, session, context, selection = "session-bou
17248
17369
  if (!listed.ok)
17249
17370
  return migrationPending(listed) ? MIGRATION_PENDING_NOTE : null;
17250
17371
  const entry = currentTaskEntry(listed.data, session, selection) ?? implicitTaskEntry(store, listed.data, session);
17251
- return entry ? entryCompactContext(store, entry, session, context) : null;
17372
+ return entryCompactContext(store, entry, session, context);
17252
17373
  }
17253
17374
  function entryCompactContext(store, entry, session, context) {
17375
+ const found = store.originTaskEntry();
17376
+ const origin = found.ok && found.data && found.data.entry.id !== entry?.id && !otherConversation(found.data.entry, session) ? found.data : null;
17377
+ if (!entry && !origin)
17378
+ return null;
17254
17379
  const workspace = store.readWorkspace();
17255
- if (!workspace.ok || !workspace.data)
17380
+ if (!workspace.ok || entry && !workspace.data)
17256
17381
  return null;
17257
17382
  const slot = `${store.root}\x00${session.host}\x00${session.handle}`;
17258
17383
  const key = canonicalJson({
17259
- task: entry.id,
17260
- revision: entry.revision,
17261
- file: entry.file,
17262
- workspace: workspace.data.revision,
17384
+ task: entry?.id ?? null,
17385
+ revision: entry?.revision ?? null,
17386
+ file: entry?.file ?? null,
17387
+ origin: origin && origin.entry.file,
17388
+ workspace: workspace.data?.revision ?? null,
17263
17389
  caller: context.caller,
17264
17390
  workerId: context.workerId ?? null,
17265
17391
  capabilities: context.capabilities
@@ -17267,15 +17393,15 @@ function entryCompactContext(store, entry, session, context) {
17267
17393
  const hit = cache.get(slot);
17268
17394
  if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
17269
17395
  return hit.value;
17270
- const task = store.readTask(entry.id);
17271
- if (!task.ok)
17396
+ const task = entry ? store.readTask(entry.id) : null;
17397
+ if (task && !task.ok)
17272
17398
  return null;
17273
17399
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
17274
- const observed = task.data.evidence.some(isObservedCheck);
17275
- const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path16.resolve(store.root, ref.path)] : []));
17400
+ const observed = task?.data.evidence.some(isObservedCheck) ?? false;
17401
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path16.resolve(store.root, ref.path)] : []));
17276
17402
  const observedFiles = filesKey(files);
17277
- const compact = new WorkitCore(store, context).compactContext(entry.id, freshness);
17278
- if (!compact.ok)
17403
+ const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
17404
+ if (!compact.ok || !compact.data)
17279
17405
  return null;
17280
17406
  cache.delete(slot);
17281
17407
  cache.set(slot, {
@@ -17294,8 +17420,8 @@ var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task stat
17294
17420
  return sessionTaskEntry(entries, session);
17295
17421
  const active = entries.filter((entry) => entry.status === "active");
17296
17422
  return active.length === 1 ? active[0] : null;
17297
- }, implicitTaskEntry = (store, entries, session) => {
17298
- const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle)));
17423
+ }, otherConversation = (entry, session) => entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle)), implicitTaskEntry = (store, entries, session) => {
17424
+ const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !otherConversation(entry, session));
17299
17425
  if (open.length === 0)
17300
17426
  return null;
17301
17427
  const key = store.currentKey();
@@ -17346,7 +17472,7 @@ ${options.addendum}` : ""}
17346
17472
  if (!listed.ok)
17347
17473
  throw new Error(listed.error);
17348
17474
  const entry = currentTaskEntry(listed.data, session, descriptor.context.task) ?? implicitTaskEntry(store, listed.data, session);
17349
- const text = entry ? entryCompactContext(store, entry, session, hookOperationContext(input, descriptor)) : null;
17475
+ const text = entryCompactContext(store, entry, session, hookOperationContext(input, descriptor));
17350
17476
  if (text)
17351
17477
  compact = `
17352
17478
  <workit-task-context>${text}</workit-task-context>`;
@@ -19409,6 +19409,38 @@ var isNewerVersion = (candidate, current) => {
19409
19409
  return false;
19410
19410
  };
19411
19411
  var 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);
19412
+ var readText = (file) => {
19413
+ try {
19414
+ return fs7.readFileSync(file, "utf8");
19415
+ } catch {
19416
+ return "";
19417
+ }
19418
+ };
19419
+ var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
19420
+ var branchStart = (location, branch, aliases) => {
19421
+ const first = readText(path6.join(path6.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
19422
+ `, 1)[0];
19423
+ const created = REFLOG_LINE.exec(first);
19424
+ const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
19425
+ if (!created || !start)
19426
+ return null;
19427
+ if (start !== "HEAD")
19428
+ return start;
19429
+ if (!location.git)
19430
+ return null;
19431
+ for (const line of readText(path6.join(location.git.gitDir, "logs", "HEAD")).split(`
19432
+ `)) {
19433
+ const entry = REFLOG_LINE.exec(line);
19434
+ const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
19435
+ const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
19436
+ if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
19437
+ return moved[1];
19438
+ }
19439
+ return null;
19440
+ };
19441
+ var refExists = (common, ref) => fs7.existsSync(path6.join(common, ref)) || readText(path6.join(common, "packed-refs")).includes(` ${ref}
19442
+ `);
19443
+ var origins = new Map;
19412
19444
  var sameDirectoryIdentity = (left, right) => {
19413
19445
  if (!path6.isAbsolute(left) || !path6.isAbsolute(right))
19414
19446
  return false;
@@ -19661,6 +19693,66 @@ class TaskStore {
19661
19693
  return bound;
19662
19694
  return success(null, null, { key: key.data, bound: bound.data });
19663
19695
  }
19696
+ taskForWorker(workerId) {
19697
+ if (typeof workerId !== "string" || !validId(workerId))
19698
+ return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
19699
+ const listed = this.listTaskIndex();
19700
+ if (!listed.ok)
19701
+ return listed;
19702
+ for (const entry of listed.data) {
19703
+ if (entry.status === "closed")
19704
+ continue;
19705
+ const loaded = this.load(entry.id);
19706
+ if (!loaded.ok)
19707
+ return loaded;
19708
+ if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
19709
+ return success(null, null, entry.id);
19710
+ }
19711
+ return success(null, null, null);
19712
+ }
19713
+ originTaskEntry() {
19714
+ const key = this.currentKey();
19715
+ const location = this.location();
19716
+ const branch = key.ok ? key.data.branch : null;
19717
+ if (!branch || !location.ok)
19718
+ return success(null, null, null);
19719
+ const common = path6.dirname(location.data.dir);
19720
+ const heads = path6.join(common, "logs", "refs", "heads");
19721
+ const headLog = location.data.git ? path6.join(location.data.git.gitDir, "logs", "HEAD") : "";
19722
+ const signatures = [
19723
+ path6.join(heads, branch),
19724
+ headLog,
19725
+ heads,
19726
+ path6.join(common, "packed-refs")
19727
+ ].map((file) => fileSignature(file) ?? "-");
19728
+ const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
19729
+ let resolved = origins.get(slot);
19730
+ if (resolved?.signature !== signatures.join("|")) {
19731
+ const renames = branchRenames(location.data);
19732
+ const aliases = [branch];
19733
+ for (const rename of renames.toReversed())
19734
+ if (rename.to === aliases.at(-1))
19735
+ aliases.push(rename.from);
19736
+ const start = branchStart(location.data, branch, aliases);
19737
+ const slash = start?.indexOf("/") ?? -1;
19738
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path6.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
19739
+ const name = start && (remote ? start.slice(slash + 1) : start);
19740
+ resolved = {
19741
+ signature: signatures.join("|"),
19742
+ name: name && !aliases.includes(name) ? name : null,
19743
+ renames
19744
+ };
19745
+ origins.set(slot, resolved);
19746
+ }
19747
+ const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
19748
+ if (entry && !entry.ok)
19749
+ return entry;
19750
+ return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
19751
+ }
19752
+ readStoreTask(taskId) {
19753
+ const loaded = this.load(taskId);
19754
+ return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
19755
+ }
19664
19756
  create(input, provenance, expectedWorkspaceRevision) {
19665
19757
  const value = "intent" in input ? input : {
19666
19758
  intent: input,
@@ -20330,12 +20422,12 @@ class TaskStore {
20330
20422
  return 0;
20331
20423
  }
20332
20424
  }
20333
- openTasksForKey(key) {
20425
+ openTasksForKey(key, known) {
20334
20426
  const all = this.listStoreIndex();
20335
20427
  if (!all.ok)
20336
20428
  return all;
20337
- const location = this.location();
20338
- const renames = location.ok ? branchRenames(location.data) : [];
20429
+ const location = known ? null : this.location();
20430
+ const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
20339
20431
  return success(null, null, all.data.filter((entry) => entry.key !== null && entry.status !== "closed").filter((entry) => (renames.length ? followRenames(entry.key, entry.boundAt ?? entry.createdAt, renames) : entry.key) === key).toSorted(oldestFirst));
20340
20432
  }
20341
20433
  openTaskForKey(key) {
@@ -24421,38 +24513,45 @@ var compactReference = (ref) => {
24421
24513
  };
24422
24514
  return { kind: ref.kind };
24423
24515
  };
24424
- function compactTaskContext(view) {
24425
- const decisions = view.task.decisions.slice().toSorted((left, right) => {
24426
- const leftAt = compactTimestamp(left.recordedAt);
24427
- const rightAt = compactTimestamp(right.recordedAt);
24428
- if (leftAt !== rightAt)
24429
- return leftAt > rightAt ? -1 : 1;
24430
- return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
24431
- }).slice(0, COMPACT_MAX_ITEMS).map((entry) => ({
24432
- id: entry.id,
24433
- purpose: entry.data.purpose,
24434
- status: entry.data.response,
24435
- digest: entry.data.digest,
24436
- choice: compactDecisionChoice(entry) || `task.inspect taskId=${view.task.id} view=full; decision=${entry.id}`,
24437
- references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
24438
- }));
24439
- const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
24516
+ var newestDecisionFirst = (left, right) => {
24517
+ const leftAt = compactTimestamp(left.recordedAt);
24518
+ const rightAt = compactTimestamp(right.recordedAt);
24519
+ if (leftAt !== rightAt)
24520
+ return leftAt > rightAt ? -1 : 1;
24521
+ return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
24522
+ };
24523
+ var compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
24524
+ id: entry.id,
24525
+ purpose: entry.data.purpose,
24526
+ status: entry.data.response,
24527
+ digest: entry.data.digest,
24528
+ choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
24529
+ references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
24530
+ origin: source.origin ?? undefined
24531
+ })) : []).slice(0, COMPACT_MAX_ITEMS);
24532
+ function compactTaskContext(view, inherited = null) {
24533
+ const decisions = compactDecisions([
24534
+ view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
24535
+ inherited
24536
+ ]);
24537
+ const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
24440
24538
  id: method.id,
24441
24539
  assurance: method.assurance,
24442
24540
  reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
24443
24541
  })) : [];
24542
+ const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
24444
24543
  const gaps = Array.from(new Set([
24445
- ...view.evidence.filter((entry) => entry.status === "stale").map((entry) => `stale evidence: ${entry.evidenceId}`),
24446
- ...view.requirements.filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
24447
- ...view.task.progress.blockers.map((entry) => entry.reason)
24544
+ ...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
24545
+ ...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
24546
+ ...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
24448
24547
  ])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
24449
24548
  const context = {
24450
- objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
24451
- status: view.task.status,
24549
+ objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
24550
+ status: view?.task.status ?? null,
24452
24551
  decisions,
24453
24552
  methods,
24454
24553
  gaps,
24455
- nextAction: compactText(view.task.progress.nextAction, COMPACT_TEXT_BYTES)
24554
+ nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
24456
24555
  };
24457
24556
  let encoded = JSON.stringify(context);
24458
24557
  while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
@@ -25057,6 +25156,13 @@ var TASK_SCOPED_ACTIONS = new Set([
25057
25156
  "worker.cancel",
25058
25157
  "state.export"
25059
25158
  ]);
25159
+ var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
25160
+ var workerCommand = (taskId, request) => {
25161
+ const { action, workerId, reason, report } = request;
25162
+ const rest = JSON.stringify({ reason, report });
25163
+ const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
25164
+ return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
25165
+ };
25060
25166
  var CREATING_ACTIONS = new Set([
25061
25167
  "task.revise",
25062
25168
  "task.progress",
@@ -25307,6 +25413,17 @@ class WorkitCore {
25307
25413
  const root = this.contextRootError();
25308
25414
  if (!root.ok)
25309
25415
  return root;
25416
+ const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
25417
+ const workerId = addressed ? value.workerId : this.context.workerId ?? null;
25418
+ if (workerId !== null) {
25419
+ const owner = this.store.taskForWorker(workerId);
25420
+ if (!owner.ok)
25421
+ return owner;
25422
+ if (owner.data)
25423
+ return success(null, null, { ...request, taskId: owner.data });
25424
+ if (addressed)
25425
+ return failure2("not_found", `worker ${workerId} is on no open task; find its task with \`workit task status --all\`, then run \`${workerCommand("<task-id>", request)}\``);
25426
+ }
25310
25427
  const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
25311
25428
  const found = this.store.implicitTask({
25312
25429
  provenance: provenance(this.context),
@@ -26013,7 +26130,9 @@ class WorkitCore {
26013
26130
  if (!resolved.ok)
26014
26131
  return resolved;
26015
26132
  const value = resolved.data;
26016
- return this.retryOmittedRevisions(value, () => this.workerOnce(value));
26133
+ const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
26134
+ const { action, taskId } = value ?? {};
26135
+ return result.ok || request?.taskId !== undefined || typeof taskId !== "string" || !WORKER_ADDRESSED_ACTIONS.has(`worker.${String(action)}`) ? result : { ...result, error: `${result.error}; run \`${workerCommand(taskId, value)}\`` };
26017
26136
  }
26018
26137
  workerOnce(request) {
26019
26138
  const root = this.contextRootError();
@@ -26218,10 +26337,18 @@ class WorkitCore {
26218
26337
  requirements
26219
26338
  });
26220
26339
  }
26221
- compactContext(taskId, freshness) {
26340
+ compactContext(taskId, freshness, origin = null) {
26222
26341
  const root = this.contextRootError();
26223
26342
  if (!root.ok)
26224
26343
  return root;
26344
+ const base = origin && this.store.readStoreTask(origin.taskId);
26345
+ const inherited = base?.ok && base.data ? {
26346
+ origin: origin.origin,
26347
+ taskId: base.data.id,
26348
+ decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
26349
+ } : null;
26350
+ if (taskId === null)
26351
+ return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
26225
26352
  const task = this.store.readTask(taskId);
26226
26353
  if (!task.ok)
26227
26354
  return task;
@@ -26231,7 +26358,7 @@ class WorkitCore {
26231
26358
  const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
26232
26359
  if (!view.ok)
26233
26360
  return view;
26234
- return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
26361
+ return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
26235
26362
  }
26236
26363
  view(task, capture = true, freshness = treeFreshness(this.store.root)) {
26237
26364
  const workspace = this.store.readWorkspace();
@@ -12972,6 +12972,38 @@ var isNewerVersion = (candidate, current) => {
12972
12972
  return false;
12973
12973
  };
12974
12974
  var 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);
12975
+ var readText = (file) => {
12976
+ try {
12977
+ return fs10.readFileSync(file, "utf8");
12978
+ } catch {
12979
+ return "";
12980
+ }
12981
+ };
12982
+ var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
12983
+ var branchStart = (location, branch, aliases) => {
12984
+ const first = readText(path13.join(path13.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
12985
+ `, 1)[0];
12986
+ const created = REFLOG_LINE.exec(first);
12987
+ const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
12988
+ if (!created || !start)
12989
+ return null;
12990
+ if (start !== "HEAD")
12991
+ return start;
12992
+ if (!location.git)
12993
+ return null;
12994
+ for (const line of readText(path13.join(location.git.gitDir, "logs", "HEAD")).split(`
12995
+ `)) {
12996
+ const entry = REFLOG_LINE.exec(line);
12997
+ const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
12998
+ const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
12999
+ if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
13000
+ return moved[1];
13001
+ }
13002
+ return null;
13003
+ };
13004
+ var refExists = (common, ref) => fs10.existsSync(path13.join(common, ref)) || readText(path13.join(common, "packed-refs")).includes(` ${ref}
13005
+ `);
13006
+ var origins = new Map;
12975
13007
  var sameDirectoryIdentity = (left, right) => {
12976
13008
  if (!path13.isAbsolute(left) || !path13.isAbsolute(right))
12977
13009
  return false;
@@ -13224,6 +13256,66 @@ class TaskStore {
13224
13256
  return bound;
13225
13257
  return success(null, null, { key: key.data, bound: bound.data });
13226
13258
  }
13259
+ taskForWorker(workerId) {
13260
+ if (typeof workerId !== "string" || !validId(workerId))
13261
+ return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
13262
+ const listed = this.listTaskIndex();
13263
+ if (!listed.ok)
13264
+ return listed;
13265
+ for (const entry of listed.data) {
13266
+ if (entry.status === "closed")
13267
+ continue;
13268
+ const loaded = this.load(entry.id);
13269
+ if (!loaded.ok)
13270
+ return loaded;
13271
+ if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
13272
+ return success(null, null, entry.id);
13273
+ }
13274
+ return success(null, null, null);
13275
+ }
13276
+ originTaskEntry() {
13277
+ const key = this.currentKey();
13278
+ const location = this.location();
13279
+ const branch = key.ok ? key.data.branch : null;
13280
+ if (!branch || !location.ok)
13281
+ return success(null, null, null);
13282
+ const common = path13.dirname(location.data.dir);
13283
+ const heads = path13.join(common, "logs", "refs", "heads");
13284
+ const headLog = location.data.git ? path13.join(location.data.git.gitDir, "logs", "HEAD") : "";
13285
+ const signatures = [
13286
+ path13.join(heads, branch),
13287
+ headLog,
13288
+ heads,
13289
+ path13.join(common, "packed-refs")
13290
+ ].map((file) => fileSignature(file) ?? "-");
13291
+ const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
13292
+ let resolved = origins.get(slot);
13293
+ if (resolved?.signature !== signatures.join("|")) {
13294
+ const renames = branchRenames(location.data);
13295
+ const aliases = [branch];
13296
+ for (const rename of renames.toReversed())
13297
+ if (rename.to === aliases.at(-1))
13298
+ aliases.push(rename.from);
13299
+ const start = branchStart(location.data, branch, aliases);
13300
+ const slash = start?.indexOf("/") ?? -1;
13301
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path13.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
13302
+ const name = start && (remote ? start.slice(slash + 1) : start);
13303
+ resolved = {
13304
+ signature: signatures.join("|"),
13305
+ name: name && !aliases.includes(name) ? name : null,
13306
+ renames
13307
+ };
13308
+ origins.set(slot, resolved);
13309
+ }
13310
+ const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
13311
+ if (entry && !entry.ok)
13312
+ return entry;
13313
+ return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
13314
+ }
13315
+ readStoreTask(taskId) {
13316
+ const loaded = this.load(taskId);
13317
+ return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
13318
+ }
13227
13319
  create(input, provenance, expectedWorkspaceRevision) {
13228
13320
  const value = "intent" in input ? input : {
13229
13321
  intent: input,
@@ -13893,12 +13985,12 @@ class TaskStore {
13893
13985
  return 0;
13894
13986
  }
13895
13987
  }
13896
- openTasksForKey(key) {
13988
+ openTasksForKey(key, known) {
13897
13989
  const all = this.listStoreIndex();
13898
13990
  if (!all.ok)
13899
13991
  return all;
13900
- const location = this.location();
13901
- const renames = location.ok ? branchRenames(location.data) : [];
13992
+ const location = known ? null : this.location();
13993
+ const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
13902
13994
  return success(null, null, all.data.filter((entry) => entry.key !== null && entry.status !== "closed").filter((entry) => (renames.length ? followRenames(entry.key, entry.boundAt ?? entry.createdAt, renames) : entry.key) === key).toSorted(oldestFirst));
13903
13995
  }
13904
13996
  openTaskForKey(key) {
@@ -15573,38 +15665,45 @@ var compactReference = (ref) => {
15573
15665
  };
15574
15666
  return { kind: ref.kind };
15575
15667
  };
15576
- function compactTaskContext(view) {
15577
- const decisions = view.task.decisions.slice().toSorted((left, right) => {
15578
- const leftAt = compactTimestamp(left.recordedAt);
15579
- const rightAt = compactTimestamp(right.recordedAt);
15580
- if (leftAt !== rightAt)
15581
- return leftAt > rightAt ? -1 : 1;
15582
- return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
15583
- }).slice(0, COMPACT_MAX_ITEMS).map((entry) => ({
15584
- id: entry.id,
15585
- purpose: entry.data.purpose,
15586
- status: entry.data.response,
15587
- digest: entry.data.digest,
15588
- choice: compactDecisionChoice(entry) || `task.inspect taskId=${view.task.id} view=full; decision=${entry.id}`,
15589
- references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
15590
- }));
15591
- const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
15668
+ var newestDecisionFirst = (left, right) => {
15669
+ const leftAt = compactTimestamp(left.recordedAt);
15670
+ const rightAt = compactTimestamp(right.recordedAt);
15671
+ if (leftAt !== rightAt)
15672
+ return leftAt > rightAt ? -1 : 1;
15673
+ return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
15674
+ };
15675
+ var compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
15676
+ id: entry.id,
15677
+ purpose: entry.data.purpose,
15678
+ status: entry.data.response,
15679
+ digest: entry.data.digest,
15680
+ choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
15681
+ references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
15682
+ origin: source.origin ?? undefined
15683
+ })) : []).slice(0, COMPACT_MAX_ITEMS);
15684
+ function compactTaskContext(view, inherited = null) {
15685
+ const decisions = compactDecisions([
15686
+ view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
15687
+ inherited
15688
+ ]);
15689
+ const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
15592
15690
  id: method.id,
15593
15691
  assurance: method.assurance,
15594
15692
  reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
15595
15693
  })) : [];
15694
+ const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
15596
15695
  const gaps = Array.from(new Set([
15597
- ...view.evidence.filter((entry) => entry.status === "stale").map((entry) => `stale evidence: ${entry.evidenceId}`),
15598
- ...view.requirements.filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
15599
- ...view.task.progress.blockers.map((entry) => entry.reason)
15696
+ ...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
15697
+ ...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
15698
+ ...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
15600
15699
  ])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
15601
15700
  const context = {
15602
- objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
15603
- status: view.task.status,
15701
+ objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
15702
+ status: view?.task.status ?? null,
15604
15703
  decisions,
15605
15704
  methods,
15606
15705
  gaps,
15607
- nextAction: compactText(view.task.progress.nextAction, COMPACT_TEXT_BYTES)
15706
+ nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
15608
15707
  };
15609
15708
  let encoded = JSON.stringify(context);
15610
15709
  while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
@@ -15793,6 +15892,13 @@ var TASK_SCOPED_ACTIONS = new Set([
15793
15892
  "worker.cancel",
15794
15893
  "state.export"
15795
15894
  ]);
15895
+ var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
15896
+ var workerCommand = (taskId, request) => {
15897
+ const { action, workerId, reason, report } = request;
15898
+ const rest = JSON.stringify({ reason, report });
15899
+ const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
15900
+ return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
15901
+ };
15796
15902
  var CREATING_ACTIONS = new Set([
15797
15903
  "task.revise",
15798
15904
  "task.progress",
@@ -16043,6 +16149,17 @@ class WorkitCore {
16043
16149
  const root = this.contextRootError();
16044
16150
  if (!root.ok)
16045
16151
  return root;
16152
+ const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
16153
+ const workerId = addressed ? value.workerId : this.context.workerId ?? null;
16154
+ if (workerId !== null) {
16155
+ const owner = this.store.taskForWorker(workerId);
16156
+ if (!owner.ok)
16157
+ return owner;
16158
+ if (owner.data)
16159
+ return success(null, null, { ...request, taskId: owner.data });
16160
+ if (addressed)
16161
+ return failure2("not_found", `worker ${workerId} is on no open task; find its task with \`workit task status --all\`, then run \`${workerCommand("<task-id>", request)}\``);
16162
+ }
16046
16163
  const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
16047
16164
  const found = this.store.implicitTask({
16048
16165
  provenance: provenance(this.context),
@@ -16749,7 +16866,9 @@ class WorkitCore {
16749
16866
  if (!resolved.ok)
16750
16867
  return resolved;
16751
16868
  const value = resolved.data;
16752
- return this.retryOmittedRevisions(value, () => this.workerOnce(value));
16869
+ const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
16870
+ const { action, taskId } = value ?? {};
16871
+ return result.ok || request?.taskId !== undefined || typeof taskId !== "string" || !WORKER_ADDRESSED_ACTIONS.has(`worker.${String(action)}`) ? result : { ...result, error: `${result.error}; run \`${workerCommand(taskId, value)}\`` };
16753
16872
  }
16754
16873
  workerOnce(request) {
16755
16874
  const root = this.contextRootError();
@@ -16954,10 +17073,18 @@ class WorkitCore {
16954
17073
  requirements
16955
17074
  });
16956
17075
  }
16957
- compactContext(taskId, freshness) {
17076
+ compactContext(taskId, freshness, origin = null) {
16958
17077
  const root = this.contextRootError();
16959
17078
  if (!root.ok)
16960
17079
  return root;
17080
+ const base = origin && this.store.readStoreTask(origin.taskId);
17081
+ const inherited = base?.ok && base.data ? {
17082
+ origin: origin.origin,
17083
+ taskId: base.data.id,
17084
+ decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
17085
+ } : null;
17086
+ if (taskId === null)
17087
+ return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
16961
17088
  const task = this.store.readTask(taskId);
16962
17089
  if (!task.ok)
16963
17090
  return task;
@@ -16967,7 +17094,7 @@ class WorkitCore {
16967
17094
  const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
16968
17095
  if (!view.ok)
16969
17096
  return view;
16970
- return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
17097
+ return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
16971
17098
  }
16972
17099
  view(task, capture = true, freshness = treeFreshness(this.store.root)) {
16973
17100
  const workspace = this.store.readWorkspace();
@@ -17288,8 +17415,9 @@ var currentTaskEntry = (entries, session, selection) => {
17288
17415
  const active = entries.filter((entry) => entry.status === "active");
17289
17416
  return active.length === 1 ? active[0] : null;
17290
17417
  };
17418
+ var otherConversation = (entry, session) => entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle));
17291
17419
  var implicitTaskEntry = (store, entries, session) => {
17292
- const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle)));
17420
+ const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !otherConversation(entry, session));
17293
17421
  if (open.length === 0)
17294
17422
  return null;
17295
17423
  const key = store.currentKey();
@@ -17330,18 +17458,23 @@ function sessionCompactContext(store, session, context, selection = "session-bou
17330
17458
  if (!listed.ok)
17331
17459
  return migrationPending(listed) ? MIGRATION_PENDING_NOTE : null;
17332
17460
  const entry = currentTaskEntry(listed.data, session, selection) ?? implicitTaskEntry(store, listed.data, session);
17333
- return entry ? entryCompactContext(store, entry, session, context) : null;
17461
+ return entryCompactContext(store, entry, session, context);
17334
17462
  }
17335
17463
  function entryCompactContext(store, entry, session, context) {
17464
+ const found = store.originTaskEntry();
17465
+ const origin = found.ok && found.data && found.data.entry.id !== entry?.id && !otherConversation(found.data.entry, session) ? found.data : null;
17466
+ if (!entry && !origin)
17467
+ return null;
17336
17468
  const workspace = store.readWorkspace();
17337
- if (!workspace.ok || !workspace.data)
17469
+ if (!workspace.ok || entry && !workspace.data)
17338
17470
  return null;
17339
17471
  const slot = `${store.root}\x00${session.host}\x00${session.handle}`;
17340
17472
  const key = canonicalJson({
17341
- task: entry.id,
17342
- revision: entry.revision,
17343
- file: entry.file,
17344
- workspace: workspace.data.revision,
17473
+ task: entry?.id ?? null,
17474
+ revision: entry?.revision ?? null,
17475
+ file: entry?.file ?? null,
17476
+ origin: origin && origin.entry.file,
17477
+ workspace: workspace.data?.revision ?? null,
17345
17478
  caller: context.caller,
17346
17479
  workerId: context.workerId ?? null,
17347
17480
  capabilities: context.capabilities
@@ -17349,15 +17482,15 @@ function entryCompactContext(store, entry, session, context) {
17349
17482
  const hit = cache.get(slot);
17350
17483
  if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
17351
17484
  return hit.value;
17352
- const task = store.readTask(entry.id);
17353
- if (!task.ok)
17485
+ const task = entry ? store.readTask(entry.id) : null;
17486
+ if (task && !task.ok)
17354
17487
  return null;
17355
17488
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
17356
- const observed = task.data.evidence.some(isObservedCheck);
17357
- const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path16.resolve(store.root, ref.path)] : []));
17489
+ const observed = task?.data.evidence.some(isObservedCheck) ?? false;
17490
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path16.resolve(store.root, ref.path)] : []));
17358
17491
  const observedFiles = filesKey(files);
17359
- const compact = new WorkitCore(store, context).compactContext(entry.id, freshness);
17360
- if (!compact.ok)
17492
+ const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
17493
+ if (!compact.ok || !compact.data)
17361
17494
  return null;
17362
17495
  cache.delete(slot);
17363
17496
  cache.set(slot, {
@@ -17396,7 +17529,7 @@ ${options.addendum}` : ""}
17396
17529
  if (!listed.ok)
17397
17530
  throw new Error(listed.error);
17398
17531
  const entry = currentTaskEntry(listed.data, session, descriptor.context.task) ?? implicitTaskEntry(store, listed.data, session);
17399
- const text = entry ? entryCompactContext(store, entry, session, hookOperationContext(input, descriptor)) : null;
17532
+ const text = entryCompactContext(store, entry, session, hookOperationContext(input, descriptor));
17400
17533
  if (text)
17401
17534
  compact = `
17402
17535
  <workit-task-context>${text}</workit-task-context>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-cursor",
3
- "version": "8.9.0",
3
+ "version": "8.9.1",
4
4
  "private": false,
5
5
  "description": "Workit Cursor plugin — shared MCP transport, native hooks, and sixteen method skills",
6
6
  "keywords": [
@@ -46,8 +46,8 @@
46
46
  "build": "bun scripts/build.ts"
47
47
  },
48
48
  "dependencies": {
49
- "@brainervirus/workit-core": "^8.9.0",
50
- "@brainervirus/workit-mcp": "^8.9.0"
49
+ "@brainervirus/workit-core": "^8.9.1",
50
+ "@brainervirus/workit-mcp": "^8.9.1"
51
51
  },
52
52
  "engines": {
53
53
  "node": ">=24"