@brainervirus/workit-codex 8.27.0 → 8.28.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workit",
3
- "version": "8.27.0",
3
+ "version": "8.28.1",
4
4
  "description": "Adaptive engineering workflow rails for Codex",
5
5
  "author": {
6
6
  "name": "BrainerVirus"
@@ -13570,8 +13570,9 @@ function flatOperationJsonSchema(family, options = {}) {
13570
13570
  };
13571
13571
  const properties = { action: { type: "string", enum: actions } };
13572
13572
  const readField = (name) => name === "taskId" || (family === "task" ? READ_TASK_FIELDS.has(name) : family === "policy");
13573
+ const taken = (name) => family === "policy" || actions.some((action) => actionKeys(family, action)?.accepted.has(name));
13573
13574
  for (const [name, field] of Object.entries({ ...COMMON, ...spec.fields }))
13574
- if (options.readOnly ? readField(name) : !(family === "state" && name === "expectedRevision"))
13575
+ if (options.readOnly ? readField(name) : taken(name))
13575
13576
  properties[name] = property(field);
13576
13577
  return {
13577
13578
  $schema: "https://json-schema.org/draft/2020-12/schema",
@@ -13813,6 +13814,98 @@ function normalizeOperationInput(family, input, host) {
13813
13814
  request[key] = strings(request[key]);
13814
13815
  return request;
13815
13816
  }
13817
+ var defOf = (node) => node._zod.def;
13818
+ var unwrap = (node) => {
13819
+ let inner = node;
13820
+ while (["optional", "nullable"].includes(defOf(inner).type) && defOf(inner).innerType)
13821
+ inner = defOf(inner).innerType;
13822
+ return inner;
13823
+ };
13824
+ var FLAT_NAME = {
13825
+ authorityRefs: "refs",
13826
+ contentRefs: "refs",
13827
+ statedChoice: "choice",
13828
+ reviewContext: "reviewSession"
13829
+ };
13830
+ var NOT_FLAT = new Set(["schemaVersion", "action", "workspaceId"]);
13831
+ function actionKeys(family, action) {
13832
+ const options = advertisedOperationSchemas[family].options;
13833
+ const option = options.find((item) => {
13834
+ const literal = defOf(item).shape?.action;
13835
+ return literal._zod.def.values.includes(action);
13836
+ });
13837
+ if (!option)
13838
+ return null;
13839
+ const out = { accepted: new Set, required: [] };
13840
+ const walk = (shape, nested) => {
13841
+ for (const [key, node] of Object.entries(shape)) {
13842
+ if (NOT_FLAT.has(key) || nested && key === "taskId")
13843
+ continue;
13844
+ const inner = unwrap(node);
13845
+ const type = defOf(inner).type;
13846
+ const needed = !["optional", "nullable"].includes(defOf(node).type);
13847
+ if (key === "scope")
13848
+ for (const name of ["scope", "paths", "exclusions"])
13849
+ out.accepted.add(name);
13850
+ else if (type === "object" && key !== "bundle" && !(key in FLAT_NAME)) {
13851
+ out.accepted.add(key);
13852
+ walk(defOf(inner).shape, true);
13853
+ } else {
13854
+ const name = FLAT_NAME[key] ?? key;
13855
+ out.accepted.add(name);
13856
+ if (needed && type !== "array" && type !== "record" && !out.required.includes(name))
13857
+ out.required.push(name);
13858
+ }
13859
+ }
13860
+ };
13861
+ walk(defOf(option).shape, false);
13862
+ return out;
13863
+ }
13864
+ function exampleOperation(family, action) {
13865
+ const keys = actionKeys(family, action);
13866
+ const example = { action };
13867
+ for (const name of keys?.required ?? [])
13868
+ example[name] = FLAT[family].fields[name]?.enum?.[0] ?? `<${name}>`;
13869
+ if (family === "policy" && !keys?.required.length && action !== "explain")
13870
+ example.riskTier = "normal";
13871
+ return example;
13872
+ }
13873
+ var oneOf = (values) => values.join(" | ");
13874
+ function invalidOperation(family, input, error) {
13875
+ const request = isObject2(input) ? input : {};
13876
+ const action = typeof request.action === "string" ? request.action : "";
13877
+ const actions = OPERATION_ACTIONS[family];
13878
+ if (!actions.includes(action))
13879
+ return failure2("invalid_input", `${family}: action must be one of ${oneOf(actions)}`, {
13880
+ fields: [{ path: "action", reason: `got ${JSON.stringify(request.action ?? null)}` }]
13881
+ });
13882
+ const own = actionKeys(family, action);
13883
+ const fields = [];
13884
+ const sentence = [];
13885
+ for (const issue of error.issues) {
13886
+ if (issue.code === "unrecognized_keys") {
13887
+ for (const key of issue.keys) {
13888
+ const home = actions.find((other) => other !== action && actionKeys(family, other).accepted.has(key));
13889
+ const reason = home ? `${key} belongs to action: ${home}` : `${key} is not a field of ${family} ${action}; it takes ${[...own.accepted].join(", ")}`;
13890
+ fields.push({ path: key, reason });
13891
+ sentence.push(reason);
13892
+ }
13893
+ continue;
13894
+ }
13895
+ const last = String(issue.path.at(-1) ?? "");
13896
+ const name = Object.entries(FLAT_NAME).find(([contract]) => contract === last)?.[1] ?? last;
13897
+ const missing = issue.code === "invalid_type" && /received undefined/.test(issue.message);
13898
+ const absent = own.required.filter((key) => !(key in request));
13899
+ const needs = missing && issue.path.length === 1 && absent.length ? absent.join(", ") : name;
13900
+ const reason = missing ? `${family} ${action} requires ${needs}` : `${name}: ${issue.message}`;
13901
+ fields.push({ path: issue.path.join("."), reason });
13902
+ sentence.push(reason);
13903
+ }
13904
+ const example = JSON.stringify(exampleOperation(family, action));
13905
+ return failure2("invalid_input", `${[...new Set(sentence)].join("; ")}. Example: ${example}`, {
13906
+ fields
13907
+ });
13908
+ }
13816
13909
  function parseAdvertisedOperation(family, input, host) {
13817
13910
  const request = normalizeOperationInput(family, input, host);
13818
13911
  if (isObject2(request) && request.schemaVersion !== SCHEMA_VERSION)
@@ -13820,12 +13913,7 @@ function parseAdvertisedOperation(family, input, host) {
13820
13913
  const parsed = advertisedOperationSchemas[family].safeParse(request);
13821
13914
  if (parsed.success)
13822
13915
  return success(null, null, request);
13823
- return failure2("invalid_input", "operation input is invalid", {
13824
- fields: parsed.error.issues.map((issue) => ({
13825
- path: issue.code === "unrecognized_keys" ? issue.keys.join(".") : issue.path.join("."),
13826
- reason: issue.message
13827
- }))
13828
- });
13916
+ return invalidOperation(family, request, parsed.error);
13829
13917
  }
13830
13918
  // packages/workit-core/src/core/task-store.ts
13831
13919
  import { createHash as createHash8, randomUUID as randomUUID3 } from "node:crypto";
@@ -28202,6 +28290,96 @@ var WRITE_TOOLS = new Set([
28202
28290
  ]);
28203
28291
  // packages/workit-core/src/hooks/standing.ts
28204
28292
  var sinceStart = new Map;
28293
+ // packages/workit-core/src/core/role-models.ts
28294
+ var MODEL_ROLES = [
28295
+ "decider",
28296
+ "implementer",
28297
+ "verifier",
28298
+ "reviewer",
28299
+ "explore",
28300
+ "babysit"
28301
+ ];
28302
+ var MODEL_TIERS = ["mundane", "standard", "hard"];
28303
+ var HOST_KEYS = new Set([
28304
+ "claude_code",
28305
+ "opencode",
28306
+ "codex",
28307
+ "codex_cli",
28308
+ "codex_desktop",
28309
+ "cursor",
28310
+ "pi",
28311
+ "default"
28312
+ ]);
28313
+ var BUILT_IN_CLAUDE_CODE = {
28314
+ decider: { model: "fable", effort: "high" },
28315
+ "implementer.hard": { model: "opus" },
28316
+ "implementer.standard": { model: "sonnet" },
28317
+ "implementer.mundane": { model: "haiku" },
28318
+ verifier: { model: "sonnet" },
28319
+ reviewer: { model: "opus" },
28320
+ explore: { model: "haiku" },
28321
+ babysit: { model: "haiku" }
28322
+ };
28323
+ var isObject7 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
28324
+ var name = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
28325
+ var leaf = (value) => {
28326
+ const text = name(value);
28327
+ if (text)
28328
+ return { model: text, effort: null };
28329
+ if (!isObject7(value))
28330
+ return null;
28331
+ const model = name(value.model);
28332
+ return model ? { model, effort: name(value.effort) } : null;
28333
+ };
28334
+ var isFlat = (value) => typeof value === "string" || isObject7(value) && Object.hasOwn(value, "model");
28335
+ var forHost = (value, host) => {
28336
+ if (isFlat(value))
28337
+ return leaf(value);
28338
+ if (!isObject7(value))
28339
+ return null;
28340
+ const family = host ? hostFamily(host) : null;
28341
+ for (const key of [family, host, "default"])
28342
+ if (key && Object.hasOwn(value, key)) {
28343
+ const found = leaf(value[key]);
28344
+ if (found)
28345
+ return found;
28346
+ }
28347
+ return null;
28348
+ };
28349
+ var rolesOf = (models) => isObject7(models) && isObject7(models.roles) ? models.roles : null;
28350
+ var resolveRoleModel = (cwd, selector, host, dir) => {
28351
+ const { role, tier } = selector;
28352
+ for (const source of modelSources(cwd, dir)) {
28353
+ const where = source.global ? "the global default" : `workspace ${source.name}`;
28354
+ const roles = rolesOf(source.models);
28355
+ const configured = roles && Object.hasOwn(roles, role) ? roles[role] : undefined;
28356
+ const fromRoles = role === "implementer" ? tier && isObject7(configured) && Object.hasOwn(configured, tier) ? forHost(configured[tier], host) : null : forHost(configured, host);
28357
+ if (fromRoles)
28358
+ return { ...fromRoles, source: `models.roles in ${where}` };
28359
+ if (role === "implementer" && tier && isObject7(source.tierModels)) {
28360
+ const fromTiers = Object.hasOwn(source.tierModels, tier) ? forHost(source.tierModels[tier], host) : null;
28361
+ if (fromTiers)
28362
+ return { model: fromTiers.model, effort: null, source: `fanout.tierModels in ${where}` };
28363
+ }
28364
+ }
28365
+ if (host && hostFamily(host) === "claude_code") {
28366
+ const builtIn = BUILT_IN_CLAUDE_CODE[tier ? `${role}.${tier}` : role];
28367
+ if (builtIn)
28368
+ return { model: builtIn.model, effort: builtIn.effort ?? null, source: "built-in" };
28369
+ }
28370
+ return null;
28371
+ };
28372
+ var uniformImplementerModel = (cwd, host, dir) => {
28373
+ const tiers = MODEL_TIERS.map((tier) => resolveRoleModel(cwd, { role: "implementer", tier }, host, dir));
28374
+ const [first] = tiers;
28375
+ return first && tiers.every((t) => t?.model === first.model && t.effort === first.effort) ? first : null;
28376
+ };
28377
+ var ROLE_SELECTORS = MODEL_ROLES.flatMap((role) => role === "implementer" ? MODEL_TIERS.map((tier) => ({ role, tier })) : [{ role }]);
28378
+
28379
+ // packages/workit-core/src/hooks/agent-model.ts
28380
+ var MODELS = new Set(["sonnet", "opus", "haiku", "fable"]);
28381
+ var EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
28382
+
28205
28383
  // packages/workit-core/src/hooks/cwd-normalize.ts
28206
28384
  var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
28207
28385
  var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
@@ -28375,92 +28553,6 @@ var codexCapabilities = (host, availability = {}) => capabilitiesFor(codexDescri
28375
28553
  if (false)
28376
28554
  ;
28377
28555
 
28378
- // packages/workit-core/src/core/role-models.ts
28379
- var MODEL_ROLES = [
28380
- "decider",
28381
- "implementer",
28382
- "verifier",
28383
- "reviewer",
28384
- "explore",
28385
- "babysit"
28386
- ];
28387
- var MODEL_TIERS = ["mundane", "standard", "hard"];
28388
- var HOST_KEYS = new Set([
28389
- "claude_code",
28390
- "opencode",
28391
- "codex",
28392
- "codex_cli",
28393
- "codex_desktop",
28394
- "cursor",
28395
- "pi",
28396
- "default"
28397
- ]);
28398
- var BUILT_IN_CLAUDE_CODE = {
28399
- decider: { model: "fable", effort: "high" },
28400
- "implementer.hard": { model: "opus" },
28401
- "implementer.standard": { model: "sonnet" },
28402
- "implementer.mundane": { model: "haiku" },
28403
- verifier: { model: "sonnet" },
28404
- reviewer: { model: "opus" },
28405
- explore: { model: "haiku" },
28406
- babysit: { model: "haiku" }
28407
- };
28408
- var isObject7 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
28409
- var name = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
28410
- var leaf = (value) => {
28411
- const text = name(value);
28412
- if (text)
28413
- return { model: text, effort: null };
28414
- if (!isObject7(value))
28415
- return null;
28416
- const model = name(value.model);
28417
- return model ? { model, effort: name(value.effort) } : null;
28418
- };
28419
- var isFlat = (value) => typeof value === "string" || isObject7(value) && Object.hasOwn(value, "model");
28420
- var forHost = (value, host) => {
28421
- if (isFlat(value))
28422
- return leaf(value);
28423
- if (!isObject7(value))
28424
- return null;
28425
- const family = host ? hostFamily(host) : null;
28426
- for (const key of [family, host, "default"])
28427
- if (key && Object.hasOwn(value, key)) {
28428
- const found = leaf(value[key]);
28429
- if (found)
28430
- return found;
28431
- }
28432
- return null;
28433
- };
28434
- var rolesOf = (models) => isObject7(models) && isObject7(models.roles) ? models.roles : null;
28435
- var resolveRoleModel = (cwd, selector, host, dir) => {
28436
- const { role, tier } = selector;
28437
- for (const source of modelSources(cwd, dir)) {
28438
- const where = source.global ? "the global default" : `workspace ${source.name}`;
28439
- const roles = rolesOf(source.models);
28440
- const configured = roles && Object.hasOwn(roles, role) ? roles[role] : undefined;
28441
- const fromRoles = role === "implementer" ? tier && isObject7(configured) && Object.hasOwn(configured, tier) ? forHost(configured[tier], host) : null : forHost(configured, host);
28442
- if (fromRoles)
28443
- return { ...fromRoles, source: `models.roles in ${where}` };
28444
- if (role === "implementer" && tier && isObject7(source.tierModels)) {
28445
- const fromTiers = Object.hasOwn(source.tierModels, tier) ? forHost(source.tierModels[tier], host) : null;
28446
- if (fromTiers)
28447
- return { model: fromTiers.model, effort: null, source: `fanout.tierModels in ${where}` };
28448
- }
28449
- }
28450
- if (host && hostFamily(host) === "claude_code") {
28451
- const builtIn = BUILT_IN_CLAUDE_CODE[tier ? `${role}.${tier}` : role];
28452
- if (builtIn)
28453
- return { model: builtIn.model, effort: builtIn.effort ?? null, source: "built-in" };
28454
- }
28455
- return null;
28456
- };
28457
- var uniformImplementerModel = (cwd, host, dir) => {
28458
- const tiers = MODEL_TIERS.map((tier) => resolveRoleModel(cwd, { role: "implementer", tier }, host, dir));
28459
- const [first] = tiers;
28460
- return first && tiers.every((t) => t?.model === first.model && t.effort === first.effort) ? first : null;
28461
- };
28462
- var ROLE_SELECTORS = MODEL_ROLES.flatMap((role) => role === "implementer" ? MODEL_TIERS.map((tier) => ({ role, tier })) : [{ role }]);
28463
-
28464
28556
  // packages/workit-codex/scripts/agents.ts
28465
28557
  import {
28466
28558
  existsSync as existsSync10,
@@ -1,4 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ gitStoreDir2,
4
+ standingSeenFile2,
5
+ readStandingRev2,
6
+ readStandingSeen2,
7
+ writeStandingSeen2
8
+ } from "./workit-hook-jy7y0gdx.js";
2
9
  import {
3
10
  CHECKS_FILE,
4
11
  repoHeavyCommands,
@@ -6,13 +13,8 @@ import {
6
13
  matchesNamedCheck,
7
14
  gateCheckNames,
8
15
  checkCommand,
9
- maybeHeavy,
10
- gitStoreDir,
11
- standingSeenFile,
12
- readStandingRev,
13
- readStandingSeen,
14
- writeStandingSeen
15
- } from "./workit-hook-vhz9yrcw.js";
16
+ maybeHeavy2
17
+ } from "./workit-hook-2m8a4m5j.js";
16
18
  // packages/workit-core/src/hooks/descriptor.ts
17
19
  var support = (descriptor, axis) => {
18
20
  if (axis.startsWith("event:"))
@@ -8814,6 +8816,34 @@ var workspaceTrailers = (cwd, dir) => {
8814
8816
  };
8815
8817
  };
8816
8818
  var workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false;
8819
+ var modelSources = (cwd, dir) => {
8820
+ const out = [];
8821
+ try {
8822
+ const matched = matchRuntimeWorkspace(cwd, dir);
8823
+ if (matched) {
8824
+ const enclosing = matched.matches.filter((entry) => entry !== matched.selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
8825
+ for (const entry of [matched.selected, ...enclosing])
8826
+ out.push({
8827
+ name: entry.name,
8828
+ global: false,
8829
+ models: entry.models,
8830
+ tierModels: entry.fanout?.tierModels
8831
+ });
8832
+ }
8833
+ } catch {}
8834
+ try {
8835
+ const document = JSON.parse(readFileSync2(path6.join(dir ?? configDir(), "workspaces.json"), "utf8"));
8836
+ if (document && typeof document === "object" && !Array.isArray(document))
8837
+ out.push({
8838
+ name: "global",
8839
+ global: true,
8840
+ models: document.models,
8841
+ tierModels: undefined
8842
+ });
8843
+ } catch {}
8844
+ return out;
8845
+ };
8846
+ var hostFamily = (host) => host.replace(/_(?:cli|desktop)$/u, "");
8817
8847
  var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
8818
8848
  var TIER_HOSTS = new Set([
8819
8849
  "claude_code",
@@ -9571,11 +9601,16 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
9571
9601
  fingerprint
9572
9602
  };
9573
9603
  };
9604
+ var rethrowRuntimeBug = (error) => {
9605
+ if (error instanceof TypeError || error instanceof ReferenceError)
9606
+ throw error;
9607
+ };
9574
9608
  var validateBranchNameFor = (workspaceRoot, name) => {
9575
9609
  try {
9576
9610
  const resolved = branchPolicySnapshotFor(workspaceRoot);
9577
9611
  return validateBranchNamePolicy(name, resolved.namePolicy, resolved.source);
9578
9612
  } catch (error) {
9613
+ rethrowRuntimeBug(error);
9579
9614
  const correction = "repair or select a valid workspace policy before creating a branch";
9580
9615
  return {
9581
9616
  ok: false,
@@ -15196,6 +15231,7 @@ function normalizeOperationInput(family, input, host) {
15196
15231
  request[key] = strings(request[key]);
15197
15232
  return request;
15198
15233
  }
15234
+ var NOT_FLAT = new Set(["schemaVersion", "action", "workspaceId"]);
15199
15235
 
15200
15236
  // packages/workit-core/src/core/policy/judgment.ts
15201
15237
  var isObject6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -20492,7 +20528,7 @@ var rulesFor = (host, cwd, env) => {
20492
20528
  function heavyGate(cwd, command, options) {
20493
20529
  const env = options.env ?? process.env;
20494
20530
  try {
20495
- if (env.WORKIT_HEAVY_GATE === "0" || !maybeHeavy(command, cwd, env) || !workitOnPath(env))
20531
+ if (env.WORKIT_HEAVY_GATE === "0" || !maybeHeavy2(command, cwd, env) || !workitOnPath(env))
20496
20532
  return { kind: "none" };
20497
20533
  return heavyPlan(command, {
20498
20534
  cwd,
@@ -21871,11 +21907,11 @@ var standingFor = (input, store, rev) => {
21871
21907
  return { rev, orders, rows };
21872
21908
  };
21873
21909
  var located = (input) => {
21874
- const store = input.session.id ? gitStoreDir(input.cwd) : null;
21875
- const rev = store ? readStandingRev(store) : null;
21910
+ const store = input.session.id ? gitStoreDir2(input.cwd) : null;
21911
+ const rev = store ? readStandingRev2(store) : null;
21876
21912
  if (!store || rev === null)
21877
21913
  return null;
21878
- return { store, rev, file: standingSeenFile(store, input.session.id, input.session.agentId) };
21914
+ return { store, rev, file: standingSeenFile2(store, input.session.id, input.session.agentId) };
21879
21915
  };
21880
21916
  var seedStanding = (input) => {
21881
21917
  try {
@@ -21883,7 +21919,7 @@ var seedStanding = (input) => {
21883
21919
  if (!where)
21884
21920
  return;
21885
21921
  const now = standingFor(input, where.store, where.rev);
21886
- writeStandingSeen(where.file, { rev: where.rev, ids: [...now.orders.keys()] });
21922
+ writeStandingSeen2(where.file, { rev: where.rev, ids: [...now.orders.keys()] });
21887
21923
  } catch {}
21888
21924
  };
21889
21925
  var fanoutOf = (row) => typeof row.fanout === "string" ? row.fanout : "?";
@@ -21910,17 +21946,144 @@ var standingDelta = (input) => {
21910
21946
  const where = located(input);
21911
21947
  if (!where)
21912
21948
  return null;
21913
- const seen = readStandingSeen(where.file);
21949
+ const seen = readStandingSeen2(where.file);
21914
21950
  if (seen.rev === where.rev)
21915
21951
  return null;
21916
21952
  const now = standingFor(input, where.store, where.rev);
21917
- writeStandingSeen(where.file, { rev: where.rev, ids: [...now.orders.keys()] });
21953
+ writeStandingSeen2(where.file, { rev: where.rev, ids: [...now.orders.keys()] });
21918
21954
  return changedSince(now, seen.ids);
21919
21955
  } catch {
21920
21956
  return null;
21921
21957
  }
21922
21958
  };
21923
21959
  var sinceStart = new Map;
21960
+ // packages/workit-core/src/core/role-models.ts
21961
+ var MODEL_ROLES = [
21962
+ "decider",
21963
+ "implementer",
21964
+ "verifier",
21965
+ "reviewer",
21966
+ "explore",
21967
+ "babysit"
21968
+ ];
21969
+ var MODEL_TIERS = ["mundane", "standard", "hard"];
21970
+ var HOST_KEYS = new Set([
21971
+ "claude_code",
21972
+ "opencode",
21973
+ "codex",
21974
+ "codex_cli",
21975
+ "codex_desktop",
21976
+ "cursor",
21977
+ "pi",
21978
+ "default"
21979
+ ]);
21980
+ var BUILT_IN_CLAUDE_CODE = {
21981
+ decider: { model: "fable", effort: "high" },
21982
+ "implementer.hard": { model: "opus" },
21983
+ "implementer.standard": { model: "sonnet" },
21984
+ "implementer.mundane": { model: "haiku" },
21985
+ verifier: { model: "sonnet" },
21986
+ reviewer: { model: "opus" },
21987
+ explore: { model: "haiku" },
21988
+ babysit: { model: "haiku" }
21989
+ };
21990
+ var isObject7 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
21991
+ var name = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
21992
+ var leaf = (value) => {
21993
+ const text = name(value);
21994
+ if (text)
21995
+ return { model: text, effort: null };
21996
+ if (!isObject7(value))
21997
+ return null;
21998
+ const model = name(value.model);
21999
+ return model ? { model, effort: name(value.effort) } : null;
22000
+ };
22001
+ var isFlat = (value) => typeof value === "string" || isObject7(value) && Object.hasOwn(value, "model");
22002
+ var forHost = (value, host) => {
22003
+ if (isFlat(value))
22004
+ return leaf(value);
22005
+ if (!isObject7(value))
22006
+ return null;
22007
+ const family = host ? hostFamily(host) : null;
22008
+ for (const key of [family, host, "default"])
22009
+ if (key && Object.hasOwn(value, key)) {
22010
+ const found = leaf(value[key]);
22011
+ if (found)
22012
+ return found;
22013
+ }
22014
+ return null;
22015
+ };
22016
+ var rolesOf = (models) => isObject7(models) && isObject7(models.roles) ? models.roles : null;
22017
+ var resolveRoleModel = (cwd, selector, host, dir) => {
22018
+ const { role, tier } = selector;
22019
+ for (const source of modelSources(cwd, dir)) {
22020
+ const where = source.global ? "the global default" : `workspace ${source.name}`;
22021
+ const roles = rolesOf(source.models);
22022
+ const configured = roles && Object.hasOwn(roles, role) ? roles[role] : undefined;
22023
+ const fromRoles = role === "implementer" ? tier && isObject7(configured) && Object.hasOwn(configured, tier) ? forHost(configured[tier], host) : null : forHost(configured, host);
22024
+ if (fromRoles)
22025
+ return { ...fromRoles, source: `models.roles in ${where}` };
22026
+ if (role === "implementer" && tier && isObject7(source.tierModels)) {
22027
+ const fromTiers = Object.hasOwn(source.tierModels, tier) ? forHost(source.tierModels[tier], host) : null;
22028
+ if (fromTiers)
22029
+ return { model: fromTiers.model, effort: null, source: `fanout.tierModels in ${where}` };
22030
+ }
22031
+ }
22032
+ if (host && hostFamily(host) === "claude_code") {
22033
+ const builtIn = BUILT_IN_CLAUDE_CODE[tier ? `${role}.${tier}` : role];
22034
+ if (builtIn)
22035
+ return { model: builtIn.model, effort: builtIn.effort ?? null, source: "built-in" };
22036
+ }
22037
+ return null;
22038
+ };
22039
+ var ROLE_SELECTORS = MODEL_ROLES.flatMap((role) => role === "implementer" ? MODEL_TIERS.map((tier) => ({ role, tier })) : [{ role }]);
22040
+ var roleForAgentType = (agentType, prompt) => {
22041
+ switch (agentType) {
22042
+ case "workit:implementer": {
22043
+ const tier = typeof prompt === "string" ? /^[ \t]*TIER:[ \t]*(mundane|standard|hard)\b/im.exec(prompt)?.[1] : undefined;
22044
+ return { role: "implementer", tier: tier?.toLowerCase() ?? "standard" };
22045
+ }
22046
+ case "workit:verifier":
22047
+ return { role: "verifier" };
22048
+ case "workit:reviewer":
22049
+ return { role: "reviewer" };
22050
+ case "Explore":
22051
+ return { role: "explore" };
22052
+ default:
22053
+ return null;
22054
+ }
22055
+ };
22056
+
22057
+ // packages/workit-core/src/hooks/agent-model.ts
22058
+ var MODELS = new Set(["sonnet", "opus", "haiku", "fable"]);
22059
+ var EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
22060
+ var given = (value) => typeof value === "string" && value.trim() !== "";
22061
+ var agentModelDecision = (cwd, tool, env = process.env) => {
22062
+ if (given(tool.model) || env.CLAUDE_CODE_SUBAGENT_MODEL_FORCE)
22063
+ return { kind: "none" };
22064
+ const selector = roleForAgentType(tool.subagent_type, tool.prompt);
22065
+ if (!selector)
22066
+ return { kind: "none" };
22067
+ const resolved = resolveRoleModel(cwd, selector, "claude_code");
22068
+ if (!resolved)
22069
+ return { kind: "none" };
22070
+ const model = resolved.model.toLowerCase();
22071
+ const effort = resolved.effort?.toLowerCase() ?? null;
22072
+ const input = { ...tool };
22073
+ if (MODELS.has(model))
22074
+ input.model = model;
22075
+ if (effort && EFFORTS.has(effort) && !given(tool.effort))
22076
+ input.effort = effort;
22077
+ if (input.model === undefined && input.effort === tool.effort)
22078
+ return { kind: "none" };
22079
+ const label = selector.tier ? `${selector.role} ${selector.tier}` : selector.role;
22080
+ return {
22081
+ kind: "update-input",
22082
+ input,
22083
+ text: `Workit set ${String(input.model ?? "no model")}${input.effort !== tool.effort ? ` (effort ${String(input.effort)})` : ""} on this ${String(tool.subagent_type)} spawn: role ${label}, ${resolved.source}. Name a model to choose another.`
22084
+ };
22085
+ };
22086
+
21924
22087
  // packages/workit-core/src/hooks/cwd-normalize.ts
21925
22088
  var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
21926
22089
  var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
@@ -22284,7 +22447,7 @@ function handleHook(input, deps) {
22284
22447
  const delta = standingDelta(input);
22285
22448
  if (!delta)
22286
22449
  return decision;
22287
- return decision.kind === "rewrite" ? { ...decision, text: [decision.text, delta].filter(Boolean).join(`
22450
+ return decision.kind === "rewrite" || decision.kind === "update-input" ? { ...decision, text: [decision.text, delta].filter(Boolean).join(`
22288
22451
  `) } : withContextLine(decision, delta);
22289
22452
  }
22290
22453
  function decide(input, deps) {
@@ -22397,6 +22560,8 @@ function decide(input, deps) {
22397
22560
  case "tool.pre":
22398
22561
  if (event.skill)
22399
22562
  recordSkillLoad(input, event.skill, "tool");
22563
+ if (event.agentInput && input.host === "claude_code")
22564
+ return agentModelDecision(input.cwd, event.agentInput);
22400
22565
  return NONE6;
22401
22566
  case "stop":
22402
22567
  return usable2(descriptor.events.stop.support) ? stopDecision(input, descriptor) : NONE6;
@@ -22680,6 +22845,16 @@ var eventOf = (name, value) => {
22680
22845
  skill: isRecord5(value.tool_input) ? optionalText(value.tool_input.skill) : null
22681
22846
  }
22682
22847
  };
22848
+ if (value.tool_name === "Agent" || value.tool_name === "Task")
22849
+ return {
22850
+ ok: true,
22851
+ event: {
22852
+ kind: "tool.pre",
22853
+ tool: value.tool_name,
22854
+ toolUseId,
22855
+ ...isRecord5(value.tool_input) ? { agentInput: value.tool_input } : {}
22856
+ }
22857
+ };
22683
22858
  if (!SHELL_TOOLS.has(value.tool_name))
22684
22859
  return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
22685
22860
  const command = toolCommand(value);
@@ -22756,7 +22931,7 @@ var CONTEXT_EVENTS = new Set([
22756
22931
  "PostToolUse"
22757
22932
  ]);
22758
22933
  var render = (decision, native) => {
22759
- if (decision.kind === "rewrite" && native === "PreToolUse" && decision.input)
22934
+ if ((decision.kind === "rewrite" || decision.kind === "update-input") && native === "PreToolUse" && decision.input)
22760
22935
  return {
22761
22936
  json: {
22762
22937
  hookSpecificOutput: {
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ stateDir2,
4
+ maybeHeavy2
5
+ } from "./workit-hook-2m8a4m5j.js";
6
+ export {
7
+ maybeHeavy2 as maybeHeavy,
8
+ stateDir2 as stateDir
9
+ };
@@ -235,7 +235,7 @@ function checkCommand(args, version, onPath = workitAvailable()) {
235
235
  // packages/workit-core/src/core/heavy-words.ts
236
236
  var HEAVY_WORDS = new Set(("npm npx pnpm pnpx yarn bun bunx deno corepack make gmake docker podman nerdctl " + "python python3 py uv poetry pdm pipenv hatch rye bundle rake nx " + "jest vitest mocha ava tap karma jasmine nyc c8 tsc vue-tsc svelte-check eslint stylelint " + "tslint webpack webpack-cli rollup parcel tsup vite next nuxt nuxi astro ng react-scripts " + "playwright cypress wdio testcafe turbo lerna sonar-scanner sonarless sonarless-mcp go " + "golangci-lint gotestsum staticcheck cargo mvn mvnw gradle gradlew ant sbt bazel bazelisk " + "buck2 dotnet msbuild pytest py.test tox nox mypy pyright pylint coverage maturin pip pip3 " + "rspec rails phpunit pest phpstan psalm composer mix swift xcodebuild flutter dart pod cmake " + "ninja meson ctest scons docker-compose buildah earthly").split(" "));
237
237
  var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
238
- var stateDir = (env) => {
238
+ var stateDir2 = (env) => {
239
239
  if (env.WORKFLOW_TOOLKIT_STATE)
240
240
  return env.WORKFLOW_TOOLKIT_STATE;
241
241
  if (env.XDG_STATE_HOME)
@@ -256,7 +256,7 @@ var readJson = (file) => {
256
256
  };
257
257
  var patternWords = (cwd, env) => {
258
258
  const patterns = [];
259
- const machine = readJson(path2.join(stateDir(env), "heavy-commands.json"));
259
+ const machine = readJson(path2.join(stateDir2(env), "heavy-commands.json"));
260
260
  if (Array.isArray(machine?.patterns))
261
261
  patterns.push(...machine.patterns);
262
262
  for (let dir = path2.resolve(cwd);; dir = path2.dirname(dir)) {
@@ -278,7 +278,7 @@ var patternWords = (cwd, env) => {
278
278
  }
279
279
  return words;
280
280
  };
281
- function maybeHeavy(command, cwd, env = process.env) {
281
+ function maybeHeavy2(command, cwd, env = process.env) {
282
282
  try {
283
283
  const words = wordsOf(command);
284
284
  if (words.some((word) => HEAVY_WORDS.has(word) || /^python[\d.]*$/u.test(word)))
@@ -290,64 +290,4 @@ function maybeHeavy(command, cwd, env = process.env) {
290
290
  }
291
291
  }
292
292
 
293
- // packages/workit-core/src/hooks/standing-signal.ts
294
- import { createHash } from "node:crypto";
295
- import fs3 from "node:fs";
296
- import path3 from "node:path";
297
- var gitStoreDir = (cwd) => {
298
- try {
299
- for (let dir = path3.resolve(cwd);; dir = path3.dirname(dir)) {
300
- const dotGit = path3.join(dir, ".git");
301
- if (fs3.existsSync(dotGit)) {
302
- let gitDir = dotGit;
303
- if (fs3.statSync(dotGit).isFile()) {
304
- const target = /^gitdir:\s*(.+)$/mu.exec(fs3.readFileSync(dotGit, "utf8"))?.[1]?.trim();
305
- if (!target)
306
- return null;
307
- gitDir = path3.resolve(dir, target);
308
- const common = path3.join(gitDir, "commondir");
309
- if (fs3.existsSync(common))
310
- gitDir = path3.resolve(gitDir, fs3.readFileSync(common, "utf8").trim());
311
- }
312
- return path3.join(gitDir, "workit");
313
- }
314
- if (path3.dirname(dir) === dir)
315
- return null;
316
- }
317
- } catch {
318
- return null;
319
- }
320
- };
321
- var standingRevFile = (store) => path3.join(store, "standing", "rev");
322
- var standingSeenFile = (store, session, agentId) => path3.join(store, "standing", "seen", `${createHash("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`);
323
- var readStandingRev = (store) => {
324
- try {
325
- return fs3.readFileSync(standingRevFile(store), "utf8").trim() || null;
326
- } catch {
327
- return null;
328
- }
329
- };
330
- var readStandingSeen = (file) => {
331
- try {
332
- const value = JSON.parse(fs3.readFileSync(file, "utf8"));
333
- return {
334
- rev: typeof value.rev === "string" ? value.rev : null,
335
- ids: Array.isArray(value.ids) ? value.ids.filter((id) => typeof id === "string") : []
336
- };
337
- } catch {
338
- return { rev: null, ids: [] };
339
- }
340
- };
341
- var writeStandingSeen = (file, seen) => {
342
- try {
343
- fs3.mkdirSync(path3.dirname(file), { recursive: true });
344
- fs3.writeFileSync(file, JSON.stringify(seen));
345
- } catch {}
346
- };
347
- var standingPending = (cwd, session, agentId) => {
348
- const store = gitStoreDir(cwd);
349
- const rev = store ? readStandingRev(store) : null;
350
- return rev !== null && readStandingSeen(standingSeenFile(store, session, agentId)).rev !== rev;
351
- };
352
-
353
- export { CHECKS_FILE, repoHeavyCommands, loadCheckConfig, matchesNamedCheck, gateCheckNames, checkCommand, stateDir, maybeHeavy, gitStoreDir, standingSeenFile, readStandingRev, readStandingSeen, writeStandingSeen, standingPending };
293
+ export { CHECKS_FILE, repoHeavyCommands, loadCheckConfig, matchesNamedCheck, gateCheckNames, checkCommand, stateDir2, maybeHeavy2 };
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+
3
+ // packages/workit-core/src/hooks/standing-signal.ts
4
+ import { createHash } from "node:crypto";
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+ var gitStoreDir2 = (cwd) => {
8
+ try {
9
+ for (let dir = path.resolve(cwd);; dir = path.dirname(dir)) {
10
+ const dotGit = path.join(dir, ".git");
11
+ if (fs.existsSync(dotGit)) {
12
+ let gitDir = dotGit;
13
+ if (fs.statSync(dotGit).isFile()) {
14
+ const target = /^gitdir:\s*(.+)$/mu.exec(fs.readFileSync(dotGit, "utf8"))?.[1]?.trim();
15
+ if (!target)
16
+ return null;
17
+ gitDir = path.resolve(dir, target);
18
+ const common = path.join(gitDir, "commondir");
19
+ if (fs.existsSync(common))
20
+ gitDir = path.resolve(gitDir, fs.readFileSync(common, "utf8").trim());
21
+ }
22
+ return path.join(gitDir, "workit");
23
+ }
24
+ if (path.dirname(dir) === dir)
25
+ return null;
26
+ }
27
+ } catch {
28
+ return null;
29
+ }
30
+ };
31
+ var standingRevFile = (store) => path.join(store, "standing", "rev");
32
+ var standingSeenFile2 = (store, session, agentId) => path.join(store, "standing", "seen", `${createHash("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`);
33
+ var readStandingRev2 = (store) => {
34
+ try {
35
+ return fs.readFileSync(standingRevFile(store), "utf8").trim() || null;
36
+ } catch {
37
+ return null;
38
+ }
39
+ };
40
+ var readStandingSeen2 = (file) => {
41
+ try {
42
+ const value = JSON.parse(fs.readFileSync(file, "utf8"));
43
+ return {
44
+ rev: typeof value.rev === "string" ? value.rev : null,
45
+ ids: Array.isArray(value.ids) ? value.ids.filter((id) => typeof id === "string") : []
46
+ };
47
+ } catch {
48
+ return { rev: null, ids: [] };
49
+ }
50
+ };
51
+ var writeStandingSeen2 = (file, seen) => {
52
+ try {
53
+ fs.mkdirSync(path.dirname(file), { recursive: true });
54
+ fs.writeFileSync(file, JSON.stringify(seen));
55
+ } catch {}
56
+ };
57
+ var bumpStandingRev2 = (store, rev) => {
58
+ try {
59
+ fs.mkdirSync(path.join(store, "standing"), { recursive: true });
60
+ fs.writeFileSync(standingRevFile(store), `${rev}
61
+ `);
62
+ } catch {}
63
+ };
64
+ var standingPending2 = (cwd, session, agentId) => {
65
+ const store = gitStoreDir2(cwd);
66
+ const rev = store ? readStandingRev2(store) : null;
67
+ return rev !== null && readStandingSeen2(standingSeenFile2(store, session, agentId)).rev !== rev;
68
+ };
69
+
70
+ export { gitStoreDir2, standingSeenFile2, readStandingRev2, readStandingSeen2, writeStandingSeen2, bumpStandingRev2, standingPending2 };
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ gitStoreDir2,
4
+ standingSeenFile2,
5
+ readStandingRev2,
6
+ readStandingSeen2,
7
+ writeStandingSeen2,
8
+ bumpStandingRev2,
9
+ standingPending2
10
+ } from "./workit-hook-jy7y0gdx.js";
11
+ export {
12
+ bumpStandingRev2 as bumpStandingRev,
13
+ gitStoreDir2 as gitStoreDir,
14
+ readStandingRev2 as readStandingRev,
15
+ readStandingSeen2 as readStandingSeen,
16
+ standingPending2 as standingPending,
17
+ standingSeenFile2 as standingSeenFile,
18
+ writeStandingSeen2 as writeStandingSeen
19
+ };
@@ -1,11 +1,41 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- stateDir,
4
- maybeHeavy,
5
- standingPending
6
- } from "./workit-hook-vhz9yrcw.js";
7
2
 
8
3
  // packages/workit-codex/hooks/launcher.ts
4
+ import { readFileSync } from "node:fs";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ var nodeFloorMessage = () => {
8
+ try {
9
+ const found = process.env.WORKIT_HOOK_NODE_VERSION || process.versions.node;
10
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
+ const range = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8")).engines?.node;
12
+ const min = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(String(range ?? ""));
13
+ if (!min)
14
+ return null;
15
+ const need = [min[1], min[2] ?? 0, min[3] ?? 0].map(Number);
16
+ const have = String(found).replace(/^v/, "").split(".").map(Number);
17
+ for (let i = 0;i < 3; i++) {
18
+ const a = have[i] ?? 0;
19
+ if (Number.isNaN(a))
20
+ return null;
21
+ if (a !== need[i])
22
+ return a < need[i] ? `needs Node ${range}, found ${found}` : null;
23
+ }
24
+ return null;
25
+ } catch {
26
+ return null;
27
+ }
28
+ };
29
+ var tooOld = nodeFloorMessage();
30
+ if (tooOld) {
31
+ process.stderr.write(`[workit] Codex hook unavailable: ${tooOld}
32
+ `);
33
+ process.stdout.write(`{}
34
+ `);
35
+ process.exit(0);
36
+ }
37
+ var { maybeHeavy, stateDir } = await import("./workit-hook-27bwpc4a.js");
38
+ var { standingPending } = await import("./workit-hook-mk64s4fk.js");
9
39
  var payload = "";
10
40
  for await (const chunk of process.stdin)
11
41
  payload += String(chunk);
@@ -49,6 +79,6 @@ if (quiet)
49
79
  process.stdout.write(`${JSON.stringify({ hookSpecificOutput: { hookEventName: quiet } })}
50
80
  `);
51
81
  else {
52
- const { runCodexHook } = await import("./workit-hook-sza2zvq6.js");
82
+ const { runCodexHook } = await import("./workit-hook-0vzd6e1j.js");
53
83
  await runCodexHook([payload]);
54
84
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-codex",
3
- "version": "8.27.0",
3
+ "version": "8.28.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.27.0",
43
- "@brainervirus/workit-mcp": "^8.27.0"
42
+ "@brainervirus/workit-core": "^8.28.1",
43
+ "@brainervirus/workit-mcp": "^8.28.1"
44
44
  },
45
45
  "engines": {
46
46
  "node": ">=24"