@brainervirus/workit-core 8.30.0 → 8.31.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-core",
3
- "version": "8.30.0",
3
+ "version": "8.31.0",
4
4
  "private": false,
5
5
  "description": "Workit shared core — task, policy, evidence, review, decision, worker, and writer state for agentic coding workflows",
6
6
  "keywords": [
@@ -0,0 +1,35 @@
1
+ // A mid-session model switch (#435): the new model inherits a long context
2
+ // whose Workit contract sits far up, so the host re-sends the compact contract
3
+ // and the current turn context once per change, never on every turn.
4
+
5
+ /** The last model seen per session, in the host plugin's own state. */
6
+ export type SessionModels = Map<string, string>;
7
+
8
+ const MAX_SESSIONS = 256;
9
+
10
+ /**
11
+ * Record `model` for the session and say whether it differs from the last one
12
+ * seen. The first model a session reports is a start, not a switch; an unknown
13
+ * model (null) changes nothing.
14
+ */
15
+ export const modelSwitched = (
16
+ models: SessionModels,
17
+ sessionId: string,
18
+ model: string | null,
19
+ ): boolean => {
20
+ if (!model) return false;
21
+ const previous = models.get(sessionId);
22
+ models.delete(sessionId);
23
+ models.set(sessionId, model);
24
+ if (models.size > MAX_SESSIONS) models.delete(models.keys().next().value!);
25
+ return previous !== undefined && previous !== model;
26
+ };
27
+
28
+ /** `provider/id` from a host's model reference, or null when it names none. */
29
+ export const modelKey = (model: unknown): string | null => {
30
+ if (model === null || typeof model !== "object") return null;
31
+ const { id, providerID, provider } = model as Record<string, unknown>;
32
+ if (typeof id !== "string" || !id) return null;
33
+ const source = typeof providerID === "string" ? providerID : provider;
34
+ return typeof source === "string" && source ? `${source}/${id}` : id;
35
+ };
@@ -0,0 +1,115 @@
1
+ // A repeated identical failing Workit call (#435): the second failure in a row
2
+ // of the same tool and args (or the same `workit …` command) in a session ends
3
+ // with one line naming the exact corrected next call, instead of the same error.
4
+ import { createHash } from "node:crypto";
5
+
6
+ export const SAME_CALL_FAILED = "Same call failed twice:";
7
+
8
+ /** The last failing call's hash per session, in the host plugin's own state. */
9
+ export type FailureMemo = Map<string, string>;
10
+
11
+ const MAX_SESSIONS = 256;
12
+
13
+ const stable = (value: unknown): string =>
14
+ JSON.stringify(value, (_key, entry: unknown) =>
15
+ entry && typeof entry === "object" && !Array.isArray(entry)
16
+ ? Object.fromEntries(Object.entries(entry).toSorted(([a], [b]) => a.localeCompare(b)))
17
+ : entry,
18
+ ) ?? "null";
19
+
20
+ const hashOf = (call: string): string =>
21
+ createHash("sha256").update(call).digest("hex").slice(0, 32);
22
+
23
+ /**
24
+ * Record this call's outcome for the session. A success clears the memo; a
25
+ * failure returns the escalation line when the previous call failed the same
26
+ * way and a corrected call is known.
27
+ */
28
+ const record = (
29
+ memo: FailureMemo,
30
+ sessionId: string,
31
+ call: string,
32
+ failed: boolean,
33
+ next: string | null,
34
+ ): string | null => {
35
+ const previous = memo.get(sessionId);
36
+ memo.delete(sessionId);
37
+ if (!failed) return null;
38
+ const hash = hashOf(call);
39
+ memo.set(sessionId, hash);
40
+ if (memo.size > MAX_SESSIONS) memo.delete(memo.keys().next().value!);
41
+ return previous === hash && next ? `${SAME_CALL_FAILED} ${next}` : null;
42
+ };
43
+
44
+ type Failed = { ok: false; error: string; details?: { guidance?: unknown } };
45
+
46
+ const isFailure = (result: unknown): result is Failed =>
47
+ result !== null &&
48
+ typeof result === "object" &&
49
+ (result as { ok?: unknown }).ok === false &&
50
+ typeof (result as { error?: unknown }).error === "string";
51
+
52
+ /** A failed tool result's corrected payload: its `Example:` or, failing that, its guidance. */
53
+ const correctedPayload = (tool: string, result: Failed): string | null => {
54
+ const example = /Example: (\{.*\})\s*$/.exec(result.error)?.[1];
55
+ if (example) return `${tool} ${example}`;
56
+ const guidance = result.details?.guidance;
57
+ return typeof guidance === "string" && guidance ? guidance : null;
58
+ };
59
+
60
+ /**
61
+ * A Workit tool result, with the escalation line appended to its error when
62
+ * the same tool and args failed on the session's previous call too.
63
+ */
64
+ export const escalateToolFailure = <T>(
65
+ memo: FailureMemo,
66
+ sessionId: string,
67
+ tool: string,
68
+ input: unknown,
69
+ result: T,
70
+ ): T => {
71
+ const failed = isFailure(result);
72
+ const line = record(
73
+ memo,
74
+ sessionId,
75
+ `tool\0${tool}\0${stable(input)}`,
76
+ failed,
77
+ failed ? correctedPayload(tool, result) : null,
78
+ );
79
+ return line && failed ? { ...result, error: `${result.error}\n${line}` } : result;
80
+ };
81
+
82
+ const WORKIT_COMMAND = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)*workit\s/;
83
+
84
+ const unblockOf = (output: string): string | null => {
85
+ const human = /^\s*unblock: (.+)$/m.exec(output)?.[1];
86
+ if (human) return human.trim();
87
+ const json = /"unblock":("(?:[^"\\]|\\.)*")/.exec(output)?.[1];
88
+ if (!json) return null;
89
+ try {
90
+ return JSON.parse(json) as string;
91
+ } catch {
92
+ return null;
93
+ }
94
+ };
95
+
96
+ /**
97
+ * The escalation line for a finished `workit …` shell command, or null: a
98
+ * failed run is one whose output carries the CLI's error envelope.
99
+ */
100
+ export const repeatedCliFailure = (
101
+ memo: FailureMemo,
102
+ sessionId: string,
103
+ command: string,
104
+ output: string,
105
+ ): string | null => {
106
+ if (!WORKIT_COMMAND.test(command)) return null;
107
+ const failed = /^workit: /m.test(output) || /"ok":\s*false/.test(output);
108
+ return record(
109
+ memo,
110
+ sessionId,
111
+ `cli\0${command.trim()}`,
112
+ failed,
113
+ failed ? unblockOf(output) : null,
114
+ );
115
+ };