@brainervirus/workit-pi 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.
package/dist/worker.js CHANGED
@@ -8370,6 +8370,7 @@ var SLOT = Symbol.for("workit.migrationReporter");
8370
8370
  var AUTO_COMPACT_BYTES = 2 * 1024 * 1024;
8371
8371
  var TOMBSTONE = Symbol("tombstone");
8372
8372
  var STALE_TEMPORARY_MS = 60 * 60000;
8373
+ var origins = new Map;
8373
8374
  var heldInProcess = new Map;
8374
8375
  // packages/workit-core/src/git/rev.ts
8375
8376
  var MAX_BUFFER = 256 * 1024 * 1024;
@@ -8605,6 +8606,7 @@ var TASK_SCOPED_ACTIONS = new Set([
8605
8606
  "worker.cancel",
8606
8607
  "state.export"
8607
8608
  ]);
8609
+ var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
8608
8610
  var CREATING_ACTIONS = new Set([
8609
8611
  "task.revise",
8610
8612
  "task.progress",
package/dist/workit.js CHANGED
@@ -10563,6 +10563,38 @@ var isNewerVersion = (candidate, current) => {
10563
10563
  return false;
10564
10564
  };
10565
10565
  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);
10566
+ var readText = (file) => {
10567
+ try {
10568
+ return fs7.readFileSync(file, "utf8");
10569
+ } catch {
10570
+ return "";
10571
+ }
10572
+ };
10573
+ var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
10574
+ var branchStart = (location, branch, aliases) => {
10575
+ const first = readText(path6.join(path6.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
10576
+ `, 1)[0];
10577
+ const created = REFLOG_LINE.exec(first);
10578
+ const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
10579
+ if (!created || !start)
10580
+ return null;
10581
+ if (start !== "HEAD")
10582
+ return start;
10583
+ if (!location.git)
10584
+ return null;
10585
+ for (const line of readText(path6.join(location.git.gitDir, "logs", "HEAD")).split(`
10586
+ `)) {
10587
+ const entry = REFLOG_LINE.exec(line);
10588
+ const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
10589
+ const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
10590
+ if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
10591
+ return moved[1];
10592
+ }
10593
+ return null;
10594
+ };
10595
+ var refExists = (common, ref) => fs7.existsSync(path6.join(common, ref)) || readText(path6.join(common, "packed-refs")).includes(` ${ref}
10596
+ `);
10597
+ var origins = new Map;
10566
10598
  var sameDirectoryIdentity = (left, right) => {
10567
10599
  if (!path6.isAbsolute(left) || !path6.isAbsolute(right))
10568
10600
  return false;
@@ -10815,6 +10847,66 @@ class TaskStore {
10815
10847
  return bound;
10816
10848
  return success(null, null, { key: key.data, bound: bound.data });
10817
10849
  }
10850
+ taskForWorker(workerId) {
10851
+ if (typeof workerId !== "string" || !validId(workerId))
10852
+ return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
10853
+ const listed = this.listTaskIndex();
10854
+ if (!listed.ok)
10855
+ return listed;
10856
+ for (const entry of listed.data) {
10857
+ if (entry.status === "closed")
10858
+ continue;
10859
+ const loaded = this.load(entry.id);
10860
+ if (!loaded.ok)
10861
+ return loaded;
10862
+ if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
10863
+ return success(null, null, entry.id);
10864
+ }
10865
+ return success(null, null, null);
10866
+ }
10867
+ originTaskEntry() {
10868
+ const key = this.currentKey();
10869
+ const location = this.location();
10870
+ const branch = key.ok ? key.data.branch : null;
10871
+ if (!branch || !location.ok)
10872
+ return success(null, null, null);
10873
+ const common = path6.dirname(location.data.dir);
10874
+ const heads = path6.join(common, "logs", "refs", "heads");
10875
+ const headLog = location.data.git ? path6.join(location.data.git.gitDir, "logs", "HEAD") : "";
10876
+ const signatures = [
10877
+ path6.join(heads, branch),
10878
+ headLog,
10879
+ heads,
10880
+ path6.join(common, "packed-refs")
10881
+ ].map((file) => fileSignature(file) ?? "-");
10882
+ const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
10883
+ let resolved = origins.get(slot);
10884
+ if (resolved?.signature !== signatures.join("|")) {
10885
+ const renames = branchRenames(location.data);
10886
+ const aliases = [branch];
10887
+ for (const rename of renames.toReversed())
10888
+ if (rename.to === aliases.at(-1))
10889
+ aliases.push(rename.from);
10890
+ const start = branchStart(location.data, branch, aliases);
10891
+ const slash = start?.indexOf("/") ?? -1;
10892
+ 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)}"]`));
10893
+ const name = start && (remote ? start.slice(slash + 1) : start);
10894
+ resolved = {
10895
+ signature: signatures.join("|"),
10896
+ name: name && !aliases.includes(name) ? name : null,
10897
+ renames
10898
+ };
10899
+ origins.set(slot, resolved);
10900
+ }
10901
+ const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
10902
+ if (entry && !entry.ok)
10903
+ return entry;
10904
+ return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
10905
+ }
10906
+ readStoreTask(taskId) {
10907
+ const loaded = this.load(taskId);
10908
+ return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
10909
+ }
10818
10910
  create(input, provenance, expectedWorkspaceRevision) {
10819
10911
  const value = "intent" in input ? input : {
10820
10912
  intent: input,
@@ -11484,12 +11576,12 @@ class TaskStore {
11484
11576
  return 0;
11485
11577
  }
11486
11578
  }
11487
- openTasksForKey(key) {
11579
+ openTasksForKey(key, known) {
11488
11580
  const all = this.listStoreIndex();
11489
11581
  if (!all.ok)
11490
11582
  return all;
11491
- const location = this.location();
11492
- const renames = location.ok ? branchRenames(location.data) : [];
11583
+ const location = known ? null : this.location();
11584
+ const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
11493
11585
  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));
11494
11586
  }
11495
11587
  openTaskForKey(key) {
@@ -15659,38 +15751,45 @@ var compactReference = (ref) => {
15659
15751
  };
15660
15752
  return { kind: ref.kind };
15661
15753
  };
15662
- function compactTaskContext(view) {
15663
- const decisions = view.task.decisions.slice().toSorted((left, right) => {
15664
- const leftAt = compactTimestamp(left.recordedAt);
15665
- const rightAt = compactTimestamp(right.recordedAt);
15666
- if (leftAt !== rightAt)
15667
- return leftAt > rightAt ? -1 : 1;
15668
- return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
15669
- }).slice(0, COMPACT_MAX_ITEMS).map((entry) => ({
15670
- id: entry.id,
15671
- purpose: entry.data.purpose,
15672
- status: entry.data.response,
15673
- digest: entry.data.digest,
15674
- choice: compactDecisionChoice(entry) || `task.inspect taskId=${view.task.id} view=full; decision=${entry.id}`,
15675
- references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
15676
- }));
15677
- const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
15754
+ var newestDecisionFirst = (left, right) => {
15755
+ const leftAt = compactTimestamp(left.recordedAt);
15756
+ const rightAt = compactTimestamp(right.recordedAt);
15757
+ if (leftAt !== rightAt)
15758
+ return leftAt > rightAt ? -1 : 1;
15759
+ return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
15760
+ };
15761
+ var compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
15762
+ id: entry.id,
15763
+ purpose: entry.data.purpose,
15764
+ status: entry.data.response,
15765
+ digest: entry.data.digest,
15766
+ choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
15767
+ references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
15768
+ origin: source.origin ?? undefined
15769
+ })) : []).slice(0, COMPACT_MAX_ITEMS);
15770
+ function compactTaskContext(view, inherited = null) {
15771
+ const decisions = compactDecisions([
15772
+ view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
15773
+ inherited
15774
+ ]);
15775
+ const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
15678
15776
  id: method.id,
15679
15777
  assurance: method.assurance,
15680
15778
  reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
15681
15779
  })) : [];
15780
+ const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
15682
15781
  const gaps = Array.from(new Set([
15683
- ...view.evidence.filter((entry) => entry.status === "stale").map((entry) => `stale evidence: ${entry.evidenceId}`),
15684
- ...view.requirements.filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
15685
- ...view.task.progress.blockers.map((entry) => entry.reason)
15782
+ ...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
15783
+ ...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
15784
+ ...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
15686
15785
  ])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
15687
15786
  const context = {
15688
- objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
15689
- status: view.task.status,
15787
+ objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
15788
+ status: view?.task.status ?? null,
15690
15789
  decisions,
15691
15790
  methods,
15692
15791
  gaps,
15693
- nextAction: compactText(view.task.progress.nextAction, COMPACT_TEXT_BYTES)
15792
+ nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
15694
15793
  };
15695
15794
  let encoded = JSON.stringify(context);
15696
15795
  while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
@@ -16408,6 +16507,13 @@ var TASK_SCOPED_ACTIONS = new Set([
16408
16507
  "worker.cancel",
16409
16508
  "state.export"
16410
16509
  ]);
16510
+ var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
16511
+ var workerCommand = (taskId, request) => {
16512
+ const { action, workerId, reason, report } = request;
16513
+ const rest = JSON.stringify({ reason, report });
16514
+ const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
16515
+ return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
16516
+ };
16411
16517
  var CREATING_ACTIONS = new Set([
16412
16518
  "task.revise",
16413
16519
  "task.progress",
@@ -16658,6 +16764,17 @@ class WorkitCore {
16658
16764
  const root = this.contextRootError();
16659
16765
  if (!root.ok)
16660
16766
  return root;
16767
+ const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
16768
+ const workerId = addressed ? value.workerId : this.context.workerId ?? null;
16769
+ if (workerId !== null) {
16770
+ const owner = this.store.taskForWorker(workerId);
16771
+ if (!owner.ok)
16772
+ return owner;
16773
+ if (owner.data)
16774
+ return success(null, null, { ...request, taskId: owner.data });
16775
+ if (addressed)
16776
+ 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)}\``);
16777
+ }
16661
16778
  const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
16662
16779
  const found = this.store.implicitTask({
16663
16780
  provenance: provenance(this.context),
@@ -17364,7 +17481,9 @@ class WorkitCore {
17364
17481
  if (!resolved.ok)
17365
17482
  return resolved;
17366
17483
  const value = resolved.data;
17367
- return this.retryOmittedRevisions(value, () => this.workerOnce(value));
17484
+ const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
17485
+ const { action, taskId } = value ?? {};
17486
+ 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)}\`` };
17368
17487
  }
17369
17488
  workerOnce(request) {
17370
17489
  const root = this.contextRootError();
@@ -17569,10 +17688,18 @@ class WorkitCore {
17569
17688
  requirements
17570
17689
  });
17571
17690
  }
17572
- compactContext(taskId, freshness) {
17691
+ compactContext(taskId, freshness, origin = null) {
17573
17692
  const root = this.contextRootError();
17574
17693
  if (!root.ok)
17575
17694
  return root;
17695
+ const base = origin && this.store.readStoreTask(origin.taskId);
17696
+ const inherited = base?.ok && base.data ? {
17697
+ origin: origin.origin,
17698
+ taskId: base.data.id,
17699
+ decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
17700
+ } : null;
17701
+ if (taskId === null)
17702
+ return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
17576
17703
  const task = this.store.readTask(taskId);
17577
17704
  if (!task.ok)
17578
17705
  return task;
@@ -17582,7 +17709,7 @@ class WorkitCore {
17582
17709
  const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
17583
17710
  if (!view.ok)
17584
17711
  return view;
17585
- return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
17712
+ return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
17586
17713
  }
17587
17714
  view(task, capture = true, freshness = treeFreshness(this.store.root)) {
17588
17715
  const workspace = this.store.readWorkspace();
@@ -18980,8 +19107,9 @@ var currentTaskEntry = (entries, session, selection) => {
18980
19107
  const active = entries.filter((entry) => entry.status === "active");
18981
19108
  return active.length === 1 ? active[0] : null;
18982
19109
  };
19110
+ var otherConversation = (entry, session) => entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle));
18983
19111
  var implicitTaskEntry = (store, entries, session) => {
18984
- 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)));
19112
+ const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !otherConversation(entry, session));
18985
19113
  if (open.length === 0)
18986
19114
  return null;
18987
19115
  const key = store.currentKey();
@@ -19022,18 +19150,23 @@ function sessionCompactContext(store, session, context, selection = "session-bou
19022
19150
  if (!listed.ok)
19023
19151
  return migrationPending(listed) ? MIGRATION_PENDING_NOTE : null;
19024
19152
  const entry = currentTaskEntry(listed.data, session, selection) ?? implicitTaskEntry(store, listed.data, session);
19025
- return entry ? entryCompactContext(store, entry, session, context) : null;
19153
+ return entryCompactContext(store, entry, session, context);
19026
19154
  }
19027
19155
  function entryCompactContext(store, entry, session, context) {
19156
+ const found = store.originTaskEntry();
19157
+ const origin = found.ok && found.data && found.data.entry.id !== entry?.id && !otherConversation(found.data.entry, session) ? found.data : null;
19158
+ if (!entry && !origin)
19159
+ return null;
19028
19160
  const workspace = store.readWorkspace();
19029
- if (!workspace.ok || !workspace.data)
19161
+ if (!workspace.ok || entry && !workspace.data)
19030
19162
  return null;
19031
19163
  const slot = `${store.root}\x00${session.host}\x00${session.handle}`;
19032
19164
  const key = canonicalJson({
19033
- task: entry.id,
19034
- revision: entry.revision,
19035
- file: entry.file,
19036
- workspace: workspace.data.revision,
19165
+ task: entry?.id ?? null,
19166
+ revision: entry?.revision ?? null,
19167
+ file: entry?.file ?? null,
19168
+ origin: origin && origin.entry.file,
19169
+ workspace: workspace.data?.revision ?? null,
19037
19170
  caller: context.caller,
19038
19171
  workerId: context.workerId ?? null,
19039
19172
  capabilities: context.capabilities
@@ -19041,15 +19174,15 @@ function entryCompactContext(store, entry, session, context) {
19041
19174
  const hit = cache.get(slot);
19042
19175
  if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
19043
19176
  return hit.value;
19044
- const task = store.readTask(entry.id);
19045
- if (!task.ok)
19177
+ const task = entry ? store.readTask(entry.id) : null;
19178
+ if (task && !task.ok)
19046
19179
  return null;
19047
19180
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
19048
- const observed = task.data.evidence.some(isObservedCheck);
19049
- const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path20.resolve(store.root, ref.path)] : []));
19181
+ const observed = task?.data.evidence.some(isObservedCheck) ?? false;
19182
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path20.resolve(store.root, ref.path)] : []));
19050
19183
  const observedFiles = filesKey(files);
19051
- const compact = new WorkitCore(store, context).compactContext(entry.id, freshness);
19052
- if (!compact.ok)
19184
+ const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
19185
+ if (!compact.ok || !compact.data)
19053
19186
  return null;
19054
19187
  cache.delete(slot);
19055
19188
  cache.set(slot, {
@@ -19088,7 +19221,7 @@ ${options.addendum}` : ""}
19088
19221
  if (!listed.ok)
19089
19222
  throw new Error(listed.error);
19090
19223
  const entry = currentTaskEntry(listed.data, session, descriptor.context.task) ?? implicitTaskEntry(store, listed.data, session);
19091
- const text = entry ? entryCompactContext(store, entry, session, hookOperationContext(input, descriptor)) : null;
19224
+ const text = entryCompactContext(store, entry, session, hookOperationContext(input, descriptor));
19092
19225
  if (text)
19093
19226
  compact = `
19094
19227
  <workit-task-context>${text}</workit-task-context>`;
@@ -20994,7 +21127,7 @@ var roleTools = (role) => {
20994
21127
  return [...readOnly, "write", "edit", ...familyTools];
20995
21128
  return [...readOnly, ...familyTools];
20996
21129
  };
20997
- function workerCommand(runtime, assignment) {
21130
+ function workerCommand2(runtime, assignment) {
20998
21131
  return {
20999
21132
  command: runtime.node,
21000
21133
  args: [
@@ -21046,7 +21179,7 @@ function launchWorker(assignment, options) {
21046
21179
  const taskId = options.taskId ?? null;
21047
21180
  const workerId = options.workerId ?? null;
21048
21181
  const sessionId = options.sessionId ?? "pi-worker-" + randomUUID4();
21049
- const spec = workerCommand({ ...options.runtime, sessionId }, assignment);
21182
+ const spec = workerCommand2({ ...options.runtime, sessionId }, assignment);
21050
21183
  const handle = {
21051
21184
  id: randomUUID4(),
21052
21185
  taskId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-pi",
3
- "version": "8.9.0",
3
+ "version": "8.9.1",
4
4
  "description": "Workit native extension for Pi — tools, method skills, and session continuity",
5
5
  "keywords": [
6
6
  "agentic-coding",