@brainervirus/workit-pi 8.31.1 → 8.33.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.
package/dist/worker.js CHANGED
@@ -6860,6 +6860,7 @@ var workspaceConfigSchema = object({
6860
6860
  verification: _enum(["proportional", "self", "independent"]).optional(),
6861
6861
  delegation: unknown().optional(),
6862
6862
  trailers: boolean2().optional(),
6863
+ deleteBranch: boolean2().optional(),
6863
6864
  liveSessionGuard: boolean2().optional(),
6864
6865
  fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
6865
6866
  models: object({ roles: unknown().optional() }).passthrough().optional(),
package/dist/workit.js CHANGED
@@ -7410,10 +7410,11 @@ function invalidOperation(family, input, error) {
7410
7410
  const request = isObject2(input) ? input : {};
7411
7411
  const action = typeof request.action === "string" ? request.action : "";
7412
7412
  const actions = OPERATION_ACTIONS[family];
7413
- if (!actions.includes(action))
7414
- return failure2("invalid_input", `${family}: action must be one of ${oneOf(actions)}`, {
7415
- fields: [{ path: "action", reason: `got ${JSON.stringify(request.action ?? null)}` }]
7416
- });
7413
+ if (!actions.includes(action)) {
7414
+ const common = actions.find((name) => READ_ONLY_ACTIONS.has(name)) ?? actions[0];
7415
+ const example = JSON.stringify(exampleOperation(family, common));
7416
+ return failure2("invalid_input", `${family}: action must be one of ${oneOf(actions)}. Example: ${example}`, { fields: [{ path: "action", reason: `got ${JSON.stringify(request.action ?? null)}` }] });
7417
+ }
7417
7418
  const own = actionKeys(family, action);
7418
7419
  const fields = [];
7419
7420
  const sentence = [];
@@ -8493,6 +8494,7 @@ var workspaceConfigSchema = object({
8493
8494
  verification: _enum(["proportional", "self", "independent"]).optional(),
8494
8495
  delegation: unknown().optional(),
8495
8496
  trailers: boolean2().optional(),
8497
+ deleteBranch: boolean2().optional(),
8496
8498
  liveSessionGuard: boolean2().optional(),
8497
8499
  fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
8498
8500
  models: object({ roles: unknown().optional() }).passthrough().optional(),
@@ -16204,7 +16206,14 @@ var sessionMarkerFile = (input, session, store) => {
16204
16206
  return path19.join(dir, "hooks", `skills-${name}.json`);
16205
16207
  };
16206
16208
  var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
16207
- var EMPTY = { nudged: [], loaded: [], raw: [], stops: [], judges: [] };
16209
+ var EMPTY = {
16210
+ nudged: [],
16211
+ loaded: [],
16212
+ raw: [],
16213
+ stops: [],
16214
+ judges: [],
16215
+ model: null
16216
+ };
16208
16217
  var readRaw = (file) => {
16209
16218
  try {
16210
16219
  return fs15.readFileSync(file, "utf8");
@@ -16220,7 +16229,8 @@ var parseMarker = (text) => {
16220
16229
  loaded: list2(value.loaded),
16221
16230
  raw: list2(value.raw),
16222
16231
  stops: list2(value.stops),
16223
- judges: list2(value.judges)
16232
+ judges: list2(value.judges),
16233
+ model: typeof value.model === "string" ? value.model : null
16224
16234
  };
16225
16235
  } catch {
16226
16236
  return { ...EMPTY };
@@ -16265,6 +16275,18 @@ var trackJudge = (input, agentId, running) => {
16265
16275
  return { ...marker, judges: running ? [...rest, agentId] : rest };
16266
16276
  });
16267
16277
  };
16278
+ var modelSwitchedOnDisk = (input) => {
16279
+ const session = input.session.id;
16280
+ const model = input.model;
16281
+ const file = session && model ? sessionMarkerFile(input, session) : null;
16282
+ if (!file || !model)
16283
+ return false;
16284
+ const previous = readSessionMarker(file).model;
16285
+ if (previous === model)
16286
+ return false;
16287
+ updateSessionMarker(file, (marker) => ({ ...marker, model }));
16288
+ return previous !== null;
16289
+ };
16268
16290
 
16269
16291
  // packages/workit-core/src/hooks/shell-words.ts
16270
16292
  class ShellAnalysisError extends Error {
@@ -24560,7 +24582,9 @@ function decide(input, deps) {
24560
24582
  if (!usable2(descriptor.context.perTurn))
24561
24583
  return NONE6;
24562
24584
  const current = turnContextText(input, descriptor);
24563
- const text = resendsOnChange(descriptor) ? changedTurnContext(input, current) : current;
24585
+ const turn = resendsOnChange(descriptor) ? changedTurnContext(input, current) : current;
24586
+ const text = input.model && modelSwitchedOnDisk(input) ? [invariantBootstrap(), turn].filter(Boolean).join(`
24587
+ `) : turn;
24564
24588
  return withContextLine(text ? { kind: "context", text } : NONE6, promptNudge(input, event.prompt));
24565
24589
  }
24566
24590
  case "shell.pre": {
@@ -24766,17 +24790,6 @@ var PI_DESCRIPTOR = {
24766
24790
  ]
24767
24791
  };
24768
24792
  // packages/workit-core/src/hooks/model-switch.ts
24769
- var MAX_SESSIONS = 256;
24770
- var modelSwitched = (models, sessionId, model) => {
24771
- if (!model)
24772
- return false;
24773
- const previous = models.get(sessionId);
24774
- models.delete(sessionId);
24775
- models.set(sessionId, model);
24776
- if (models.size > MAX_SESSIONS)
24777
- models.delete(models.keys().next().value);
24778
- return previous !== undefined && previous !== model;
24779
- };
24780
24793
  var modelKey = (model) => {
24781
24794
  if (model === null || typeof model !== "object")
24782
24795
  return null;
@@ -24812,13 +24825,15 @@ var taskTurnContext = (ctx) => {
24812
24825
  return null;
24813
24826
  }
24814
24827
  };
24815
- var sessionModels = new Map;
24816
- var turnContext = (ctx) => {
24817
- const task = taskTurnContext(ctx);
24818
- const switched = modelSwitched(sessionModels, ctx.sessionManager.getSessionId(), modelKey(ctx.model));
24819
- return switched && ctx.isProjectTrusted() ? `${invariantBootstrap()}${task ? `
24820
- ${task}` : ""}` : task;
24828
+ var switchedSessions = new Set;
24829
+ var noteModelSelect = (event, ctx) => {
24830
+ const next = modelKey(event.model);
24831
+ const previous = modelKey(event.previousModel);
24832
+ if (next && previous && next !== previous)
24833
+ switchedSessions.add(ctx.sessionManager.getSessionId());
24821
24834
  };
24835
+ var modelSwitchContract = (ctx) => switchedSessions.delete(ctx.sessionManager.getSessionId()) && ctx.isProjectTrusted() ? invariantBootstrap() : null;
24836
+ var turnContext = (ctx) => taskTurnContext(ctx);
24822
24837
  var turnInput = (ctx) => ({
24823
24838
  host: "pi",
24824
24839
  cwd: ctx.cwd,
@@ -24865,7 +24880,7 @@ var unfinishedTaskOffer2 = (ctx) => {
24865
24880
  // packages/workit-core/src/hooks/repeat-failure.ts
24866
24881
  import { createHash as createHash18 } from "node:crypto";
24867
24882
  var SAME_CALL_FAILED = "Same call failed twice:";
24868
- var MAX_SESSIONS2 = 256;
24883
+ var MAX_SESSIONS = 256;
24869
24884
  var stable = (value) => JSON.stringify(value, (_key, entry) => entry && typeof entry === "object" && !Array.isArray(entry) ? Object.fromEntries(Object.entries(entry).toSorted(([a], [b]) => a.localeCompare(b))) : entry) ?? "null";
24870
24885
  var hashOf = (call) => createHash18("sha256").update(call).digest("hex").slice(0, 32);
24871
24886
  var record2 = (memo, sessionId, call, failed, next) => {
@@ -24875,7 +24890,7 @@ var record2 = (memo, sessionId, call, failed, next) => {
24875
24890
  return null;
24876
24891
  const hash = hashOf(call);
24877
24892
  memo.set(sessionId, hash);
24878
- if (memo.size > MAX_SESSIONS2)
24893
+ if (memo.size > MAX_SESSIONS)
24879
24894
  memo.delete(memo.keys().next().value);
24880
24895
  return previous === hash && next ? `${SAME_CALL_FAILED} ${next}` : null;
24881
24896
  };
@@ -24893,6 +24908,26 @@ var escalateToolFailure = (memo, sessionId, tool, input, result) => {
24893
24908
  return line && failed ? { ...result, error: `${result.error}
24894
24909
  ${line}` } : result;
24895
24910
  };
24911
+ var WORKIT_COMMAND = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)*workit\s/;
24912
+ var unblockOf = (output) => {
24913
+ const human = /^\s*unblock: (.+)$/m.exec(output)?.[1];
24914
+ if (human)
24915
+ return human.trim();
24916
+ const json = /"unblock":("(?:[^"\\]|\\.)*")/.exec(output)?.[1];
24917
+ if (!json)
24918
+ return null;
24919
+ try {
24920
+ return JSON.parse(json);
24921
+ } catch {
24922
+ return null;
24923
+ }
24924
+ };
24925
+ var repeatedCliFailure = (memo, sessionId, command, output) => {
24926
+ if (!WORKIT_COMMAND.test(command))
24927
+ return null;
24928
+ const failed = /^workit: /m.test(output) || /"ok":\s*false/.test(output);
24929
+ return record2(memo, sessionId, `cli\x00${command.trim()}`, failed, failed ? unblockOf(output) : null);
24930
+ };
24896
24931
 
24897
24932
  // packages/workit-pi/src/tools.ts
24898
24933
  var readOnlyActions = new Set(["list", "inspect", "preview", "explain", "export"]);
@@ -25012,11 +25047,17 @@ var enforceToolPolicy = (event, ctx) => {
25012
25047
  };
25013
25048
  var observeToolResult = (event, ctx) => {
25014
25049
  try {
25015
- if (event.toolName !== "bash" || event.isError)
25050
+ if (event.toolName !== "bash")
25016
25051
  return;
25017
25052
  const command = event.input?.command;
25018
25053
  if (typeof command !== "string")
25019
25054
  return;
25055
+ const shown = event.content.map((part) => part.type === "text" ? part.text : "").join(`
25056
+ `);
25057
+ const session = piContext(ctx).caller.actor;
25058
+ const repeated = repeatedCliFailure(failures, session, command, shown);
25059
+ if (event.isError)
25060
+ return repeated ? { content: [...event.content, { type: "text", text: repeated }] } : undefined;
25020
25061
  const post = hookInput(ctx, {
25021
25062
  kind: "shell.post",
25022
25063
  command,
@@ -25028,7 +25069,10 @@ var observeToolResult = (event, ctx) => {
25028
25069
  const named = process.env.WORKIT_PI_WORKER_ROLE;
25029
25070
  const role = named === "implementer" ? "implementer" : named ? "judge" : null;
25030
25071
  const nudge = process.env.WORKIT_PI_WORKER_SESSION ? rawGitPre(post, command, { nudge: roleSteering(role, false).nudges }) : shellNudge(post, command);
25031
- return nudge.kind === "context" ? { content: [...event.content, { type: "text", text: nudge.text }] } : undefined;
25072
+ const extra = [nudge.kind === "context" ? nudge.text : null, repeated].filter((text) => text !== null);
25073
+ return extra.length > 0 ? { content: [...event.content, { type: "text", text: extra.join(`
25074
+
25075
+ `) }] } : undefined;
25032
25076
  } catch {
25033
25077
  return;
25034
25078
  }
@@ -25952,6 +25996,7 @@ function extension(pi) {
25952
25996
  sessions.delete(ctx.sessionManager.getSessionId());
25953
25997
  reconcileLostWorkers(ctx);
25954
25998
  });
25999
+ pi.on("model_select", (event, ctx) => noteModelSelect(event, ctx));
25955
26000
  pi.on("before_agent_start", (event, ctx) => {
25956
26001
  if (event.messageType !== "user")
25957
26002
  return;
@@ -25960,7 +26005,8 @@ function extension(pi) {
25960
26005
  const trusted = ctx.isProjectTrusted();
25961
26006
  const task = turnContext(ctx);
25962
26007
  let content;
25963
- if (sessions.has(id)) {
26008
+ const firstTurn = !sessions.has(id);
26009
+ if (!firstTurn) {
25964
26010
  content = trusted ? changedTurnContext(turnInput(ctx), task) : null;
25965
26011
  } else {
25966
26012
  sessions.add(id);
@@ -25972,6 +26018,10 @@ function extension(pi) {
25972
26018
 
25973
26019
  ${offer}` : ""}`;
25974
26020
  }
26021
+ const contract = modelSwitchContract(ctx);
26022
+ if (contract && !firstTurn)
26023
+ content = content ? `${contract}
26024
+ ${content}` : contract;
25975
26025
  if (content)
25976
26026
  return { message: contextMessage(ctx, nudge ? `${content}
25977
26027
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-pi",
3
- "version": "8.31.1",
3
+ "version": "8.33.0",
4
4
  "description": "Workit native extension for Pi — tools, method skills, and session continuity",
5
5
  "keywords": [
6
6
  "agentic-coding",