@brainervirus/workit-codex 8.13.1 → 8.14.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/.codex-plugin/plugin.json +1 -1
- package/dist/launch-mcp.js +96 -9
- package/dist/{workit-hook-j1zd509p.js → workit-hook-m4bfzykw.js} +512 -41
- package/dist/workit-hook.js +1 -1
- package/package.json +3 -3
- package/skills/workit-fanout/SKILL.md +4 -4
- package/skills/workit-fanout/references/orchestration.md +4 -2
package/dist/launch-mcp.js
CHANGED
|
@@ -14236,6 +14236,8 @@ var text = string2().check((ctx) => {
|
|
|
14236
14236
|
ctx.issues.push({ code: "custom", input: ctx.value, message: "invalid Unicode", path: [] });
|
|
14237
14237
|
});
|
|
14238
14238
|
var id = text.regex(uuidPattern, "expected lowercase UUID");
|
|
14239
|
+
var idRefPattern = /^[0-9a-f][0-9a-f-]{7,35}$/;
|
|
14240
|
+
var idRef = text.regex(idRefPattern, "expected a UUID or an id prefix of 8+ characters");
|
|
14239
14241
|
var digest = text.regex(digestPattern, "expected lowercase SHA-256 digest");
|
|
14240
14242
|
var revision = text.regex(uuidPattern, "expected UUID revision");
|
|
14241
14243
|
var validUtc = (value) => {
|
|
@@ -14613,7 +14615,7 @@ var workerReportSchema = object({
|
|
|
14613
14615
|
outcome: _enum(["completed", "failed", "cancelled"]),
|
|
14614
14616
|
summary: text,
|
|
14615
14617
|
evidenceIds: array(id),
|
|
14616
|
-
findingIds: array(
|
|
14618
|
+
findingIds: array(idRef)
|
|
14617
14619
|
}).strict();
|
|
14618
14620
|
var workerSchema = object({
|
|
14619
14621
|
assignment: assignmentSchema,
|
|
@@ -14859,7 +14861,7 @@ var findingOperations = {
|
|
|
14859
14861
|
action: literal("resolve"),
|
|
14860
14862
|
...taskId,
|
|
14861
14863
|
expectedRevision: revision.optional(),
|
|
14862
|
-
findingId:
|
|
14864
|
+
findingId: idRef,
|
|
14863
14865
|
disposition: _enum(["open", "fixed", "dismissed", "deferred"]),
|
|
14864
14866
|
reason: text,
|
|
14865
14867
|
evidenceIds: array(id),
|
|
@@ -14900,7 +14902,7 @@ var workerOperations = {
|
|
|
14900
14902
|
...taskId,
|
|
14901
14903
|
expectedRevision: revision.optional(),
|
|
14902
14904
|
expectedWorkspaceRevision: revision.optional(),
|
|
14903
|
-
workerId:
|
|
14905
|
+
workerId: idRef,
|
|
14904
14906
|
report: workerReportSchema
|
|
14905
14907
|
}),
|
|
14906
14908
|
cancel: operation({
|
|
@@ -14908,7 +14910,7 @@ var workerOperations = {
|
|
|
14908
14910
|
...taskId,
|
|
14909
14911
|
...revisions,
|
|
14910
14912
|
expectedWorkspaceRevision: revision.optional(),
|
|
14911
|
-
workerId:
|
|
14913
|
+
workerId: idRef,
|
|
14912
14914
|
reason: text
|
|
14913
14915
|
})
|
|
14914
14916
|
};
|
|
@@ -14958,6 +14960,16 @@ var success = (revisionValue, workspaceRevisionValue, data) => ({
|
|
|
14958
14960
|
data
|
|
14959
14961
|
});
|
|
14960
14962
|
var failure2 = (code, error, details = {}) => ({ ok: false, schemaVersion: 1, code, error, details });
|
|
14963
|
+
function resolveIdRef(kind, ref, ids) {
|
|
14964
|
+
if (ids.includes(ref))
|
|
14965
|
+
return success(null, null, ref);
|
|
14966
|
+
const matches = ids.filter((candidate) => candidate.startsWith(ref));
|
|
14967
|
+
if (matches.length === 1)
|
|
14968
|
+
return success(null, null, matches[0]);
|
|
14969
|
+
if (matches.length === 0)
|
|
14970
|
+
return failure2("not_found", `${kind} not found`);
|
|
14971
|
+
return failure2("invalid_input", `${kind} prefix ${ref} is ambiguous; candidates: ${matches.join(", ")}`);
|
|
14972
|
+
}
|
|
14961
14973
|
function parseOperation(family, input) {
|
|
14962
14974
|
if (typeof input === "object" && input !== null && "schemaVersion" in input && input.schemaVersion !== SCHEMA_VERSION)
|
|
14963
14975
|
return failure2("unsupported_version", "unsupported schema version", { operation: family });
|
|
@@ -17476,20 +17488,26 @@ class TaskStore {
|
|
|
17476
17488
|
return success(null, null, { key: key.data, bound: bound.data });
|
|
17477
17489
|
}
|
|
17478
17490
|
taskForWorker(workerId) {
|
|
17479
|
-
if (typeof workerId !== "string" || !
|
|
17491
|
+
if (typeof workerId !== "string" || !idRefPattern.test(workerId))
|
|
17480
17492
|
return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
|
|
17481
17493
|
const listed = this.listTaskIndex();
|
|
17482
17494
|
if (!listed.ok)
|
|
17483
17495
|
return listed;
|
|
17496
|
+
const owners = new Map;
|
|
17484
17497
|
for (const entry of listed.data) {
|
|
17485
17498
|
if (entry.status === "closed")
|
|
17486
17499
|
continue;
|
|
17487
17500
|
const loaded = this.load(entry.id);
|
|
17488
17501
|
if (!loaded.ok)
|
|
17489
17502
|
return loaded;
|
|
17490
|
-
|
|
17491
|
-
|
|
17503
|
+
for (const worker of loaded.data?.record.workers ?? [])
|
|
17504
|
+
owners.set(worker.id, entry.id);
|
|
17492
17505
|
}
|
|
17506
|
+
const resolved = resolveIdRef("worker", workerId, [...owners.keys()]);
|
|
17507
|
+
if (resolved.ok)
|
|
17508
|
+
return success(null, null, owners.get(resolved.data) ?? null);
|
|
17509
|
+
if (resolved.code !== "not_found")
|
|
17510
|
+
return resolved;
|
|
17493
17511
|
return success(null, null, null);
|
|
17494
17512
|
}
|
|
17495
17513
|
originTaskEntry() {
|
|
@@ -19602,6 +19620,8 @@ var workspaceConfigSchema = object({
|
|
|
19602
19620
|
defaultEndpoint: unknown().optional(),
|
|
19603
19621
|
verification: _enum(["self", "independent"]).optional(),
|
|
19604
19622
|
trailers: boolean2().optional(),
|
|
19623
|
+
liveSessionGuard: boolean2().optional(),
|
|
19624
|
+
fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
19605
19625
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
19606
19626
|
kind,
|
|
19607
19627
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -19919,6 +19939,17 @@ var resolveRuntimeReleaseTracks = (cwd) => {
|
|
|
19919
19939
|
const matched = matchRuntimeWorkspace(cwd);
|
|
19920
19940
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
19921
19941
|
};
|
|
19942
|
+
var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
19943
|
+
var TIER_HOSTS = new Set([
|
|
19944
|
+
"claude_code",
|
|
19945
|
+
"opencode",
|
|
19946
|
+
"codex",
|
|
19947
|
+
"codex_cli",
|
|
19948
|
+
"codex_desktop",
|
|
19949
|
+
"cursor",
|
|
19950
|
+
"pi",
|
|
19951
|
+
"default"
|
|
19952
|
+
]);
|
|
19922
19953
|
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
19923
19954
|
const matched = matchRuntimeWorkspace(cwd);
|
|
19924
19955
|
if (!matched)
|
|
@@ -23194,6 +23225,8 @@ var TASK_SCOPED_ACTIONS = new Set([
|
|
|
23194
23225
|
"worker.cancel",
|
|
23195
23226
|
"state.export"
|
|
23196
23227
|
]);
|
|
23228
|
+
var ONLY_OWN_REPORT = "workers can submit only their own report";
|
|
23229
|
+
var NO_HELPER_CONTROL = "helpers cannot cancel or control workers";
|
|
23197
23230
|
var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
|
|
23198
23231
|
var workerCommand = (taskId, request) => {
|
|
23199
23232
|
const { action, workerId, reason, report } = request;
|
|
@@ -24091,6 +24124,10 @@ class WorkitCore {
|
|
|
24091
24124
|
}
|
|
24092
24125
|
if (helper?.ok)
|
|
24093
24126
|
return failure2("permission_denied", "helpers cannot resolve findings");
|
|
24127
|
+
const findingRef = resolveIdRef("finding", input.findingId, task.data.findings.map((entry) => entry.id));
|
|
24128
|
+
if (!findingRef.ok)
|
|
24129
|
+
return findingRef;
|
|
24130
|
+
input.findingId = findingRef.data;
|
|
24094
24131
|
const finding = task.data.findings.find((entry) => entry.id === input.findingId);
|
|
24095
24132
|
if (!finding)
|
|
24096
24133
|
return failure2("not_found", "finding not found");
|
|
@@ -24242,15 +24279,31 @@ class WorkitCore {
|
|
|
24242
24279
|
return changed;
|
|
24243
24280
|
return success(changed.data.task.revision, changed.data.workspace.revision, changed.data.task.workers.at(-1));
|
|
24244
24281
|
}
|
|
24282
|
+
const workerRef = resolveIdRef("worker", input.workerId, helperId === null ? task.data.workers.map((candidate) => candidate.id) : [helperId]);
|
|
24283
|
+
if (!workerRef.ok)
|
|
24284
|
+
return helperId === null ? workerRef : failure2("permission_denied", input.action === "report" ? ONLY_OWN_REPORT : NO_HELPER_CONTROL);
|
|
24285
|
+
input.workerId = workerRef.data;
|
|
24245
24286
|
const entry = task.data.workers.find((candidate) => candidate.id === input.workerId);
|
|
24246
24287
|
if (!entry)
|
|
24247
24288
|
return failure2("not_found", "worker not found");
|
|
24248
24289
|
if (input.action === "report") {
|
|
24249
24290
|
if (helperId === null || helperId !== input.workerId)
|
|
24250
|
-
return failure2("permission_denied",
|
|
24291
|
+
return failure2("permission_denied", ONLY_OWN_REPORT);
|
|
24251
24292
|
const helper = this.helperEntry(task.data, true, true);
|
|
24252
24293
|
if (!helper.ok)
|
|
24253
24294
|
return helper;
|
|
24295
|
+
const ownFindings = task.data.findings.filter((candidate) => candidate.provenance.workerId === helperId).map((candidate) => candidate.id);
|
|
24296
|
+
const reported = [];
|
|
24297
|
+
for (const findingRef of input.report.findingIds) {
|
|
24298
|
+
const found = resolveIdRef("finding", findingRef, ownFindings);
|
|
24299
|
+
if (found.ok)
|
|
24300
|
+
reported.push(found.data);
|
|
24301
|
+
else if (found.code === "not_found")
|
|
24302
|
+
reported.push(findingRef);
|
|
24303
|
+
else
|
|
24304
|
+
return found;
|
|
24305
|
+
}
|
|
24306
|
+
input.report.findingIds = reported;
|
|
24254
24307
|
const evidence = task.data.evidence.filter((candidate) => input.report.evidenceIds.includes(candidate.id));
|
|
24255
24308
|
const findings = task.data.findings.filter((candidate) => input.report.findingIds.includes(candidate.id));
|
|
24256
24309
|
if (evidence.length !== input.report.evidenceIds.length || findings.length !== input.report.findingIds.length)
|
|
@@ -24278,7 +24331,7 @@ class WorkitCore {
|
|
|
24278
24331
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during report");
|
|
24279
24332
|
}
|
|
24280
24333
|
if (helperId !== null)
|
|
24281
|
-
return failure2("permission_denied",
|
|
24334
|
+
return failure2("permission_denied", NO_HELPER_CONTROL);
|
|
24282
24335
|
if (task.data.status === "closed")
|
|
24283
24336
|
return failure2("invalid_transition", "closed task cannot cancel workers");
|
|
24284
24337
|
if (entry.data.state === "stopped") {
|
|
@@ -28451,10 +28504,44 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
28451
28504
|
var signals = new Map;
|
|
28452
28505
|
var cache = new Map;
|
|
28453
28506
|
var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
28507
|
+
// packages/workit-core/src/core/live-session.ts
|
|
28508
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
28509
|
+
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
28510
|
+
var liveProbe = {
|
|
28511
|
+
platform: process.platform,
|
|
28512
|
+
run: (args) => spawnSync15("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
|
|
28513
|
+
start: (pid) => processStartOf(pid)
|
|
28514
|
+
};
|
|
28515
|
+
var PIDS = new Map;
|
|
28516
|
+
|
|
28454
28517
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
28455
28518
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
28456
28519
|
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
28457
28520
|
var cache2 = new Map;
|
|
28521
|
+
var MUTATING_GIT = new Set([
|
|
28522
|
+
"add",
|
|
28523
|
+
"am",
|
|
28524
|
+
"apply",
|
|
28525
|
+
"checkout",
|
|
28526
|
+
"cherry-pick",
|
|
28527
|
+
"clean",
|
|
28528
|
+
"commit",
|
|
28529
|
+
"merge",
|
|
28530
|
+
"mv",
|
|
28531
|
+
"pull",
|
|
28532
|
+
"rebase",
|
|
28533
|
+
"reset",
|
|
28534
|
+
"restore",
|
|
28535
|
+
"revert",
|
|
28536
|
+
"rm",
|
|
28537
|
+
"stash",
|
|
28538
|
+
"switch"
|
|
28539
|
+
]);
|
|
28540
|
+
var DRY_N = new Set(["add", "clean", "rm", "mv"]);
|
|
28541
|
+
var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
|
|
28542
|
+
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
28543
|
+
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
28544
|
+
var POPD2 = new Set(["popd", "pop-location"]);
|
|
28458
28545
|
// packages/workit-core/src/core/skill-manifests.ts
|
|
28459
28546
|
if (false) {}
|
|
28460
28547
|
|
|
@@ -7966,6 +7966,8 @@ var text = string2().check((ctx) => {
|
|
|
7966
7966
|
ctx.issues.push({ code: "custom", input: ctx.value, message: "invalid Unicode", path: [] });
|
|
7967
7967
|
});
|
|
7968
7968
|
var id = text.regex(uuidPattern, "expected lowercase UUID");
|
|
7969
|
+
var idRefPattern = /^[0-9a-f][0-9a-f-]{7,35}$/;
|
|
7970
|
+
var idRef = text.regex(idRefPattern, "expected a UUID or an id prefix of 8+ characters");
|
|
7969
7971
|
var digest = text.regex(digestPattern, "expected lowercase SHA-256 digest");
|
|
7970
7972
|
var revision = text.regex(uuidPattern, "expected UUID revision");
|
|
7971
7973
|
var validUtc = (value) => {
|
|
@@ -8343,7 +8345,7 @@ var workerReportSchema = object({
|
|
|
8343
8345
|
outcome: _enum(["completed", "failed", "cancelled"]),
|
|
8344
8346
|
summary: text,
|
|
8345
8347
|
evidenceIds: array(id),
|
|
8346
|
-
findingIds: array(
|
|
8348
|
+
findingIds: array(idRef)
|
|
8347
8349
|
}).strict();
|
|
8348
8350
|
var workerSchema = object({
|
|
8349
8351
|
assignment: assignmentSchema,
|
|
@@ -8589,7 +8591,7 @@ var findingOperations = {
|
|
|
8589
8591
|
action: literal("resolve"),
|
|
8590
8592
|
...taskId,
|
|
8591
8593
|
expectedRevision: revision.optional(),
|
|
8592
|
-
findingId:
|
|
8594
|
+
findingId: idRef,
|
|
8593
8595
|
disposition: _enum(["open", "fixed", "dismissed", "deferred"]),
|
|
8594
8596
|
reason: text,
|
|
8595
8597
|
evidenceIds: array(id),
|
|
@@ -8630,7 +8632,7 @@ var workerOperations = {
|
|
|
8630
8632
|
...taskId,
|
|
8631
8633
|
expectedRevision: revision.optional(),
|
|
8632
8634
|
expectedWorkspaceRevision: revision.optional(),
|
|
8633
|
-
workerId:
|
|
8635
|
+
workerId: idRef,
|
|
8634
8636
|
report: workerReportSchema
|
|
8635
8637
|
}),
|
|
8636
8638
|
cancel: operation({
|
|
@@ -8638,7 +8640,7 @@ var workerOperations = {
|
|
|
8638
8640
|
...taskId,
|
|
8639
8641
|
...revisions,
|
|
8640
8642
|
expectedWorkspaceRevision: revision.optional(),
|
|
8641
|
-
workerId:
|
|
8643
|
+
workerId: idRef,
|
|
8642
8644
|
reason: text
|
|
8643
8645
|
})
|
|
8644
8646
|
};
|
|
@@ -8688,6 +8690,16 @@ var success = (revisionValue, workspaceRevisionValue, data) => ({
|
|
|
8688
8690
|
data
|
|
8689
8691
|
});
|
|
8690
8692
|
var failure2 = (code, error, details = {}) => ({ ok: false, schemaVersion: 1, code, error, details });
|
|
8693
|
+
function resolveIdRef(kind, ref, ids) {
|
|
8694
|
+
if (ids.includes(ref))
|
|
8695
|
+
return success(null, null, ref);
|
|
8696
|
+
const matches = ids.filter((candidate) => candidate.startsWith(ref));
|
|
8697
|
+
if (matches.length === 1)
|
|
8698
|
+
return success(null, null, matches[0]);
|
|
8699
|
+
if (matches.length === 0)
|
|
8700
|
+
return failure2("not_found", `${kind} not found`);
|
|
8701
|
+
return failure2("invalid_input", `${kind} prefix ${ref} is ambiguous; candidates: ${matches.join(", ")}`);
|
|
8702
|
+
}
|
|
8691
8703
|
function parseOperation(family, input) {
|
|
8692
8704
|
if (typeof input === "object" && input !== null && "schemaVersion" in input && input.schemaVersion !== SCHEMA_VERSION)
|
|
8693
8705
|
return failure2("unsupported_version", "unsupported schema version", { operation: family });
|
|
@@ -10937,20 +10949,26 @@ class TaskStore {
|
|
|
10937
10949
|
return success(null, null, { key: key.data, bound: bound.data });
|
|
10938
10950
|
}
|
|
10939
10951
|
taskForWorker(workerId) {
|
|
10940
|
-
if (typeof workerId !== "string" || !
|
|
10952
|
+
if (typeof workerId !== "string" || !idRefPattern.test(workerId))
|
|
10941
10953
|
return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
|
|
10942
10954
|
const listed = this.listTaskIndex();
|
|
10943
10955
|
if (!listed.ok)
|
|
10944
10956
|
return listed;
|
|
10957
|
+
const owners = new Map;
|
|
10945
10958
|
for (const entry of listed.data) {
|
|
10946
10959
|
if (entry.status === "closed")
|
|
10947
10960
|
continue;
|
|
10948
10961
|
const loaded = this.load(entry.id);
|
|
10949
10962
|
if (!loaded.ok)
|
|
10950
10963
|
return loaded;
|
|
10951
|
-
|
|
10952
|
-
|
|
10964
|
+
for (const worker of loaded.data?.record.workers ?? [])
|
|
10965
|
+
owners.set(worker.id, entry.id);
|
|
10953
10966
|
}
|
|
10967
|
+
const resolved = resolveIdRef("worker", workerId, [...owners.keys()]);
|
|
10968
|
+
if (resolved.ok)
|
|
10969
|
+
return success(null, null, owners.get(resolved.data) ?? null);
|
|
10970
|
+
if (resolved.code !== "not_found")
|
|
10971
|
+
return resolved;
|
|
10954
10972
|
return success(null, null, null);
|
|
10955
10973
|
}
|
|
10956
10974
|
originTaskEntry() {
|
|
@@ -12672,6 +12690,8 @@ var workspaceConfigSchema = object({
|
|
|
12672
12690
|
defaultEndpoint: unknown().optional(),
|
|
12673
12691
|
verification: _enum(["self", "independent"]).optional(),
|
|
12674
12692
|
trailers: boolean2().optional(),
|
|
12693
|
+
liveSessionGuard: boolean2().optional(),
|
|
12694
|
+
fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
12675
12695
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
12676
12696
|
kind,
|
|
12677
12697
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -12989,24 +13009,39 @@ var resolveRuntimeReleaseTracks = (cwd) => {
|
|
|
12989
13009
|
const matched = matchRuntimeWorkspace(cwd);
|
|
12990
13010
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
12991
13011
|
};
|
|
12992
|
-
var
|
|
13012
|
+
var inheritedSetting = (cwd, dir, has) => {
|
|
12993
13013
|
let matched;
|
|
12994
13014
|
try {
|
|
12995
13015
|
matched = matchRuntimeWorkspace(cwd, dir);
|
|
12996
13016
|
} catch {
|
|
12997
|
-
return
|
|
13017
|
+
return null;
|
|
12998
13018
|
}
|
|
12999
13019
|
if (!matched)
|
|
13000
|
-
return
|
|
13020
|
+
return null;
|
|
13001
13021
|
const { selected, matches } = matched;
|
|
13002
13022
|
const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
13003
|
-
|
|
13023
|
+
return { workspace: selected.name, source: [selected, ...enclosing].find(has) ?? null };
|
|
13024
|
+
};
|
|
13025
|
+
var workspaceTrailers = (cwd, dir) => {
|
|
13026
|
+
const found = inheritedSetting(cwd, dir, (entry) => typeof entry.trailers === "boolean");
|
|
13004
13027
|
return {
|
|
13005
|
-
enabled: source?.trailers !== false,
|
|
13006
|
-
workspace:
|
|
13007
|
-
from: source?.name ?? null
|
|
13028
|
+
enabled: found?.source?.trailers !== false,
|
|
13029
|
+
workspace: found?.workspace ?? null,
|
|
13030
|
+
from: found?.source?.name ?? null
|
|
13008
13031
|
};
|
|
13009
13032
|
};
|
|
13033
|
+
var workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false;
|
|
13034
|
+
var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
13035
|
+
var TIER_HOSTS = new Set([
|
|
13036
|
+
"claude_code",
|
|
13037
|
+
"opencode",
|
|
13038
|
+
"codex",
|
|
13039
|
+
"codex_cli",
|
|
13040
|
+
"codex_desktop",
|
|
13041
|
+
"cursor",
|
|
13042
|
+
"pi",
|
|
13043
|
+
"default"
|
|
13044
|
+
]);
|
|
13010
13045
|
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
13011
13046
|
const matched = matchRuntimeWorkspace(cwd);
|
|
13012
13047
|
if (!matched)
|
|
@@ -16277,6 +16312,8 @@ var TASK_SCOPED_ACTIONS = new Set([
|
|
|
16277
16312
|
"worker.cancel",
|
|
16278
16313
|
"state.export"
|
|
16279
16314
|
]);
|
|
16315
|
+
var ONLY_OWN_REPORT = "workers can submit only their own report";
|
|
16316
|
+
var NO_HELPER_CONTROL = "helpers cannot cancel or control workers";
|
|
16280
16317
|
var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
|
|
16281
16318
|
var workerCommand = (taskId, request) => {
|
|
16282
16319
|
const { action, workerId, reason, report } = request;
|
|
@@ -17174,6 +17211,10 @@ class WorkitCore {
|
|
|
17174
17211
|
}
|
|
17175
17212
|
if (helper?.ok)
|
|
17176
17213
|
return failure2("permission_denied", "helpers cannot resolve findings");
|
|
17214
|
+
const findingRef = resolveIdRef("finding", input.findingId, task.data.findings.map((entry) => entry.id));
|
|
17215
|
+
if (!findingRef.ok)
|
|
17216
|
+
return findingRef;
|
|
17217
|
+
input.findingId = findingRef.data;
|
|
17177
17218
|
const finding = task.data.findings.find((entry) => entry.id === input.findingId);
|
|
17178
17219
|
if (!finding)
|
|
17179
17220
|
return failure2("not_found", "finding not found");
|
|
@@ -17325,15 +17366,31 @@ class WorkitCore {
|
|
|
17325
17366
|
return changed;
|
|
17326
17367
|
return success(changed.data.task.revision, changed.data.workspace.revision, changed.data.task.workers.at(-1));
|
|
17327
17368
|
}
|
|
17369
|
+
const workerRef = resolveIdRef("worker", input.workerId, helperId === null ? task.data.workers.map((candidate) => candidate.id) : [helperId]);
|
|
17370
|
+
if (!workerRef.ok)
|
|
17371
|
+
return helperId === null ? workerRef : failure2("permission_denied", input.action === "report" ? ONLY_OWN_REPORT : NO_HELPER_CONTROL);
|
|
17372
|
+
input.workerId = workerRef.data;
|
|
17328
17373
|
const entry = task.data.workers.find((candidate) => candidate.id === input.workerId);
|
|
17329
17374
|
if (!entry)
|
|
17330
17375
|
return failure2("not_found", "worker not found");
|
|
17331
17376
|
if (input.action === "report") {
|
|
17332
17377
|
if (helperId === null || helperId !== input.workerId)
|
|
17333
|
-
return failure2("permission_denied",
|
|
17378
|
+
return failure2("permission_denied", ONLY_OWN_REPORT);
|
|
17334
17379
|
const helper = this.helperEntry(task.data, true, true);
|
|
17335
17380
|
if (!helper.ok)
|
|
17336
17381
|
return helper;
|
|
17382
|
+
const ownFindings = task.data.findings.filter((candidate) => candidate.provenance.workerId === helperId).map((candidate) => candidate.id);
|
|
17383
|
+
const reported = [];
|
|
17384
|
+
for (const findingRef of input.report.findingIds) {
|
|
17385
|
+
const found = resolveIdRef("finding", findingRef, ownFindings);
|
|
17386
|
+
if (found.ok)
|
|
17387
|
+
reported.push(found.data);
|
|
17388
|
+
else if (found.code === "not_found")
|
|
17389
|
+
reported.push(findingRef);
|
|
17390
|
+
else
|
|
17391
|
+
return found;
|
|
17392
|
+
}
|
|
17393
|
+
input.report.findingIds = reported;
|
|
17337
17394
|
const evidence = task.data.evidence.filter((candidate) => input.report.evidenceIds.includes(candidate.id));
|
|
17338
17395
|
const findings = task.data.findings.filter((candidate) => input.report.findingIds.includes(candidate.id));
|
|
17339
17396
|
if (evidence.length !== input.report.evidenceIds.length || findings.length !== input.report.findingIds.length)
|
|
@@ -17361,7 +17418,7 @@ class WorkitCore {
|
|
|
17361
17418
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during report");
|
|
17362
17419
|
}
|
|
17363
17420
|
if (helperId !== null)
|
|
17364
|
-
return failure2("permission_denied",
|
|
17421
|
+
return failure2("permission_denied", NO_HELPER_CONTROL);
|
|
17365
17422
|
if (task.data.status === "closed")
|
|
17366
17423
|
return failure2("invalid_transition", "closed task cannot cancel workers");
|
|
17367
17424
|
if (entry.data.state === "stopped") {
|
|
@@ -20218,27 +20275,231 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
20218
20275
|
};
|
|
20219
20276
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
20220
20277
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
20278
|
+
import os7 from "node:os";
|
|
20279
|
+
import path23 from "node:path";
|
|
20280
|
+
|
|
20281
|
+
// packages/workit-core/src/core/live-session.ts
|
|
20282
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
20283
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
20284
|
+
import fs17 from "node:fs";
|
|
20285
|
+
import os6 from "node:os";
|
|
20221
20286
|
import path22 from "node:path";
|
|
20287
|
+
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
20288
|
+
var HEARTBEAT_EVERY_MS = 30000;
|
|
20289
|
+
var liveProbe = {
|
|
20290
|
+
platform: process.platform,
|
|
20291
|
+
run: (args) => spawnSync13("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
|
|
20292
|
+
start: (pid) => processStartOf(pid)
|
|
20293
|
+
};
|
|
20294
|
+
var leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session;
|
|
20295
|
+
var familyOf = (host) => host.replace(/_(?:cli|desktop)$/u, "");
|
|
20296
|
+
var processAlive = (pid) => {
|
|
20297
|
+
try {
|
|
20298
|
+
process.kill(pid, 0);
|
|
20299
|
+
return true;
|
|
20300
|
+
} catch (error) {
|
|
20301
|
+
return error.code === "EPERM";
|
|
20302
|
+
}
|
|
20303
|
+
};
|
|
20304
|
+
var processInfo = (pid) => {
|
|
20305
|
+
try {
|
|
20306
|
+
if (liveProbe.platform === "linux") {
|
|
20307
|
+
const stat = fs17.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
20308
|
+
const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
|
|
20309
|
+
const command = fs17.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
|
|
20310
|
+
return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
|
|
20311
|
+
}
|
|
20312
|
+
if (liveProbe.platform === "win32")
|
|
20313
|
+
return null;
|
|
20314
|
+
const match = /^\s*(\d+)\s+(.*)$/su.exec(liveProbe.run(["-o", "ppid=,command=", "-p", String(pid)]));
|
|
20315
|
+
return match ? { ppid: Number(match[1]), command: match[2].trim() } : null;
|
|
20316
|
+
} catch {
|
|
20317
|
+
return null;
|
|
20318
|
+
}
|
|
20319
|
+
};
|
|
20320
|
+
var WRAPPER = /^(?:\S*\/)?(?:sh|bash|dash|zsh|ash|ksh|fish|env|timeout|npx|npm|bunx|cmd\.exe|pwsh|powershell)(?:\s|$)|launch\.mjs|npx-cli\.js/u;
|
|
20321
|
+
var PIDS = new Map;
|
|
20322
|
+
var hostPid = (transport) => {
|
|
20323
|
+
if (transport !== "hook-process")
|
|
20324
|
+
return process.pid;
|
|
20325
|
+
const key = `${liveProbe.platform}\x00${process.ppid}`;
|
|
20326
|
+
if (PIDS.has(key))
|
|
20327
|
+
return PIDS.get(key) ?? null;
|
|
20328
|
+
if (liveProbe.platform === "win32") {
|
|
20329
|
+
PIDS.set(key, null);
|
|
20330
|
+
return null;
|
|
20331
|
+
}
|
|
20332
|
+
let pid = process.ppid;
|
|
20333
|
+
for (let hop = 0;hop < 4; hop++) {
|
|
20334
|
+
const info = processInfo(pid);
|
|
20335
|
+
if (!info || !WRAPPER.test(info.command) || info.ppid <= 1)
|
|
20336
|
+
break;
|
|
20337
|
+
pid = info.ppid;
|
|
20338
|
+
}
|
|
20339
|
+
PIDS.set(key, pid);
|
|
20340
|
+
return pid;
|
|
20341
|
+
};
|
|
20342
|
+
var sessionSafe = (value) => value.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-");
|
|
20343
|
+
var liveOwner = (input, transport, env = process.env) => {
|
|
20344
|
+
const session = input.session.id?.trim();
|
|
20345
|
+
if (!session)
|
|
20346
|
+
return null;
|
|
20347
|
+
const leads = new Set([leadOf(sessionSafe(session))]);
|
|
20348
|
+
if (input.session.parentId)
|
|
20349
|
+
leads.add(leadOf(sessionSafe(input.session.parentId)));
|
|
20350
|
+
if (env.WORKIT_PI_WORKER_SESSION && env.WORKIT_SESSION_ID)
|
|
20351
|
+
leads.add(leadOf(sessionSafe(env.WORKIT_SESSION_ID)));
|
|
20352
|
+
return {
|
|
20353
|
+
host: input.host,
|
|
20354
|
+
session: sessionSafe(session),
|
|
20355
|
+
leads: [...leads],
|
|
20356
|
+
pid: () => hostPid(transport)
|
|
20357
|
+
};
|
|
20358
|
+
};
|
|
20359
|
+
var checkoutOf = (target) => {
|
|
20360
|
+
let dir = path22.resolve(target);
|
|
20361
|
+
while (!fs17.existsSync(dir) && path22.dirname(dir) !== dir)
|
|
20362
|
+
dir = path22.dirname(dir);
|
|
20363
|
+
if (fs17.existsSync(dir) && !fs17.statSync(dir).isDirectory())
|
|
20364
|
+
dir = path22.dirname(dir);
|
|
20365
|
+
const parts = dir.split(path22.sep);
|
|
20366
|
+
const dotGit = parts.lastIndexOf(".git");
|
|
20367
|
+
if (dotGit > 0)
|
|
20368
|
+
dir = parts.slice(0, dotGit).join(path22.sep) || path22.sep;
|
|
20369
|
+
const location = resolveStore(dir);
|
|
20370
|
+
if (location instanceof Error || !location.shared || !location.top)
|
|
20371
|
+
return null;
|
|
20372
|
+
let top = location.top;
|
|
20373
|
+
try {
|
|
20374
|
+
top = fs17.realpathSync.native(top);
|
|
20375
|
+
} catch {}
|
|
20376
|
+
return top;
|
|
20377
|
+
};
|
|
20378
|
+
var markerFile2 = (top) => path22.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
|
|
20379
|
+
var checkoutKey = (top, platform = process.platform) => {
|
|
20380
|
+
let key = path22.resolve(top);
|
|
20381
|
+
try {
|
|
20382
|
+
key = fs17.realpathSync.native(key);
|
|
20383
|
+
} catch {}
|
|
20384
|
+
return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
|
|
20385
|
+
};
|
|
20386
|
+
var readMarker = (file) => {
|
|
20387
|
+
try {
|
|
20388
|
+
const value = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
20389
|
+
if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
|
|
20390
|
+
return null;
|
|
20391
|
+
return {
|
|
20392
|
+
version: 1,
|
|
20393
|
+
session: value.session,
|
|
20394
|
+
leads: Array.isArray(value.leads) ? value.leads.filter((item) => typeof item === "string") : [],
|
|
20395
|
+
host: value.host,
|
|
20396
|
+
pid: typeof value.pid === "number" ? value.pid : null,
|
|
20397
|
+
pidStart: typeof value.pidStart === "string" ? value.pidStart : null,
|
|
20398
|
+
machine: typeof value.machine === "string" ? value.machine : "",
|
|
20399
|
+
checkout: typeof value.checkout === "string" ? value.checkout : "",
|
|
20400
|
+
heartbeatAt: value.heartbeatAt
|
|
20401
|
+
};
|
|
20402
|
+
} catch {
|
|
20403
|
+
return null;
|
|
20404
|
+
}
|
|
20405
|
+
};
|
|
20406
|
+
var ageOf2 = (marker, now) => now.getTime() - Date.parse(marker.heartbeatAt);
|
|
20407
|
+
var isLive = (marker, deps) => {
|
|
20408
|
+
const age = ageOf2(marker, deps.now ?? new Date);
|
|
20409
|
+
if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
|
|
20410
|
+
return false;
|
|
20411
|
+
if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
|
|
20412
|
+
return true;
|
|
20413
|
+
if (!(deps.alive ?? processAlive)(marker.pid))
|
|
20414
|
+
return false;
|
|
20415
|
+
if (marker.pidStart === null)
|
|
20416
|
+
return true;
|
|
20417
|
+
const start = liveProbe.start(marker.pid);
|
|
20418
|
+
return start === null || start === marker.pidStart;
|
|
20419
|
+
};
|
|
20420
|
+
var sameLead = (marker, owner) => marker.session === owner.session || owner.leads.some((lead) => lead === marker.session || marker.leads.includes(lead));
|
|
20421
|
+
var sameOwner = (marker, owner, machine) => {
|
|
20422
|
+
if (sameLead(marker, owner))
|
|
20423
|
+
return true;
|
|
20424
|
+
if (marker.machine !== machine)
|
|
20425
|
+
return false;
|
|
20426
|
+
const pid = owner.pid();
|
|
20427
|
+
if (pid === null || marker.pid === null || marker.pidStart === null)
|
|
20428
|
+
return familyOf(marker.host) === familyOf(owner.host);
|
|
20429
|
+
return marker.pid === pid;
|
|
20430
|
+
};
|
|
20431
|
+
var liveConflict = (target, owner, deps = {}) => {
|
|
20432
|
+
try {
|
|
20433
|
+
const checkout = checkoutOf(target);
|
|
20434
|
+
if (!checkout)
|
|
20435
|
+
return null;
|
|
20436
|
+
const file = markerFile2(checkout);
|
|
20437
|
+
const marker = readMarker(file);
|
|
20438
|
+
if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
|
|
20439
|
+
return null;
|
|
20440
|
+
if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
|
|
20441
|
+
return null;
|
|
20442
|
+
return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
|
|
20443
|
+
} catch {
|
|
20444
|
+
return null;
|
|
20445
|
+
}
|
|
20446
|
+
};
|
|
20447
|
+
var claimLiveSession = (target, owner, deps = {}) => {
|
|
20448
|
+
try {
|
|
20449
|
+
const checkout = checkoutOf(target);
|
|
20450
|
+
if (!checkout)
|
|
20451
|
+
return;
|
|
20452
|
+
const file = markerFile2(checkout);
|
|
20453
|
+
const now = deps.now ?? new Date;
|
|
20454
|
+
const machine = deps.machine ?? os6.hostname();
|
|
20455
|
+
const current = readMarker(file);
|
|
20456
|
+
if (current) {
|
|
20457
|
+
const age = ageOf2(current, now);
|
|
20458
|
+
if (sameLead(current, owner) && age >= 0 && age < HEARTBEAT_EVERY_MS)
|
|
20459
|
+
return;
|
|
20460
|
+
if (!sameLead(current, owner) && isLive(current, deps) && !sameOwner(current, owner, machine))
|
|
20461
|
+
return;
|
|
20462
|
+
}
|
|
20463
|
+
const pid = owner.pid();
|
|
20464
|
+
const marker = {
|
|
20465
|
+
version: 1,
|
|
20466
|
+
session: owner.session,
|
|
20467
|
+
leads: owner.leads,
|
|
20468
|
+
host: owner.host,
|
|
20469
|
+
pid,
|
|
20470
|
+
pidStart: pid === null ? null : liveProbe.start(pid),
|
|
20471
|
+
machine,
|
|
20472
|
+
checkout,
|
|
20473
|
+
heartbeatAt: now.toISOString()
|
|
20474
|
+
};
|
|
20475
|
+
fs17.mkdirSync(path22.dirname(file), { recursive: true });
|
|
20476
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
20477
|
+
fs17.writeFileSync(temp, JSON.stringify(marker));
|
|
20478
|
+
fs17.renameSync(temp, file);
|
|
20479
|
+
} catch {}
|
|
20480
|
+
};
|
|
20481
|
+
|
|
20482
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
20222
20483
|
var NONE3 = { kind: "none" };
|
|
20223
20484
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
20224
20485
|
var canonical2 = (file) => {
|
|
20225
20486
|
let rest = "";
|
|
20226
|
-
for (let dir =
|
|
20487
|
+
for (let dir = path23.resolve(file);; dir = path23.dirname(dir)) {
|
|
20227
20488
|
try {
|
|
20228
|
-
return
|
|
20489
|
+
return path23.join(realpathSync5(dir), rest);
|
|
20229
20490
|
} catch {
|
|
20230
|
-
if (
|
|
20231
|
-
return
|
|
20232
|
-
rest = rest ?
|
|
20491
|
+
if (path23.dirname(dir) === dir)
|
|
20492
|
+
return path23.resolve(file);
|
|
20493
|
+
rest = rest ? path23.join(path23.basename(dir), rest) : path23.basename(dir);
|
|
20233
20494
|
}
|
|
20234
20495
|
}
|
|
20235
20496
|
};
|
|
20236
20497
|
var exempt = (cwd, root, file, plans) => {
|
|
20237
20498
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
20238
|
-
const relative =
|
|
20239
|
-
if (relative.startsWith("..") ||
|
|
20499
|
+
const relative = path23.relative(fold(canonical2(root)), fold(canonical2(path23.resolve(cwd, file))));
|
|
20500
|
+
if (relative.startsWith("..") || path23.isAbsolute(relative))
|
|
20240
20501
|
return true;
|
|
20241
|
-
const posix = relative.split(
|
|
20502
|
+
const posix = relative.split(path23.sep).join("/");
|
|
20242
20503
|
const segments = posix.split("/");
|
|
20243
20504
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
20244
20505
|
};
|
|
@@ -20336,7 +20597,7 @@ var shellWrites = (command) => {
|
|
|
20336
20597
|
return { writes: true, targets: unknown ? null : targets };
|
|
20337
20598
|
};
|
|
20338
20599
|
var cache2 = new Map;
|
|
20339
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
20600
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path23.resolve(root, file)) ?? "-").join("\x00");
|
|
20340
20601
|
var gateFor = (cwd) => {
|
|
20341
20602
|
const store = new TaskStore(cwd);
|
|
20342
20603
|
const listed = store.listTaskIndex();
|
|
@@ -20374,7 +20635,218 @@ var gateFor = (cwd) => {
|
|
|
20374
20635
|
cache2.delete(cache2.keys().next().value);
|
|
20375
20636
|
return gate;
|
|
20376
20637
|
};
|
|
20377
|
-
|
|
20638
|
+
var MUTATING_GIT = new Set([
|
|
20639
|
+
"add",
|
|
20640
|
+
"am",
|
|
20641
|
+
"apply",
|
|
20642
|
+
"checkout",
|
|
20643
|
+
"cherry-pick",
|
|
20644
|
+
"clean",
|
|
20645
|
+
"commit",
|
|
20646
|
+
"merge",
|
|
20647
|
+
"mv",
|
|
20648
|
+
"pull",
|
|
20649
|
+
"rebase",
|
|
20650
|
+
"reset",
|
|
20651
|
+
"restore",
|
|
20652
|
+
"revert",
|
|
20653
|
+
"rm",
|
|
20654
|
+
"stash",
|
|
20655
|
+
"switch"
|
|
20656
|
+
]);
|
|
20657
|
+
var DRY_N = new Set(["add", "clean", "rm", "mv"]);
|
|
20658
|
+
var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
|
|
20659
|
+
var DIR_TARGET = ".workit-live-target";
|
|
20660
|
+
var OVERRIDE = "WORKIT_LIVE_SESSION=override";
|
|
20661
|
+
var inert = (args, sub) => args.some((arg) => arg === "--help" || arg === "-h" || arg === "--dry-run" || DRY_N.has(sub) && /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg) && !arg.startsWith("--"));
|
|
20662
|
+
var gitMutationDirs = (cwd, command, dialect) => {
|
|
20663
|
+
let invocations;
|
|
20664
|
+
try {
|
|
20665
|
+
invocations = rawInvocations(command, dialect);
|
|
20666
|
+
} catch {
|
|
20667
|
+
return [];
|
|
20668
|
+
}
|
|
20669
|
+
return invocations.flatMap((invocation) => {
|
|
20670
|
+
if (invocation.tool !== "git" || invocation.dir === undefined)
|
|
20671
|
+
return [];
|
|
20672
|
+
const at = invocation.args.findIndex((arg) => !arg.startsWith("-"));
|
|
20673
|
+
const sub = invocation.args[at] ?? "";
|
|
20674
|
+
const rest = invocation.args.slice(at + 1);
|
|
20675
|
+
if (!MUTATING_GIT.has(sub) || inert(rest, sub))
|
|
20676
|
+
return [];
|
|
20677
|
+
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
20678
|
+
return [];
|
|
20679
|
+
return [path23.resolve(cwd, invocation.dir ?? ".")];
|
|
20680
|
+
});
|
|
20681
|
+
};
|
|
20682
|
+
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
20683
|
+
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
20684
|
+
var POPD2 = new Set(["popd", "pop-location"]);
|
|
20685
|
+
var cdInto = (dir, words) => {
|
|
20686
|
+
const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
20687
|
+
if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
|
|
20688
|
+
return;
|
|
20689
|
+
return target === undefined ? os7.homedir() : path23.resolve(dir, target);
|
|
20690
|
+
};
|
|
20691
|
+
var quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
20692
|
+
var segmentTargets = (cwd, command, dialect) => {
|
|
20693
|
+
const targets = [];
|
|
20694
|
+
let dir = cwd;
|
|
20695
|
+
let stack = [];
|
|
20696
|
+
const groups = [];
|
|
20697
|
+
const at = (file) => path23.isAbsolute(file) ? file : dir === undefined ? null : path23.resolve(dir, file);
|
|
20698
|
+
for (const segment of segmentsOf(command, dialect)) {
|
|
20699
|
+
if (segment.group === "open") {
|
|
20700
|
+
groups.push({ dir, stack: [...stack] });
|
|
20701
|
+
continue;
|
|
20702
|
+
}
|
|
20703
|
+
if (segment.group === "close") {
|
|
20704
|
+
const saved = groups.pop();
|
|
20705
|
+
if (saved)
|
|
20706
|
+
({ dir, stack } = saved);
|
|
20707
|
+
continue;
|
|
20708
|
+
}
|
|
20709
|
+
const lead = segment.words.findIndex((word) => !/^\w+=/.test(word));
|
|
20710
|
+
const words = lead < 0 ? [] : segment.words.slice(lead);
|
|
20711
|
+
const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
20712
|
+
if (CD2.has(name)) {
|
|
20713
|
+
dir = cdInto(dir, words);
|
|
20714
|
+
continue;
|
|
20715
|
+
}
|
|
20716
|
+
if (PUSHD2.has(name)) {
|
|
20717
|
+
stack.push(dir);
|
|
20718
|
+
dir = cdInto(dir, words);
|
|
20719
|
+
continue;
|
|
20720
|
+
}
|
|
20721
|
+
if (POPD2.has(name)) {
|
|
20722
|
+
dir = stack.length ? stack.pop() : undefined;
|
|
20723
|
+
continue;
|
|
20724
|
+
}
|
|
20725
|
+
for (const file of segment.redirects)
|
|
20726
|
+
if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
|
|
20727
|
+
const resolved = at(file);
|
|
20728
|
+
if (resolved)
|
|
20729
|
+
targets.push(resolved);
|
|
20730
|
+
}
|
|
20731
|
+
const edits = editsOf(segment.words);
|
|
20732
|
+
if (edits === null) {
|
|
20733
|
+
if (dir !== undefined)
|
|
20734
|
+
targets.push(path23.join(dir, DIR_TARGET));
|
|
20735
|
+
} else
|
|
20736
|
+
for (const file of edits) {
|
|
20737
|
+
const resolved = at(file);
|
|
20738
|
+
if (resolved)
|
|
20739
|
+
targets.push(resolved);
|
|
20740
|
+
}
|
|
20741
|
+
if (dir !== undefined && workitInvocations(segment.words.map(quoteWord).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
|
|
20742
|
+
targets.push(path23.join(dir, DIR_TARGET));
|
|
20743
|
+
}
|
|
20744
|
+
return targets;
|
|
20745
|
+
};
|
|
20746
|
+
var overridden = (command, dialect) => {
|
|
20747
|
+
const segments = segmentsOf(command, dialect);
|
|
20748
|
+
if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
|
|
20749
|
+
return false;
|
|
20750
|
+
const words = segments[0].words;
|
|
20751
|
+
const lead = words.findIndex((word) => !/^\w+=/.test(word));
|
|
20752
|
+
return lead > 0 && words.slice(0, lead).includes(OVERRIDE);
|
|
20753
|
+
};
|
|
20754
|
+
var HOST_LABELS = {
|
|
20755
|
+
claude_code: "Claude Code",
|
|
20756
|
+
opencode: "OpenCode",
|
|
20757
|
+
codex_cli: "Codex CLI",
|
|
20758
|
+
codex_desktop: "Codex Desktop",
|
|
20759
|
+
cursor: "Cursor",
|
|
20760
|
+
pi: "Pi"
|
|
20761
|
+
};
|
|
20762
|
+
var ageText = (ms) => {
|
|
20763
|
+
const seconds = Math.max(0, Math.round(ms / 1000));
|
|
20764
|
+
return seconds < 120 ? `${seconds}s` : `${Math.round(seconds / 60)} min`;
|
|
20765
|
+
};
|
|
20766
|
+
var liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
20767
|
+
const unblock = `Work in your own worktree (\`git worktree add <path> -b <branch>\`, or \`workit fanout worktree create <slice>\`). Only if the user confirms that session is finished: release it with \`rm '${file}'\`, or prefix one command with ${OVERRIDE}.`;
|
|
20768
|
+
return {
|
|
20769
|
+
kind: "deny",
|
|
20770
|
+
reason: `workit live-session guard: ${checkout} belongs to another live agent session, ${HOST_LABELS[marker.host] ?? marker.host} session ${marker.session}${marker.pid === null ? "" : ` (pid ${marker.pid})`}, last active ${ageText(ageMs)} ago. Do not edit a checkout another session is working in; read-only commands stay allowed. ${unblock}`,
|
|
20771
|
+
unblock
|
|
20772
|
+
};
|
|
20773
|
+
};
|
|
20774
|
+
var guarded = (targets) => {
|
|
20775
|
+
const seen = new Set;
|
|
20776
|
+
return targets.filter((target) => {
|
|
20777
|
+
const dir = path23.dirname(target);
|
|
20778
|
+
if (seen.has(dir))
|
|
20779
|
+
return false;
|
|
20780
|
+
seen.add(dir);
|
|
20781
|
+
return true;
|
|
20782
|
+
});
|
|
20783
|
+
};
|
|
20784
|
+
var liveGate = (owner, targets, deps) => {
|
|
20785
|
+
try {
|
|
20786
|
+
for (const target of guarded(targets)) {
|
|
20787
|
+
const conflict = liveConflict(target, owner, deps);
|
|
20788
|
+
if (conflict && workspaceLiveSessionGuard(conflict.checkout))
|
|
20789
|
+
return liveDeny(conflict);
|
|
20790
|
+
}
|
|
20791
|
+
} catch {}
|
|
20792
|
+
return NONE3;
|
|
20793
|
+
};
|
|
20794
|
+
var claimAll = (owner, targets, deps) => {
|
|
20795
|
+
for (const target of guarded(targets))
|
|
20796
|
+
claimLiveSession(target, owner, deps);
|
|
20797
|
+
};
|
|
20798
|
+
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path23.resolve(cwd, file)) : [path23.join(cwd, DIR_TARGET)];
|
|
20799
|
+
function writeGate(cwd, paths, owner, deps) {
|
|
20800
|
+
if (!owner)
|
|
20801
|
+
return beforeWriteGate(cwd, paths);
|
|
20802
|
+
const targets = liveTargets(cwd, paths);
|
|
20803
|
+
const live = liveGate(owner, targets, deps);
|
|
20804
|
+
if (live.kind !== "none")
|
|
20805
|
+
return live;
|
|
20806
|
+
const decision = beforeWriteGate(cwd, paths);
|
|
20807
|
+
if (decision.kind === "none")
|
|
20808
|
+
claimAll(owner, targets, deps);
|
|
20809
|
+
return decision;
|
|
20810
|
+
}
|
|
20811
|
+
function shellGate(cwd, command, owner, dialect = "posix", deps) {
|
|
20812
|
+
const writes = shellWrites(command);
|
|
20813
|
+
let targets = [];
|
|
20814
|
+
if (owner) {
|
|
20815
|
+
try {
|
|
20816
|
+
targets = [
|
|
20817
|
+
...segmentTargets(cwd, command, dialect),
|
|
20818
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path23.join(dir, DIR_TARGET))
|
|
20819
|
+
];
|
|
20820
|
+
} catch {
|
|
20821
|
+
targets = [];
|
|
20822
|
+
}
|
|
20823
|
+
}
|
|
20824
|
+
if (owner && targets.length) {
|
|
20825
|
+
const live = liveGate(owner, targets, deps);
|
|
20826
|
+
if (live.kind !== "none") {
|
|
20827
|
+
if (!overridden(command, dialect))
|
|
20828
|
+
return live;
|
|
20829
|
+
noteOverride(cwd, owner, command);
|
|
20830
|
+
}
|
|
20831
|
+
}
|
|
20832
|
+
const decision = writes.writes ? beforeWriteGate(cwd, writes.targets) : NONE3;
|
|
20833
|
+
if (owner && targets.length && decision.kind === "none")
|
|
20834
|
+
claimAll(owner, targets, deps);
|
|
20835
|
+
return decision;
|
|
20836
|
+
}
|
|
20837
|
+
var noteOverride = (cwd, owner, command) => {
|
|
20838
|
+
try {
|
|
20839
|
+
if (!inWorkitWorkspace(cwd))
|
|
20840
|
+
return;
|
|
20841
|
+
appendHookObserved(cwd, {
|
|
20842
|
+
type: "live_session.override",
|
|
20843
|
+
actor: { host: owner.host, session: owner.session, agentId: null },
|
|
20844
|
+
session: owner.session,
|
|
20845
|
+
command: command.slice(0, 200)
|
|
20846
|
+
});
|
|
20847
|
+
} catch {}
|
|
20848
|
+
};
|
|
20849
|
+
function beforeWriteGate(cwd, paths) {
|
|
20378
20850
|
try {
|
|
20379
20851
|
const gate = gateFor(cwd);
|
|
20380
20852
|
if (!gate || gate.blockers.length === 0)
|
|
@@ -20392,7 +20864,7 @@ function writeGate(cwd, paths) {
|
|
|
20392
20864
|
}
|
|
20393
20865
|
}
|
|
20394
20866
|
// packages/workit-core/src/hooks/stop.ts
|
|
20395
|
-
import { spawnSync as
|
|
20867
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
20396
20868
|
var NONE4 = { kind: "none" };
|
|
20397
20869
|
var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
|
|
20398
20870
|
var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
|
|
@@ -20413,7 +20885,7 @@ var asksUser = (message) => {
|
|
|
20413
20885
|
};
|
|
20414
20886
|
var short = (sha) => (sha ?? "?").slice(0, 12);
|
|
20415
20887
|
var gitList = (cwd, args) => {
|
|
20416
|
-
const run =
|
|
20888
|
+
const run = spawnSync14("git", args, {
|
|
20417
20889
|
cwd,
|
|
20418
20890
|
encoding: "utf8",
|
|
20419
20891
|
timeout: 3000,
|
|
@@ -20777,12 +21249,12 @@ ${line}` };
|
|
|
20777
21249
|
};
|
|
20778
21250
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
20779
21251
|
import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
20780
|
-
import
|
|
21252
|
+
import path24 from "node:path";
|
|
20781
21253
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
20782
21254
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
20783
21255
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
20784
21256
|
var existingDirectory = (value) => {
|
|
20785
|
-
if (!nonEmpty2(value) || !
|
|
21257
|
+
if (!nonEmpty2(value) || !path24.isAbsolute(value) || !existsSync7(value))
|
|
20786
21258
|
return null;
|
|
20787
21259
|
try {
|
|
20788
21260
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -20927,8 +21399,7 @@ function handleHook(input, deps) {
|
|
|
20927
21399
|
const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) : NONE5;
|
|
20928
21400
|
if (raw.kind === "deny")
|
|
20929
21401
|
return raw;
|
|
20930
|
-
const
|
|
20931
|
-
const gate = writes.writes ? writeGate(input.cwd, writes.targets) : NONE5;
|
|
21402
|
+
const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
|
|
20932
21403
|
if (gate.kind === "deny")
|
|
20933
21404
|
return gate;
|
|
20934
21405
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
@@ -20941,7 +21412,7 @@ function handleHook(input, deps) {
|
|
|
20941
21412
|
rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
|
|
20942
21413
|
return NONE5;
|
|
20943
21414
|
case "write.pre":
|
|
20944
|
-
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) : NONE5;
|
|
21415
|
+
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths, liveOwner(input, descriptor.transport)) : NONE5;
|
|
20945
21416
|
case "subagent.start":
|
|
20946
21417
|
return {
|
|
20947
21418
|
kind: "context",
|
|
@@ -21520,8 +21991,8 @@ var codexAdapter = {
|
|
|
21520
21991
|
};
|
|
21521
21992
|
|
|
21522
21993
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
21523
|
-
import
|
|
21524
|
-
import
|
|
21994
|
+
import fs18 from "node:fs";
|
|
21995
|
+
import path25 from "node:path";
|
|
21525
21996
|
var undocumented = { support: "undocumented", native: null };
|
|
21526
21997
|
var none = { support: "none", native: null };
|
|
21527
21998
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -21718,17 +22189,17 @@ var protocolEvent2 = (input) => {
|
|
|
21718
22189
|
};
|
|
21719
22190
|
var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
|
|
21720
22191
|
var lastAssistantText = (file) => {
|
|
21721
|
-
if (!file || !
|
|
22192
|
+
if (!file || !path25.isAbsolute(file))
|
|
21722
22193
|
return null;
|
|
21723
22194
|
try {
|
|
21724
|
-
const size =
|
|
22195
|
+
const size = fs18.statSync(file).size;
|
|
21725
22196
|
const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
|
|
21726
22197
|
const buffer = Buffer.alloc(size - start);
|
|
21727
|
-
const fd =
|
|
22198
|
+
const fd = fs18.openSync(file, "r");
|
|
21728
22199
|
try {
|
|
21729
|
-
|
|
22200
|
+
fs18.readSync(fd, buffer, 0, buffer.length, start);
|
|
21730
22201
|
} finally {
|
|
21731
|
-
|
|
22202
|
+
fs18.closeSync(fd);
|
|
21732
22203
|
}
|
|
21733
22204
|
const lines = buffer.toString("utf8").split(`
|
|
21734
22205
|
`);
|
|
@@ -21755,7 +22226,7 @@ var lastAssistantText = (file) => {
|
|
|
21755
22226
|
return null;
|
|
21756
22227
|
}
|
|
21757
22228
|
};
|
|
21758
|
-
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
22229
|
+
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path25.sep) ? root : `${root}${path25.sep}`);
|
|
21759
22230
|
var hookCwd = (input) => {
|
|
21760
22231
|
const cwd = existingDirectory(input.cwd);
|
|
21761
22232
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
package/dist/workit-hook.js
CHANGED
|
@@ -37,6 +37,6 @@ if (quiet)
|
|
|
37
37
|
process.stdout.write(`${JSON.stringify({ hookSpecificOutput: { hookEventName: quiet } })}
|
|
38
38
|
`);
|
|
39
39
|
else {
|
|
40
|
-
const { runCodexHook } = await import("./workit-hook-
|
|
40
|
+
const { runCodexHook } = await import("./workit-hook-m4bfzykw.js");
|
|
41
41
|
await runCodexHook([payload]);
|
|
42
42
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-codex",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.14.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit Codex CLI/desktop plugin — shared MCP transport, hooks, method skills, and verifier/reviewer/implementer agents",
|
|
6
6
|
"keywords": [
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"build": "bun scripts/build.ts"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@brainervirus/workit-core": "^8.
|
|
43
|
-
"@brainervirus/workit-mcp": "^8.
|
|
42
|
+
"@brainervirus/workit-core": "^8.14.1",
|
|
43
|
+
"@brainervirus/workit-mcp": "^8.14.1"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=24"
|
|
@@ -25,9 +25,9 @@ ceremony; do it yourself. Workit never spawns agents; you do.
|
|
|
25
25
|
it; change the plan or the standing orders and render again.
|
|
26
26
|
4. **Spawn in a rolling window**, in the background, each in its own worktree:
|
|
27
27
|
keep 4-6 in flight and refill from `spawnable` in `workit fanout status` as
|
|
28
|
-
each one finishes.
|
|
29
|
-
Claude Code: the `implementer` agent;
|
|
30
|
-
worktree create <slice>`.
|
|
28
|
+
each one finishes. Model: the brief's `model:` line, else TIER
|
|
29
|
+
(`references/orchestration.md`). Claude Code: the `implementer` agent;
|
|
30
|
+
elsewhere first `workit fanout worktree create <slice>`.
|
|
31
31
|
5. **Judge liveness by side effects only:** `workit fanout status` (head age,
|
|
32
32
|
PR, CI, verdict, landed; STUCK past the TIMEBOX). Stop a stuck worker and
|
|
33
33
|
observe that it exited (a timeout is not proof). Removing a worktree drops
|
|
@@ -53,7 +53,7 @@ ceremony; do it yourself. Workit never spawns agents; you do.
|
|
|
53
53
|
status` order, release the worktrees you made, and `workit ledger standing
|
|
54
54
|
clear`. Only you touch topology: workers never rebase, retarget or merge,
|
|
55
55
|
except the integration-tip merge in integration mode. Then ship
|
|
56
|
-
(read the `workit-ship` skill's SKILL.md and follow it)
|
|
56
|
+
(read the `workit-ship` skill's SKILL.md and follow it) up to `defaultEndpoint` from `workit grant show`.
|
|
57
57
|
|
|
58
58
|
## Example
|
|
59
59
|
|
|
@@ -5,8 +5,10 @@ and fans in. Workit records and checks; the host's own subagents run the work.
|
|
|
5
5
|
|
|
6
6
|
## TIER to model
|
|
7
7
|
|
|
8
|
-
TIER is a field of each slice and of its brief.
|
|
9
|
-
|
|
8
|
+
TIER is a field of each slice and of its brief. When the workspace sets
|
|
9
|
+
`fanout.tierModels`, the brief has a `model:` line under TIER and `workit
|
|
10
|
+
fanout status` names the model next to each spawnable slice: use that model.
|
|
11
|
+
Without one, map TIER where the host lets you pick a model per spawn:
|
|
10
12
|
|
|
11
13
|
| TIER | Claude Code (`model` on the Agent call) | Hosts without a per-spawn model |
|
|
12
14
|
| --- | --- | --- |
|