@brainervirus/workit-opencode 8.15.1 → 8.16.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.
Files changed (2) hide show
  1. package/dist/plugin.js +119 -5
  2. package/package.json +2 -2
package/dist/plugin.js CHANGED
@@ -16086,6 +16086,54 @@ function evaluateClosure(requestedOutcome, view) {
16086
16086
  requirementIds: evaluations.map((item) => item.requirementId)
16087
16087
  });
16088
16088
  }
16089
+ var BRIEF_TEXT = 250;
16090
+ var BRIEF_BLOCKERS = 3;
16091
+ var BRIEF_OPEN = 8;
16092
+ var clip = (value) => {
16093
+ const fits = (text) => Buffer.byteLength(JSON.stringify(text)) <= BRIEF_TEXT;
16094
+ if (fits(value))
16095
+ return value;
16096
+ let length = Math.min(value.length, BRIEF_TEXT);
16097
+ while (length > 0 && !fits(`${value.slice(0, length)}…`))
16098
+ length -= 1;
16099
+ return `${value.slice(0, length)}…`;
16100
+ };
16101
+ function boundTaskSummary(summary) {
16102
+ const rules = new Map((summary.policy?.requirements ?? []).map((item) => [item.id, item]));
16103
+ const unmet = summary.requirements.filter((item) => item.status === "unsatisfied" || item.status === "unavailable");
16104
+ const { progress, closure } = summary;
16105
+ const next = progress.nextAction;
16106
+ const open = unmet.slice(0, BRIEF_OPEN).map((item) => ({
16107
+ dimension: rules.get(item.requirementId)?.dimension ?? "verification",
16108
+ before: rules.get(item.requirementId)?.before ?? "close",
16109
+ status: item.status
16110
+ }));
16111
+ const brief = {
16112
+ id: summary.id,
16113
+ revision: summary.revision,
16114
+ workspaceRevision: summary.workspaceRevision,
16115
+ updatedAt: summary.updatedAt,
16116
+ objective: clip(summary.objective),
16117
+ status: summary.status,
16118
+ closure: closure && {
16119
+ outcome: closure.outcome,
16120
+ at: closure.at,
16121
+ summary: clip(closure.summary)
16122
+ },
16123
+ progress: {
16124
+ summary: clip(progress.summary),
16125
+ nextAction: next && clip(next),
16126
+ blockers: progress.blockers.slice(0, BRIEF_BLOCKERS).map((item) => ({
16127
+ reason: clip(item.reason),
16128
+ dependentAction: clip(item.dependentAction)
16129
+ }))
16130
+ },
16131
+ requirements: { total: summary.requirements.length, unmet: unmet.length, open },
16132
+ truncated: false
16133
+ };
16134
+ brief.truncated = JSON.stringify(brief).includes("…") || progress.blockers.length > BRIEF_BLOCKERS || unmet.length > BRIEF_OPEN;
16135
+ return brief;
16136
+ }
16089
16137
 
16090
16138
  // packages/workit-core/src/core/methods.ts
16091
16139
  var METHODS = {
@@ -19514,6 +19562,15 @@ class WorkitCore {
19514
19562
  if (!parsed.ok)
19515
19563
  return parsed;
19516
19564
  const input = parsed.data;
19565
+ if (this.context.readOnlyChild) {
19566
+ if (input.action !== "inspect")
19567
+ return failure2("permission_denied", "child sessions can only inspect the task summary");
19568
+ const task = this.store.readTask(input.taskId);
19569
+ if (!task.ok)
19570
+ return task;
19571
+ const summary = this.summary(task.data);
19572
+ return summary.ok ? { ...summary, data: boundTaskSummary(summary.data) } : summary;
19573
+ }
19517
19574
  if ((this.context.workerId ?? null) !== null) {
19518
19575
  if (input.action !== "inspect" && input.action !== "list")
19519
19576
  return failure2("permission_denied", "helpers cannot control task lifecycle or scope");
@@ -24176,13 +24233,56 @@ var createV2Lifecycle = (deps) => {
24176
24233
  const unboundLaunch = (coordinator) => {
24177
24234
  const store = new TaskStore(root);
24178
24235
  const current = currentTaskId(store);
24179
- const base = current === null ? 'recovery_required: this branch has no Workit task yet; start it (workit_task start, or `workit task start "<objective>"`), then assign a worker on it with workit_worker before launching a native task' : "recovery_required: no assigned Workit worker is attributable to this coordinator on the current branch's task; assign one with workit_worker before launching a native task";
24236
+ const base = current === null ? 'recovery_required: this branch has no Workit task yet; start it (workit_task start, or `workit task start "<objective>"`), then assign a worker on it with workit_worker before launching a native task' : "recovery_required: no assigned Workit worker is attributable to this coordinator on the current branch's task (worker assignments are branch-scoped: they follow the task of the branch checked out when the launch happens); assign one with workit_worker on this branch's task before launching a native task";
24180
24237
  const listed = store.listTasks();
24181
24238
  if (!listed.ok)
24182
24239
  return base;
24183
24240
  const stale = listed.data.flatMap((task) => task.status === "active" && task.id !== current ? task.workers.filter((worker) => (worker.data.state === "assigned" || worker.data.state === "running") && worker.provenance.session?.kind === "host" && worker.provenance.session.handle === coordinator).map((worker) => `workit_worker {"action":"cancel","taskId":"${task.id}","workerId":"${worker.id}","reason":"stale worker from another branch"}`) : []);
24184
24241
  return stale.length > 0 ? `${base}. Workers of this coordinator are still open on other tasks; cancel any that are stale: ${stale.join("; ")}` : base;
24185
24242
  };
24243
+ const rebindStaleWorker = (coordinator, current, stale) => {
24244
+ if (stale.length !== 1)
24245
+ return false;
24246
+ const { task: from, entry } = stale[0];
24247
+ const store = new TaskStore(root);
24248
+ const target = store.readTask(current);
24249
+ if (!target.ok || target.data.status !== "active")
24250
+ return false;
24251
+ const core = new WorkitCore(store, {
24252
+ root,
24253
+ caller: { host: "opencode", actor: coordinator },
24254
+ capabilities: [],
24255
+ constraints: [],
24256
+ now
24257
+ });
24258
+ const assigned = core.worker({
24259
+ schemaVersion: 1,
24260
+ action: "assign",
24261
+ taskId: current,
24262
+ assignment: entry.data.assignment
24263
+ });
24264
+ if (!assigned.ok)
24265
+ return false;
24266
+ const cancelled = core.worker({
24267
+ schemaVersion: 1,
24268
+ action: "cancel",
24269
+ taskId: from.id,
24270
+ workerId: entry.id,
24271
+ reason: `rebound to task ${current} (worker ${assigned.data.id}) after a branch switch`
24272
+ });
24273
+ const latest = store.readTask(current);
24274
+ if (cancelled.ok && latest.ok)
24275
+ core.task({
24276
+ schemaVersion: 1,
24277
+ action: "progress",
24278
+ taskId: current,
24279
+ progress: {
24280
+ ...latest.data.progress,
24281
+ summary: `${latest.data.progress.summary} (worker ${assigned.data.id} rebound from task ${from.id} after a branch switch)`
24282
+ }
24283
+ });
24284
+ return cancelled.ok;
24285
+ };
24186
24286
  const prepareDispatch = (coordinator, callID) => {
24187
24287
  if (dispatches.has(coordinator))
24188
24288
  return "unsettled";
@@ -24196,7 +24296,16 @@ var createV2Lifecycle = (deps) => {
24196
24296
  return "unmanaged";
24197
24297
  const eligible = managed.flatMap((task) => task.workers.filter((entry) => entry.data.state === "assigned" && entry.data.session === null && entry.provenance.session?.kind === "host" && entry.provenance.session.host === "opencode" && entry.provenance.session.handle === coordinator).map((entry) => ({ task, entry })));
24198
24298
  const current = currentTaskId(store);
24199
- const next = oldest(eligible.filter((item) => item.task.id === current));
24299
+ let next = oldest(eligible.filter((item) => item.task.id === current));
24300
+ if (!next && current !== null) {
24301
+ const stale = eligible.filter((item) => item.task.id !== current);
24302
+ if (rebindStaleWorker(coordinator, current, stale)) {
24303
+ const refreshed = store.readTask(current);
24304
+ const rebound = refreshed.ok ? refreshed.data.workers.filter((entry) => entry.data.state === "assigned" && entry.data.session === null && entry.provenance.session?.kind === "host" && entry.provenance.session.handle === coordinator) : [];
24305
+ if (refreshed.ok && rebound.length > 0)
24306
+ next = oldest(rebound.map((entry) => ({ task: refreshed.data, entry })));
24307
+ }
24308
+ }
24200
24309
  if (!next)
24201
24310
  return "unbound";
24202
24311
  dispatches.set(coordinator, {
@@ -24659,13 +24768,14 @@ var childMayRecord = (family, input) => {
24659
24768
  const action = fields(input).action;
24660
24769
  return family !== null && typeof action === "string" && CHILD_RECORDS[family]?.has(action) === true;
24661
24770
  };
24771
+ var childMayInspect = (family, input) => family === "task" && fields(input).action === "inspect";
24662
24772
  var childTaskMissing = (store, input) => {
24663
24773
  if (fields(input).taskId !== undefined)
24664
24774
  return false;
24665
24775
  const report = store.keyReport();
24666
24776
  return !report.ok || report.data.bound.length === 0;
24667
24777
  };
24668
- var childDenial = (identity) => `OpenCode child session has no validated Workit worker: as ${identity} it may only record evidence or findings (workit_evidence/workit_finding action "record") on an existing task; for anything else the lead assigns it a worker with workit_worker before launching it`;
24778
+ var childDenial = (identity) => `OpenCode child session has no validated Workit worker: as ${identity} it may only inspect the task summary (workit_task action "inspect") or record evidence or findings (workit_evidence/workit_finding action "record") on an existing task; for anything else the lead assigns it a worker with workit_worker before launching it`;
24669
24779
 
24670
24780
  // packages/workit-opencode/src/v2/shell.ts
24671
24781
  import path38 from "node:path";
@@ -25041,7 +25151,7 @@ var setup = async (ctx) => {
25041
25151
  }
25042
25152
  });
25043
25153
  const contextTool = createContextTool();
25044
- const runFamily = (family, input, session, store, workerId) => {
25154
+ const runFamily = (family, input, session, store, workerId, readOnlyChild = false) => {
25045
25155
  const parsed = parseAdvertisedOperation(family, input, "opencode");
25046
25156
  if (!parsed.ok)
25047
25157
  return parsed;
@@ -25052,6 +25162,7 @@ var setup = async (ctx) => {
25052
25162
  constraints: [],
25053
25163
  now: () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
25054
25164
  workerId,
25165
+ ...readOnlyChild ? { readOnlyChild } : {},
25055
25166
  nativeWorker: nativeWorkerFor(lifecycle.directChildren, session.id)
25056
25167
  });
25057
25168
  const run = core[family];
@@ -25079,8 +25190,11 @@ var setup = async (ctx) => {
25079
25190
  const identity = childSessionId(session.parentID, session.id);
25080
25191
  const lead = await sessionFacts(ctx, session.parentID);
25081
25192
  const directChild = lead !== null && lead.parentID === undefined && sameWorkspace(root, lead.directory);
25082
- if (!directChild || family === null || !childMayRecord(family, input))
25193
+ const inspecting = childMayInspect(family, input);
25194
+ if (!directChild || family === null || !(inspecting || childMayRecord(family, input)))
25083
25195
  return resultContent(failure2("permission_denied", childDenial(identity)));
25196
+ if (inspecting)
25197
+ return resultContent(runFamily(family, input, { ...session, id: identity }, store, null, true));
25084
25198
  if (childTaskMissing(store, input))
25085
25199
  return resultContent(failure2("not_found", "no task for this branch yet: the lead starts one (workit_task start) before a child records on it, or pass taskId"));
25086
25200
  return resultContent(runFamily(family, input, { ...session, id: identity }, store, null));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-opencode",
3
- "version": "8.15.1",
3
+ "version": "8.16.1",
4
4
  "private": false,
5
5
  "description": "Workit OpenCode plugin — native tools, adaptive policy, and sixteen method skills",
6
6
  "keywords": [
@@ -39,7 +39,7 @@
39
39
  "typecheck": "tsc --noEmit"
40
40
  },
41
41
  "devDependencies": {
42
- "@brainervirus/workit-core": "^8.15.1",
42
+ "@brainervirus/workit-core": "^8.16.1",
43
43
  "@opencode/plugin": "2.0.18"
44
44
  },
45
45
  "engines": {