@brainervirus/workit-opencode 8.9.1 → 8.10.0
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.
|
@@ -25,7 +25,7 @@ REPORT: branch, head SHA, files changed, each VERIFY command with its exit code,
|
|
|
25
25
|
each ACCEPTANCE line met / not met, rulings you made (`workit ledger ruling`),
|
|
26
26
|
anything out of scope as a follow-up, not a diff.
|
|
27
27
|
STANDING: <the standing orders, verbatim: `workit ledger standing add` records each one>
|
|
28
|
-
export WORKIT_SESSION_ID=<lead>-w<n> (set by the lead; a verifier brief
|
|
28
|
+
export WORKIT_SESSION_ID=<lead>-w<n> (set by the lead for a worker's commits; a verifier brief sets none: the host derives it)
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
## Worked example
|
|
@@ -72,12 +72,22 @@ Which session id a verdict carries, per host:
|
|
|
72
72
|
|
|
73
73
|
- Claude Code: subagents inherit your `WORKIT_SESSION_ID`, so the
|
|
74
74
|
SubagentStart hook names each `verifier` and `reviewer` its own session
|
|
75
|
-
(`<lead>:<agent id>`), passed as `--session
|
|
76
|
-
|
|
77
|
-
exports for its workit commands.
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
(`<lead>:<agent id>`), passed exactly as `--session`: the hook records the
|
|
76
|
+
ids it issues, and only those are accepted. An `implementer` uses the
|
|
77
|
+
session its brief exports for its workit commands.
|
|
78
|
+
- Codex: the `workit-verifier` and `workit-reviewer` agents get the same
|
|
79
|
+
issued `--session` from SubagentStart; any other verifier passes
|
|
80
|
+
`--as verifier`.
|
|
81
|
+
- OpenCode: a child session's shell already has
|
|
82
|
+
`WORKIT_SESSION_ID=<lead>:<child>`; Pi: a supervised worker's shell has
|
|
83
|
+
`<lead>:<worker session>`. Set nothing for a verifier there.
|
|
84
|
+
- Cursor (no session id in the shell): the brief sets the verifier's
|
|
85
|
+
`WORKIT_SESSION_ID=<lead>:verifier-<random hex>`, a fresh suffix per
|
|
86
|
+
verifier. Cursor cannot check it: it is declared, not derived.
|
|
87
|
+
- A worker's commits use `export WORKIT_SESSION_ID=<lead>-w-<slice>` from
|
|
88
|
+
`workit fanout brief`. A verdict under an id you made up (`<lead>-v1`,
|
|
89
|
+
`<lead>:v1`) is refused as `forged_session` on Claude Code, Codex,
|
|
90
|
+
OpenCode and Pi.
|
|
81
91
|
|
|
82
92
|
A batch verifier takes several slices on one surface in one session: for
|
|
83
93
|
each branch it checks out the head, runs that slice's VERIFY, and records its
|
|
@@ -33,8 +33,9 @@ description: Build a requested change in small verified steps - follow local pat
|
|
|
33
33
|
`workit ledger verdict tests-verified --self --how "<what you ran>"`; it
|
|
34
34
|
reads self-reviewed, never verified. High risk, a workspace with
|
|
35
35
|
`verification: independent` (`workit grant show`) or a verified merge needs
|
|
36
|
-
a fresh verifier with its own session (
|
|
37
|
-
|
|
36
|
+
a fresh verifier with its own host-derived session (Claude Code and Codex:
|
|
37
|
+
the `verifier` agent; OpenCode: a child session; Pi: a supervised worker),
|
|
38
|
+
never a hand-minted id. Before saying done, reconcile
|
|
38
39
|
every named deliverable against the target checkout and observe it (for a push:
|
|
39
40
|
`workit verify-delivery push`).
|
|
40
41
|
|
|
@@ -7,8 +7,10 @@ description: Independent review of a diff, branch or PR - intent fidelity and st
|
|
|
7
7
|
|
|
8
8
|
Review the candidate, never the author's summary of it. A session that wrote a
|
|
9
9
|
commit on the branch cannot record a passing verdict: if that is you, hand the
|
|
10
|
-
review to a fresh agent with its own session
|
|
11
|
-
|
|
10
|
+
review to a fresh agent with its own host-derived session: Claude Code and
|
|
11
|
+
Codex, the `reviewer` or `verifier` agent (`--session` from SubagentStart);
|
|
12
|
+
OpenCode, a child session; Pi, a supervised worker. Never hand-mint one such
|
|
13
|
+
as `<lead>-v1`: it is refused as `forged_session`.
|
|
12
14
|
An author's `--self` verdict reads self-reviewed and never counts as
|
|
13
15
|
independent; `type-check-only` never proves a behavior change.
|
|
14
16
|
|
package/dist/plugin.js
CHANGED
|
@@ -9597,64 +9597,11 @@ function acquireFileLockSync(targetPath, options) {
|
|
|
9597
9597
|
}
|
|
9598
9598
|
}
|
|
9599
9599
|
}
|
|
9600
|
-
// packages/workit-core/src/host-session.ts
|
|
9601
|
-
var HOST_SESSION_VARS = [
|
|
9602
|
-
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
9603
|
-
["PI_SESSION_ID", () => "pi"],
|
|
9604
|
-
[
|
|
9605
|
-
"CODEX_THREAD_ID",
|
|
9606
|
-
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
9607
|
-
]
|
|
9608
|
-
];
|
|
9609
|
-
var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
9610
|
-
function hostSessionFromEnv(env) {
|
|
9611
|
-
const value = (key) => env[key]?.trim() || null;
|
|
9612
|
-
let inner = null;
|
|
9613
|
-
for (const [key, host] of HOST_SESSION_VARS) {
|
|
9614
|
-
const session = value(key);
|
|
9615
|
-
if (session) {
|
|
9616
|
-
inner = { host: host(env), session };
|
|
9617
|
-
break;
|
|
9618
|
-
}
|
|
9619
|
-
}
|
|
9620
|
-
const declared = value("WORKIT_HOST");
|
|
9621
|
-
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
9622
|
-
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
9623
|
-
if (!inherited)
|
|
9624
|
-
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
9625
|
-
}
|
|
9626
|
-
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9627
|
-
}
|
|
9628
|
-
|
|
9629
|
-
// packages/workit-core/src/core/package-root.ts
|
|
9630
|
-
import { existsSync } from "node:fs";
|
|
9631
|
-
import path3 from "node:path";
|
|
9632
|
-
import { fileURLToPath } from "node:url";
|
|
9633
|
-
var packageRoot = () => {
|
|
9634
|
-
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
9635
|
-
while (true) {
|
|
9636
|
-
const parent = path3.dirname(dir);
|
|
9637
|
-
if (existsSync(path3.join(dir, "package.json")) || parent === dir)
|
|
9638
|
-
return dir;
|
|
9639
|
-
dir = parent;
|
|
9640
|
-
}
|
|
9641
|
-
};
|
|
9642
|
-
var assetRoot = () => {
|
|
9643
|
-
const root = packageRoot();
|
|
9644
|
-
const assets = path3.join(root, "assets");
|
|
9645
|
-
return existsSync(assets) ? assets : root;
|
|
9646
|
-
};
|
|
9647
|
-
|
|
9648
|
-
// packages/workit-core/src/core/store-lock.ts
|
|
9649
|
-
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
9650
|
-
import fs5 from "node:fs";
|
|
9651
|
-
import { hostname } from "node:os";
|
|
9652
|
-
|
|
9653
9600
|
// packages/workit-core/src/store/paths.ts
|
|
9654
9601
|
import { spawnSync } from "node:child_process";
|
|
9655
9602
|
import { createHash as createHash2 } from "node:crypto";
|
|
9656
9603
|
import fs4 from "node:fs";
|
|
9657
|
-
import
|
|
9604
|
+
import path3 from "node:path";
|
|
9658
9605
|
var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
|
|
9659
9606
|
var gitRun = (cwd, args) => {
|
|
9660
9607
|
const run = spawnSync("git", args, {
|
|
@@ -9696,14 +9643,14 @@ var DISCOVERY_ENV = [
|
|
|
9696
9643
|
];
|
|
9697
9644
|
var isGitDir = (dir) => {
|
|
9698
9645
|
try {
|
|
9699
|
-
return fs4.statSync(
|
|
9646
|
+
return fs4.statSync(path3.join(dir, "HEAD")).isFile() && fs4.statSync(path3.join(dir, "objects")).isDirectory() && fs4.statSync(path3.join(dir, "refs")).isDirectory();
|
|
9700
9647
|
} catch {
|
|
9701
9648
|
return false;
|
|
9702
9649
|
}
|
|
9703
9650
|
};
|
|
9704
9651
|
var reftableRefs = (common) => {
|
|
9705
9652
|
try {
|
|
9706
|
-
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(
|
|
9653
|
+
return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path3.join(common, "config"), "utf8"));
|
|
9707
9654
|
} catch {
|
|
9708
9655
|
return false;
|
|
9709
9656
|
}
|
|
@@ -9723,7 +9670,7 @@ function fastGit(root) {
|
|
|
9723
9670
|
if (device !== null && stat.dev !== device)
|
|
9724
9671
|
return { kind: "none" };
|
|
9725
9672
|
device = stat.dev;
|
|
9726
|
-
const dotGit =
|
|
9673
|
+
const dotGit = path3.join(dir, ".git");
|
|
9727
9674
|
let entry = null;
|
|
9728
9675
|
try {
|
|
9729
9676
|
entry = fs4.statSync(dotGit);
|
|
@@ -9744,19 +9691,19 @@ function fastGit(root) {
|
|
|
9744
9691
|
const match = /^gitdir: (.+)$/mu.exec(text);
|
|
9745
9692
|
if (!match)
|
|
9746
9693
|
return null;
|
|
9747
|
-
gitDir =
|
|
9694
|
+
gitDir = path3.resolve(dir, match[1].trim());
|
|
9748
9695
|
} else
|
|
9749
9696
|
return null;
|
|
9750
|
-
if (!fs4.existsSync(
|
|
9697
|
+
if (!fs4.existsSync(path3.join(gitDir, "HEAD")))
|
|
9751
9698
|
return null;
|
|
9752
9699
|
} else if (isGitDir(dir))
|
|
9753
9700
|
gitDir = dir;
|
|
9754
9701
|
if (gitDir) {
|
|
9755
9702
|
let common = gitDir;
|
|
9756
9703
|
try {
|
|
9757
|
-
common =
|
|
9704
|
+
common = path3.resolve(gitDir, fs4.readFileSync(path3.join(gitDir, "commondir"), "utf8").trim());
|
|
9758
9705
|
} catch {}
|
|
9759
|
-
if (!fs4.existsSync(
|
|
9706
|
+
if (!fs4.existsSync(path3.join(common, "objects")))
|
|
9760
9707
|
return null;
|
|
9761
9708
|
return {
|
|
9762
9709
|
kind: "repo",
|
|
@@ -9766,26 +9713,26 @@ function fastGit(root) {
|
|
|
9766
9713
|
reftable: reftableRefs(common)
|
|
9767
9714
|
};
|
|
9768
9715
|
}
|
|
9769
|
-
const parent =
|
|
9716
|
+
const parent = path3.dirname(dir);
|
|
9770
9717
|
if (parent === dir)
|
|
9771
9718
|
return { kind: "none" };
|
|
9772
9719
|
dir = parent;
|
|
9773
9720
|
}
|
|
9774
9721
|
}
|
|
9775
9722
|
function resolveStore(root) {
|
|
9776
|
-
root =
|
|
9723
|
+
root = path3.resolve(root);
|
|
9777
9724
|
let directory = false;
|
|
9778
9725
|
try {
|
|
9779
9726
|
directory = fs4.statSync(root).isDirectory();
|
|
9780
9727
|
} catch {}
|
|
9781
9728
|
if (!directory)
|
|
9782
|
-
return { dir:
|
|
9729
|
+
return { dir: path3.join(root, ".workit"), shared: false, top: null };
|
|
9783
9730
|
const fast = fastGit(root);
|
|
9784
9731
|
if (fast?.kind === "none")
|
|
9785
|
-
return { dir:
|
|
9732
|
+
return { dir: path3.join(root, ".workit"), shared: false, top: null };
|
|
9786
9733
|
if (fast?.kind === "repo")
|
|
9787
9734
|
return {
|
|
9788
|
-
dir:
|
|
9735
|
+
dir: path3.join(fast.common, "workit"),
|
|
9789
9736
|
shared: true,
|
|
9790
9737
|
top: fast.top,
|
|
9791
9738
|
git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
|
|
@@ -9795,20 +9742,20 @@ function resolveStore(root) {
|
|
|
9795
9742
|
if (run.ok && common) {
|
|
9796
9743
|
const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
|
|
9797
9744
|
return {
|
|
9798
|
-
dir:
|
|
9745
|
+
dir: path3.join(real(path3.resolve(root, common)), "workit"),
|
|
9799
9746
|
shared: true,
|
|
9800
9747
|
top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
|
|
9801
9748
|
};
|
|
9802
9749
|
}
|
|
9803
9750
|
if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
|
|
9804
|
-
return { dir:
|
|
9751
|
+
return { dir: path3.join(root, ".workit"), shared: false, top: null };
|
|
9805
9752
|
return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
|
|
9806
9753
|
`)[0] || "no output"}); fix the repository (git status should work here)`);
|
|
9807
9754
|
}
|
|
9808
9755
|
var rebasingBranch = (gitDir) => {
|
|
9809
9756
|
for (const state of ["rebase-merge", "rebase-apply"]) {
|
|
9810
9757
|
try {
|
|
9811
|
-
const ref = fs4.readFileSync(
|
|
9758
|
+
const ref = fs4.readFileSync(path3.join(gitDir, state, "head-name"), "utf8").trim();
|
|
9812
9759
|
const match = /^refs\/heads\/(.+)$/u.exec(ref);
|
|
9813
9760
|
if (match)
|
|
9814
9761
|
return match[1];
|
|
@@ -9823,7 +9770,7 @@ function resolveTaskKey(root, location) {
|
|
|
9823
9770
|
if (location.git && !location.git.reftable) {
|
|
9824
9771
|
let head = "";
|
|
9825
9772
|
try {
|
|
9826
|
-
head = fs4.readFileSync(
|
|
9773
|
+
head = fs4.readFileSync(path3.join(location.git.gitDir, "HEAD"), "utf8").trim();
|
|
9827
9774
|
} catch {}
|
|
9828
9775
|
const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
|
|
9829
9776
|
if (ref && ref[1] !== ".invalid")
|
|
@@ -9854,7 +9801,7 @@ function resolveTaskKey(root, location) {
|
|
|
9854
9801
|
return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
|
|
9855
9802
|
}
|
|
9856
9803
|
function checkoutRootOf(root, location) {
|
|
9857
|
-
const resolved =
|
|
9804
|
+
const resolved = path3.resolve(root);
|
|
9858
9805
|
let canonical = resolved;
|
|
9859
9806
|
try {
|
|
9860
9807
|
canonical = fs4.realpathSync(resolved);
|
|
@@ -9885,13 +9832,13 @@ function branchRenames(location) {
|
|
|
9885
9832
|
}
|
|
9886
9833
|
for (const entry of entries)
|
|
9887
9834
|
if (entry.isDirectory())
|
|
9888
|
-
walk(
|
|
9835
|
+
walk(path3.join(dir, entry.name));
|
|
9889
9836
|
else
|
|
9890
|
-
files.push(
|
|
9837
|
+
files.push(path3.join(dir, entry.name));
|
|
9891
9838
|
};
|
|
9892
|
-
walk(
|
|
9839
|
+
walk(path3.join(path3.dirname(location.dir), "logs", "refs", "heads"));
|
|
9893
9840
|
if (location.git)
|
|
9894
|
-
files.push(
|
|
9841
|
+
files.push(path3.join(location.git.gitDir, "logs", "HEAD"));
|
|
9895
9842
|
const seen = new Set;
|
|
9896
9843
|
const renames = [];
|
|
9897
9844
|
for (const file of files) {
|
|
@@ -9926,11 +9873,69 @@ var checkoutSlug = (root) => {
|
|
|
9926
9873
|
let canonical = real(root);
|
|
9927
9874
|
if (process.platform === "win32")
|
|
9928
9875
|
canonical = canonical.toLowerCase();
|
|
9929
|
-
const base =
|
|
9876
|
+
const base = path3.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
|
|
9930
9877
|
return `${base}-${shortHash(canonical)}`;
|
|
9931
9878
|
};
|
|
9932
9879
|
|
|
9880
|
+
// packages/workit-core/src/host-session.ts
|
|
9881
|
+
var HOST_SESSION_VARS = [
|
|
9882
|
+
["OPENCODE_SESSION_ID", () => "opencode"],
|
|
9883
|
+
["PI_SESSION_ID", () => "pi"],
|
|
9884
|
+
[
|
|
9885
|
+
"CODEX_THREAD_ID",
|
|
9886
|
+
(env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
|
|
9887
|
+
]
|
|
9888
|
+
];
|
|
9889
|
+
var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
|
|
9890
|
+
function hostSessionFromEnv(env) {
|
|
9891
|
+
const value = (key) => env[key]?.trim() || null;
|
|
9892
|
+
let inner = null;
|
|
9893
|
+
for (const [key, host] of HOST_SESSION_VARS) {
|
|
9894
|
+
const session = value(key);
|
|
9895
|
+
if (session) {
|
|
9896
|
+
inner = { host: host(env), session };
|
|
9897
|
+
break;
|
|
9898
|
+
}
|
|
9899
|
+
}
|
|
9900
|
+
const declared = value("WORKIT_HOST");
|
|
9901
|
+
if (env.WORKIT_SESSION_ID !== undefined) {
|
|
9902
|
+
const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
|
|
9903
|
+
if (!inherited)
|
|
9904
|
+
return { host: declared, session: value("WORKIT_SESSION_ID") };
|
|
9905
|
+
}
|
|
9906
|
+
return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
|
|
9907
|
+
}
|
|
9908
|
+
var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
|
|
9909
|
+
var SESSION_UNSAFE = /[^A-Za-z0-9_.:@/+-]/g;
|
|
9910
|
+
function childSessionId(lead, child) {
|
|
9911
|
+
const leaf = child.replace(SESSION_UNSAFE, "-").slice(-120);
|
|
9912
|
+
const prefix = lead.replace(SESSION_UNSAFE, "-").slice(0, 127 - leaf.length);
|
|
9913
|
+
return prefix ? `${prefix}:${leaf}` : leaf;
|
|
9914
|
+
}
|
|
9915
|
+
|
|
9916
|
+
// packages/workit-core/src/core/package-root.ts
|
|
9917
|
+
import { existsSync } from "node:fs";
|
|
9918
|
+
import path4 from "node:path";
|
|
9919
|
+
import { fileURLToPath } from "node:url";
|
|
9920
|
+
var packageRoot = () => {
|
|
9921
|
+
let dir = path4.dirname(fileURLToPath(import.meta.url));
|
|
9922
|
+
while (true) {
|
|
9923
|
+
const parent = path4.dirname(dir);
|
|
9924
|
+
if (existsSync(path4.join(dir, "package.json")) || parent === dir)
|
|
9925
|
+
return dir;
|
|
9926
|
+
dir = parent;
|
|
9927
|
+
}
|
|
9928
|
+
};
|
|
9929
|
+
var assetRoot = () => {
|
|
9930
|
+
const root = packageRoot();
|
|
9931
|
+
const assets = path4.join(root, "assets");
|
|
9932
|
+
return existsSync(assets) ? assets : root;
|
|
9933
|
+
};
|
|
9934
|
+
|
|
9933
9935
|
// packages/workit-core/src/core/store-lock.ts
|
|
9936
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
9937
|
+
import fs5 from "node:fs";
|
|
9938
|
+
import { hostname } from "node:os";
|
|
9934
9939
|
var metadataLockSchema = object({
|
|
9935
9940
|
pid: number2().int().nonnegative().safe(),
|
|
9936
9941
|
processStart: string2().nullable(),
|
|
@@ -14601,11 +14606,20 @@ function authorSessions(cwd, rows, branch, range = { base: null, head: null }, s
|
|
|
14601
14606
|
sessions.add(session);
|
|
14602
14607
|
return sessions;
|
|
14603
14608
|
}
|
|
14609
|
+
var derivesFrom = (derived, bare) => !bare.includes(":") && derived.endsWith(`:${bare}`);
|
|
14610
|
+
var authoredBy = (authors, session) => {
|
|
14611
|
+
if (authors.has(session))
|
|
14612
|
+
return true;
|
|
14613
|
+
for (const author of authors)
|
|
14614
|
+
if (derivesFrom(author, session) || derivesFrom(session, author))
|
|
14615
|
+
return true;
|
|
14616
|
+
return false;
|
|
14617
|
+
};
|
|
14604
14618
|
var independenceReasons = (row, authors) => {
|
|
14605
14619
|
const reasons = [];
|
|
14606
14620
|
if (!row.actor.session)
|
|
14607
14621
|
reasons.push("no_session");
|
|
14608
|
-
else if (authors
|
|
14622
|
+
else if (authoredBy(authors, row.actor.session))
|
|
14609
14623
|
reasons.push("author_session");
|
|
14610
14624
|
if (row.self === true && !reasons.includes("no_session"))
|
|
14611
14625
|
reasons.push("self");
|
|
@@ -15686,7 +15700,8 @@ Autonomy contract:
|
|
|
15686
15700
|
satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
|
|
15687
15701
|
- Author is not verifier: your own verdict is \`--self\`, shown as
|
|
15688
15702
|
self-reviewed, never verified. High risk, \`verification: independent\` and
|
|
15689
|
-
merge need a verifier with its own session (
|
|
15703
|
+
merge need a verifier with its own host-derived session (a subagent's
|
|
15704
|
+
\`<lead>:<child>\`, set by the host); never hand-mint one such as \`<lead>-v1\`.
|
|
15690
15705
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
15691
15706
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
15692
15707
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -21309,9 +21324,12 @@ var createV2Lifecycle = (deps) => {
|
|
|
21309
21324
|
expectedWorkspaceRevision: workspace.data.revision
|
|
21310
21325
|
} : null;
|
|
21311
21326
|
};
|
|
21312
|
-
const
|
|
21313
|
-
const
|
|
21314
|
-
|
|
21327
|
+
const currentTaskId = (store) => {
|
|
21328
|
+
const report = store.keyReport();
|
|
21329
|
+
return report.ok ? report.data.bound[0]?.id ?? null : null;
|
|
21330
|
+
};
|
|
21331
|
+
const oldest = (items) => {
|
|
21332
|
+
if (items.length === 0)
|
|
21315
21333
|
return null;
|
|
21316
21334
|
return [...items].toSorted((a, b) => a.entry.recordedAt < b.entry.recordedAt ? -1 : a.entry.recordedAt > b.entry.recordedAt ? 1 : a.entry.id < b.entry.id ? -1 : 1)[0];
|
|
21317
21335
|
};
|
|
@@ -21320,6 +21338,16 @@ var createV2Lifecycle = (deps) => {
|
|
|
21320
21338
|
const cancel = workerId ? `workit_worker {"action":"cancel","workerId":"${workerId}","reason":"launch not observed"}` : 'workit_worker {"action":"cancel","workerId":"<id>","reason":"launch not observed"} for the stuck worker';
|
|
21321
21339
|
return `recovery_required: a previous native subagent launch is unsettled; wait for it to settle, or ${cancel} before launching again`;
|
|
21322
21340
|
};
|
|
21341
|
+
const unboundLaunch = (coordinator) => {
|
|
21342
|
+
const store = new TaskStore(root);
|
|
21343
|
+
const current = currentTaskId(store);
|
|
21344
|
+
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";
|
|
21345
|
+
const listed = store.listTasks();
|
|
21346
|
+
if (!listed.ok)
|
|
21347
|
+
return base;
|
|
21348
|
+
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"}`) : []);
|
|
21349
|
+
return stale.length > 0 ? `${base}. Workers of this coordinator are still open on other tasks; cancel any that are stale: ${stale.join("; ")}` : base;
|
|
21350
|
+
};
|
|
21323
21351
|
const prepareDispatch = (coordinator, callID) => {
|
|
21324
21352
|
if (dispatches.has(coordinator))
|
|
21325
21353
|
return "unsettled";
|
|
@@ -21332,7 +21360,8 @@ var createV2Lifecycle = (deps) => {
|
|
|
21332
21360
|
if (managed.length === 0)
|
|
21333
21361
|
return "unmanaged";
|
|
21334
21362
|
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 })));
|
|
21335
|
-
const
|
|
21363
|
+
const current = currentTaskId(store);
|
|
21364
|
+
const next = oldest(eligible.filter((item) => item.task.id === current));
|
|
21336
21365
|
if (!next)
|
|
21337
21366
|
return "unbound";
|
|
21338
21367
|
dispatches.set(coordinator, {
|
|
@@ -21526,9 +21555,8 @@ var createV2Lifecycle = (deps) => {
|
|
|
21526
21555
|
if (prepared === "unsettled") {
|
|
21527
21556
|
throw new Error(unsettledLaunch(coordinator));
|
|
21528
21557
|
}
|
|
21529
|
-
if (prepared === "unbound")
|
|
21530
|
-
throw new Error(
|
|
21531
|
-
}
|
|
21558
|
+
if (prepared === "unbound")
|
|
21559
|
+
throw new Error(unboundLaunch(coordinator));
|
|
21532
21560
|
} catch (error) {
|
|
21533
21561
|
if (inflight.get(coordinator) === event.id)
|
|
21534
21562
|
inflight.delete(coordinator);
|
|
@@ -21782,6 +21810,24 @@ var evaluateShellPermission = (root, event, workdirs = new Map) => {
|
|
|
21782
21810
|
}
|
|
21783
21811
|
};
|
|
21784
21812
|
|
|
21813
|
+
// packages/workit-opencode/src/v2/child-identity.ts
|
|
21814
|
+
var CHILD_RECORDS = {
|
|
21815
|
+
evidence: new Set(["record"]),
|
|
21816
|
+
finding: new Set(["record"])
|
|
21817
|
+
};
|
|
21818
|
+
var fields = (input) => typeof input === "object" && input !== null ? input : {};
|
|
21819
|
+
var childMayRecord = (family, input) => {
|
|
21820
|
+
const action = fields(input).action;
|
|
21821
|
+
return family !== null && typeof action === "string" && CHILD_RECORDS[family]?.has(action) === true;
|
|
21822
|
+
};
|
|
21823
|
+
var childTaskMissing = (store, input) => {
|
|
21824
|
+
if (fields(input).taskId !== undefined)
|
|
21825
|
+
return false;
|
|
21826
|
+
const report = store.keyReport();
|
|
21827
|
+
return !report.ok || report.data.bound.length === 0;
|
|
21828
|
+
};
|
|
21829
|
+
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`;
|
|
21830
|
+
|
|
21785
21831
|
// packages/workit-opencode/src/v2/shell.ts
|
|
21786
21832
|
import path31 from "node:path";
|
|
21787
21833
|
var commandOf = (input) => {
|
|
@@ -21838,6 +21884,54 @@ ${nudge.text}` };
|
|
|
21838
21884
|
} catch {}
|
|
21839
21885
|
};
|
|
21840
21886
|
|
|
21887
|
+
// packages/workit-opencode/src/v2/shell-session.ts
|
|
21888
|
+
var PENDING_LIMIT = 256;
|
|
21889
|
+
var commandText2 = (input) => {
|
|
21890
|
+
if (input === null || typeof input !== "object")
|
|
21891
|
+
return null;
|
|
21892
|
+
const command = input.command;
|
|
21893
|
+
return typeof command === "string" && command.trim() ? command : null;
|
|
21894
|
+
};
|
|
21895
|
+
var noteShellCall = (pending, event) => {
|
|
21896
|
+
const command = commandText2(event.input);
|
|
21897
|
+
if (!command)
|
|
21898
|
+
return;
|
|
21899
|
+
const sessionID = String(event.sessionID);
|
|
21900
|
+
const queue = pending.get(command) ?? { sessions: [], mixed: false };
|
|
21901
|
+
if (queue.sessions.some((other) => other !== sessionID))
|
|
21902
|
+
queue.mixed = true;
|
|
21903
|
+
queue.sessions.push(sessionID);
|
|
21904
|
+
pending.set(command, queue);
|
|
21905
|
+
if (pending.size > PENDING_LIMIT)
|
|
21906
|
+
pending.delete(pending.keys().next().value);
|
|
21907
|
+
};
|
|
21908
|
+
var settleShellCall = (pending, event) => {
|
|
21909
|
+
const command = commandText2(event.input);
|
|
21910
|
+
const queue = command ? pending.get(command) : undefined;
|
|
21911
|
+
if (!command || !queue)
|
|
21912
|
+
return;
|
|
21913
|
+
const index = queue.sessions.indexOf(String(event.sessionID));
|
|
21914
|
+
if (index >= 0)
|
|
21915
|
+
queue.sessions.splice(index, 1);
|
|
21916
|
+
if (queue.sessions.length === 0)
|
|
21917
|
+
pending.delete(command);
|
|
21918
|
+
};
|
|
21919
|
+
var applyShellSession = async (pending, event, parentOf) => {
|
|
21920
|
+
event.env.WORKIT_HOST = "opencode";
|
|
21921
|
+
delete event.env.WORKIT_SESSION_ID;
|
|
21922
|
+
const queue = pending.get(event.command);
|
|
21923
|
+
if (!queue || queue.sessions.length === 0)
|
|
21924
|
+
return;
|
|
21925
|
+
const sessionID = queue.sessions.shift();
|
|
21926
|
+
if (queue.sessions.length === 0)
|
|
21927
|
+
pending.delete(event.command);
|
|
21928
|
+
if (queue.mixed)
|
|
21929
|
+
return;
|
|
21930
|
+
const parent = await parentOf(sessionID);
|
|
21931
|
+
if (parent)
|
|
21932
|
+
event.env.WORKIT_SESSION_ID = childSessionId(parent, sessionID);
|
|
21933
|
+
};
|
|
21934
|
+
|
|
21841
21935
|
// packages/workit-opencode/src/v2/skills.ts
|
|
21842
21936
|
var hookInput2 = (root, sessionID, subagent) => ({
|
|
21843
21937
|
host: "opencode",
|
|
@@ -22141,9 +22235,17 @@ var setup = async (ctx) => {
|
|
|
22141
22235
|
};
|
|
22142
22236
|
const store = new TaskStore(root);
|
|
22143
22237
|
const workerId = workerIdFor(store, session.id, session.parentID, lifecycle.directChildren);
|
|
22144
|
-
if (session.parentID !== undefined && workerId === null)
|
|
22145
|
-
return resultContent(failure2("permission_denied", "OpenCode child session has no validated Workit worker"));
|
|
22146
22238
|
const family = workitFamilyOf(spec.name);
|
|
22239
|
+
if (session.parentID !== undefined && workerId === null) {
|
|
22240
|
+
const identity = childSessionId(session.parentID, session.id);
|
|
22241
|
+
const lead = await sessionFacts(ctx, session.parentID);
|
|
22242
|
+
const directChild = lead !== null && lead.parentID === undefined && sameWorkspace(root, lead.directory);
|
|
22243
|
+
if (!directChild || family === null || !childMayRecord(family, input))
|
|
22244
|
+
return resultContent(failure2("permission_denied", childDenial(identity)));
|
|
22245
|
+
if (childTaskMissing(store, input))
|
|
22246
|
+
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"));
|
|
22247
|
+
return resultContent(runFamily(family, input, { ...session, id: identity }, store, null));
|
|
22248
|
+
}
|
|
22147
22249
|
if (family !== null)
|
|
22148
22250
|
return resultContent(runFamily(family, input, session, store, workerId));
|
|
22149
22251
|
if (spec.name === "workit_init_apply") {
|
|
@@ -22155,9 +22257,12 @@ var setup = async (ctx) => {
|
|
|
22155
22257
|
}
|
|
22156
22258
|
});
|
|
22157
22259
|
const shellWorkdirs = new Map;
|
|
22260
|
+
const pendingShells = new Map;
|
|
22158
22261
|
await ctx.tool.hook("execute.before", async (event) => {
|
|
22159
|
-
if (event.tool === "shell")
|
|
22262
|
+
if (event.tool === "shell") {
|
|
22263
|
+
noteShellCall(pendingShells, { sessionID: String(event.sessionID), input: event.input });
|
|
22160
22264
|
return observeShellBefore(root, event, shellWorkdirs);
|
|
22265
|
+
}
|
|
22161
22266
|
if (event.tool === "skill")
|
|
22162
22267
|
return observeSkillLoad(root, event);
|
|
22163
22268
|
if (event.tool !== "subagent")
|
|
@@ -22169,9 +22274,17 @@ var setup = async (ctx) => {
|
|
|
22169
22274
|
input: event.input
|
|
22170
22275
|
});
|
|
22171
22276
|
});
|
|
22277
|
+
const shellDomain = ctx.shell;
|
|
22278
|
+
if (shellDomain)
|
|
22279
|
+
await shellDomain.hook("create.before", (event) => applyShellSession(pendingShells, event, async (sessionID) => {
|
|
22280
|
+
const known = lifecycle.directChildren.get(sessionID);
|
|
22281
|
+
return known ?? (await sessionFacts(ctx, sessionID))?.parentID;
|
|
22282
|
+
}));
|
|
22172
22283
|
await ctx.tool.hook("execute.after", async (event) => {
|
|
22173
|
-
if (event.tool === "shell")
|
|
22284
|
+
if (event.tool === "shell") {
|
|
22285
|
+
settleShellCall(pendingShells, { sessionID: String(event.sessionID), input: event.input });
|
|
22174
22286
|
return observeShellResult(root, event);
|
|
22287
|
+
}
|
|
22175
22288
|
if (event.tool !== "subagent")
|
|
22176
22289
|
return;
|
|
22177
22290
|
await lifecycle.executeAfter({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-opencode",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.10.0",
|
|
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
|
"dependencies": {
|
|
42
|
-
"@brainervirus/workit-core": "^8.
|
|
42
|
+
"@brainervirus/workit-core": "^8.10.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@opencode/plugin": "2.0.18"
|