@brainervirus/workit-codex 8.36.0 → 8.37.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.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ observeHookBudget2,
4
+ budgetHooks2
5
+ } from "./workit-hook-b8nrcfzr.js";
6
+ import"./workit-hook-jy7y0gdx.js";
7
+ export {
8
+ budgetHooks2 as budgetHooks,
9
+ observeHookBudget2 as observeHookBudget
10
+ };
@@ -18,6 +18,9 @@ import {
18
18
  import {
19
19
  recordHookRun2
20
20
  } from "./workit-hook-w6s3hcyz.js";
21
+ import {
22
+ budgetHooks2
23
+ } from "./workit-hook-b8nrcfzr.js";
21
24
  // packages/workit-core/src/hooks/descriptor.ts
22
25
  var support = (descriptor, axis) => {
23
26
  if (axis.startsWith("event:"))
@@ -22620,7 +22623,8 @@ function handleHook(input, deps) {
22620
22623
  const decision = decide(input, deps);
22621
22624
  if (deps.descriptor.transport !== "hook-process" || !PRE_TOOL.has(input.event.kind) || decision.kind === "deny" || decision.kind === "ask")
22622
22625
  return decision;
22623
- const delta = standingDelta(input);
22626
+ const delta = [standingDelta(input), deps.budget?.line(input) ?? null].filter(Boolean).join(`
22627
+ `) || null;
22624
22628
  if (!delta)
22625
22629
  return decision;
22626
22630
  return decision.kind === "rewrite" || decision.kind === "update-input" ? { ...decision, text: [decision.text, delta].filter(Boolean).join(`
@@ -22730,7 +22734,11 @@ function decide(input, deps) {
22730
22734
  case "subagent.start":
22731
22735
  if (workitRole(descriptor, event.agentType) === "judge")
22732
22736
  trackJudge(input, event.agentId, true);
22733
- seedStanding({ ...input, session: { ...input.session, agentId: event.agentId } });
22737
+ seedStanding({
22738
+ ...input,
22739
+ session: { ...input.session, agentId: event.agentId }
22740
+ });
22741
+ deps.budget?.start(input, event.agentId, event.agentType);
22734
22742
  return {
22735
22743
  kind: "context",
22736
22744
  text: subagentStartText(input, descriptor, event)
@@ -22743,13 +22751,19 @@ function decide(input, deps) {
22743
22751
  case "tool.pre":
22744
22752
  if (event.skill)
22745
22753
  recordSkillLoad(input, event.skill, "tool");
22746
- if (event.agentInput && input.host === "claude_code")
22747
- return spawnDecision(descriptor, input.cwd, event.agentInput);
22754
+ if (event.agentInput && input.host === "claude_code") {
22755
+ const spawn = spawnDecision(descriptor, input.cwd, event.agentInput);
22756
+ if (spawn.kind !== "deny")
22757
+ deps.budget?.spawn(input, event.agentInput);
22758
+ return spawn;
22759
+ }
22748
22760
  return NONE6;
22749
22761
  case "stop":
22750
22762
  return usable2(descriptor.events.stop.support) ? stopDecision(input, descriptor) : NONE6;
22751
22763
  case "subagent.stop":
22752
22764
  trackJudge(input, event.agentId, false);
22765
+ if (event.agentId)
22766
+ deps.budget?.stop(input, event.agentId);
22753
22767
  return NONE6;
22754
22768
  case "prompt.submit":
22755
22769
  return NONE6;
@@ -22773,7 +22787,8 @@ var dispatchHook = (adapter, raw, env = process.env) => {
22773
22787
  try {
22774
22788
  const decision = handleHook(parsed.input, {
22775
22789
  descriptor,
22776
- addendum: adapter.addendum?.(parsed.input) ?? null
22790
+ addendum: adapter.addendum?.(parsed.input) ?? null,
22791
+ budget: adapter.budget
22777
22792
  });
22778
22793
  return { ...adapter.render(decision, parsed.native), error: null };
22779
22794
  } catch (error) {
@@ -22906,7 +22921,7 @@ var CLAUDE_CODE_DESCRIPTOR = {
22906
22921
  "shell.post": { support: "native", native: "PostToolUse" },
22907
22922
  "subagent.start": { support: "native", native: "SubagentStart" },
22908
22923
  "prompt.submit": { support: "native", native: "UserPromptSubmit" },
22909
- "subagent.stop": { support: "none", native: null },
22924
+ "subagent.stop": { support: "native", native: "SubagentStop" },
22910
22925
  "compact.pre": { support: "none", native: null },
22911
22926
  stop: { support: "native", native: "Stop" }
22912
22927
  },
@@ -23810,9 +23825,10 @@ function warnOnSurfaceFallback(env = process.env) {
23810
23825
  process.stderr.write(`[workit] unrecognized CODEX_INTERNAL_ORIGINATOR_OVERRIDE=${JSON.stringify(override)} — treating surface as codex_cli
23811
23826
  `);
23812
23827
  }
23828
+ var adapter = { ...codexAdapter, budget: budgetHooks2 };
23813
23829
  var runCodexHook = async (stdin = process.stdin) => {
23814
23830
  warnOnSurfaceFallback();
23815
- process.exitCode = await runHookProcess(detectCodexSurface(process.env), stdin, process.stdout, process.stderr, process.env, { startedAt: PROCESS_START });
23831
+ process.exitCode = await runHookProcess(adapter, stdin, process.stdout, process.stderr, process.env, { startedAt: PROCESS_START });
23816
23832
  };
23817
23833
  if (false)
23818
23834
  ;
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ gitStoreDir2
4
+ } from "./workit-hook-jy7y0gdx.js";
5
+
6
+ // packages/workit-core/src/hooks/budget-signal.ts
7
+ import { createHash } from "node:crypto";
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+ var hash = (text) => createHash("sha256").update(text).digest("hex").slice(0, 32);
11
+ var budgetDir = (store) => path.join(store, "budget");
12
+ var agentBase = (store, session, agentId) => path.join(budgetDir(store), hash(`${session}\x00${agentId ?? ""}`));
13
+ var spawnsFile = (store, session) => path.join(budgetDir(store), `spawns-${hash(session)}.jsonl`);
14
+ var spawnBudget = (prompt) => {
15
+ const found = /^TIMEBOX:[^\n]*?\b(\d+)\s+tool calls\b/mu.exec(prompt);
16
+ const n = found ? Number(found[1]) : NaN;
17
+ return Number.isInteger(n) && n > 0 ? n : null;
18
+ };
19
+ var noteSpawnBudget = (cwd, session, type, prompt) => {
20
+ try {
21
+ const store = session ? gitStoreDir2(cwd) : null;
22
+ if (!store)
23
+ return;
24
+ fs.mkdirSync(budgetDir(store), { recursive: true });
25
+ fs.appendFileSync(spawnsFile(store, session), `${JSON.stringify({ type: type ?? "", n: spawnBudget(prompt) })}
26
+ `);
27
+ } catch {}
28
+ };
29
+ var readBudget = (file) => {
30
+ try {
31
+ const { limit, told } = JSON.parse(fs.readFileSync(file, "utf8"));
32
+ return typeof limit === "number" && limit > 0 ? { limit, told: Number(told) || 0 } : null;
33
+ } catch {
34
+ return null;
35
+ }
36
+ };
37
+ var claimSpawnBudget = (cwd, session, agentId, agentType) => {
38
+ try {
39
+ const store = session ? gitStoreDir2(cwd) : null;
40
+ if (!store)
41
+ return null;
42
+ const file = spawnsFile(store, session);
43
+ const entries = fs.readFileSync(file, "utf8").split(`
44
+ `).filter(Boolean).map((line) => JSON.parse(line));
45
+ const at = entries.findIndex((entry) => entry.type === (agentType ?? ""));
46
+ if (at < 0)
47
+ return null;
48
+ const [{ n }] = entries.splice(at, 1);
49
+ fs.writeFileSync(file, entries.map((entry) => `${JSON.stringify(entry)}
50
+ `).join(""));
51
+ if (typeof n !== "number" || n <= 0)
52
+ return null;
53
+ startBudget(store, session, agentId, n);
54
+ return n;
55
+ } catch {
56
+ return null;
57
+ }
58
+ };
59
+ var startBudget = (store, session, agentId, n) => {
60
+ const dir = budgetDir(store);
61
+ fs.mkdirSync(dir, { recursive: true });
62
+ const cutoff = Date.now() - 604800000;
63
+ for (const name of fs.readdirSync(dir))
64
+ try {
65
+ if (fs.statSync(path.join(dir, name)).mtimeMs < cutoff)
66
+ fs.rmSync(path.join(dir, name));
67
+ } catch {}
68
+ const base = agentBase(store, session, agentId);
69
+ fs.writeFileSync(`${base}.json`, JSON.stringify({ limit: n, told: 0 }));
70
+ fs.rmSync(`${base}.calls`, { force: true });
71
+ };
72
+ var tickBudget = (cwd, session, agentId) => {
73
+ try {
74
+ const store = session ? gitStoreDir2(cwd) : null;
75
+ if (!store)
76
+ return false;
77
+ const base = agentBase(store, session, agentId);
78
+ const budget = readBudget(`${base}.json`);
79
+ if (!budget)
80
+ return false;
81
+ fs.appendFileSync(`${base}.calls`, ".");
82
+ return fs.statSync(`${base}.calls`).size >= budget.limit * (budget.told + 1);
83
+ } catch {
84
+ return false;
85
+ }
86
+ };
87
+ var lineFor = (calls) => `Tool-call budget reached (${calls}): commit your work, write your report, stop.`;
88
+ var reachedOf = (cwd, session, agentId) => {
89
+ const store = session ? gitStoreDir2(cwd) : null;
90
+ const base = store && agentBase(store, session, agentId);
91
+ const budget = base && readBudget(`${base}.json`);
92
+ if (!budget)
93
+ return null;
94
+ return { base, budget, reached: Math.floor(fs.statSync(`${base}.calls`).size / budget.limit) };
95
+ };
96
+ var dropBudget = (cwd, session, agentId) => {
97
+ const store = session ? gitStoreDir2(cwd) : null;
98
+ if (store)
99
+ for (const ext of [".json", ".calls"])
100
+ fs.rmSync(agentBase(store, session, agentId) + ext, { force: true });
101
+ };
102
+ var budgetLine = (cwd, session, agentId) => {
103
+ try {
104
+ const at = reachedOf(cwd, session, agentId);
105
+ if (!at || at.reached <= at.budget.told)
106
+ return null;
107
+ fs.writeFileSync(`${at.base}.json`, JSON.stringify({ ...at.budget, told: at.reached }));
108
+ return lineFor(at.budget.limit * at.reached);
109
+ } catch {
110
+ return null;
111
+ }
112
+ };
113
+ var CODEX_GATED = new Set(["bash", "unifiedexec", "shell", "applypatch", "edit", "write"]);
114
+ var observeHookBudget2 = (payload) => {
115
+ try {
116
+ const value = JSON.parse(payload);
117
+ if (value.hook_event_name !== "PreToolUse" || typeof value.session_id !== "string")
118
+ return false;
119
+ const cwd = typeof value.cwd === "string" ? value.cwd : ".";
120
+ const agentId = typeof value.agent_id === "string" && value.agent_id.trim() ? value.agent_id : null;
121
+ if (agentId)
122
+ return tickBudget(cwd, value.session_id, agentId);
123
+ const tool = typeof value.tool_name === "string" ? value.tool_name : "";
124
+ const spawn = value.tool_input;
125
+ if (CODEX_GATED.has(tool.toLowerCase().replace(/[_-]/g, "")) || !spawn || typeof spawn !== "object")
126
+ return false;
127
+ const fields = spawn;
128
+ const prompt = Object.values(fields).find((field) => typeof field === "string" && field.includes("TIMEBOX:"));
129
+ const type = [fields.agent_type, fields.subagent_type].find((field) => typeof field === "string");
130
+ if (prompt !== undefined || type !== undefined)
131
+ noteSpawnBudget(cwd, value.session_id, type ?? null, prompt ?? "");
132
+ return false;
133
+ } catch {
134
+ return false;
135
+ }
136
+ };
137
+ var budgetHooks2 = {
138
+ line: (input) => budgetLine(input.cwd, input.session.id ?? "", input.session.agentId),
139
+ start: (input, agentId, agentType) => void claimSpawnBudget(input.cwd, input.session.id ?? "", agentId, agentType),
140
+ spawn: (input, spawn) => {
141
+ if (!input.session.agentId)
142
+ noteSpawnBudget(input.cwd, input.session.id ?? "", String(spawn.subagent_type || "general-purpose"), String(spawn.prompt ?? ""));
143
+ },
144
+ stop: (input, agentId) => {
145
+ try {
146
+ dropBudget(input.cwd, input.session.id ?? "", agentId);
147
+ } catch {}
148
+ }
149
+ };
150
+
151
+ export { observeHookBudget2, budgetHooks2 };
@@ -37,6 +37,7 @@ if (tooOld) {
37
37
  var { maybeHeavy, stateDir } = await import("./workit-hook-9pq84bdw.js");
38
38
  var { standingPending } = await import("./workit-hook-mk64s4fk.js");
39
39
  var { recordHookRun } = await import("./workit-hook-b9ansxvm.js");
40
+ var { observeHookBudget } = await import("./workit-hook-3g6cv3hd.js");
40
41
  var payload = "";
41
42
  for await (const chunk of process.stdin)
42
43
  payload += String(chunk);
@@ -75,13 +76,13 @@ var quietEvent = () => {
75
76
  return null;
76
77
  }
77
78
  };
78
- var quiet = quietEvent();
79
+ var quiet = observeHookBudget(payload) ? null : quietEvent();
79
80
  if (quiet) {
80
81
  if (quiet === "PreToolUse")
81
82
  recordHookRun(process.env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || process.env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli", quiet);
82
83
  process.stdout.write(`${JSON.stringify({ hookSpecificOutput: { hookEventName: quiet } })}
83
84
  `);
84
85
  } else {
85
- const { runCodexHook } = await import("./workit-hook-7dqgrrwd.js");
86
+ const { runCodexHook } = await import("./workit-hook-4hyzn1m0.js");
86
87
  await runCodexHook([payload]);
87
88
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-codex",
3
- "version": "8.36.0",
3
+ "version": "8.37.0",
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.36.0",
43
- "@brainervirus/workit-mcp": "^8.36.0"
42
+ "@brainervirus/workit-core": "^8.37.0",
43
+ "@brainervirus/workit-mcp": "^8.37.0"
44
44
  },
45
45
  "engines": {
46
46
  "node": ">=24"