@brainervirus/workit-opencode 8.30.0 → 8.31.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/dist/plugin.js +139 -27
- package/package.json +2 -2
package/dist/plugin.js
CHANGED
|
@@ -14149,6 +14149,18 @@ function diffHash(cwd, base, head) {
|
|
|
14149
14149
|
}
|
|
14150
14150
|
return `sha256:${hash.digest("hex")}`;
|
|
14151
14151
|
}
|
|
14152
|
+
function cleanRebase(cwd, range) {
|
|
14153
|
+
if (!Object.values(range).every(safeRef2))
|
|
14154
|
+
return false;
|
|
14155
|
+
const fromFork = git4(cwd, ["merge-base", range.fromBase, range.from]);
|
|
14156
|
+
const toFork = git4(cwd, ["merge-base", range.toBase, range.to]);
|
|
14157
|
+
if (!fromFork || !toFork || fromFork === toFork)
|
|
14158
|
+
return false;
|
|
14159
|
+
const merged = gitRun2(cwd, ["merge-tree", "--write-tree", "--no-messages", `--merge-base=${fromFork}`, toFork, range.from], GIT_TIMEOUTS.worktree);
|
|
14160
|
+
const tree = merged.ok ? merged.stdout.split(`
|
|
14161
|
+
`)[0]?.trim() : null;
|
|
14162
|
+
return Boolean(tree) && tree === git4(cwd, ["rev-parse", "--verify", "-q", `${range.to}^{tree}`]);
|
|
14163
|
+
}
|
|
14152
14164
|
function storeRoot(cwd) {
|
|
14153
14165
|
const location = resolveStore(path16.resolve(cwd));
|
|
14154
14166
|
if (location instanceof Error)
|
|
@@ -14812,23 +14824,24 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
14812
14824
|
return drift;
|
|
14813
14825
|
};
|
|
14814
14826
|
const exactCache = new Map;
|
|
14815
|
-
const
|
|
14816
|
-
if (FAILING.has(String(row.result)))
|
|
14817
|
-
return true;
|
|
14827
|
+
const exactMatch = (row) => {
|
|
14818
14828
|
let exact = exactCache.get(row);
|
|
14819
14829
|
if (exact === undefined) {
|
|
14820
14830
|
const toBase = row.base ?? fallbackBase;
|
|
14821
14831
|
const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
|
|
14822
14832
|
const fromBase = row.baseSha ?? toBaseSha;
|
|
14823
|
-
|
|
14833
|
+
const range = row.head && head && toBaseSha && fromBase ? { from: row.head, to: head, fromBase, toBase: toBaseSha } : null;
|
|
14834
|
+
exact = !range ? null : sameOwnChange(cwd, range) ? "same" : cleanRebase(cwd, range) ? "rebase" : null;
|
|
14824
14835
|
exactCache.set(row, exact);
|
|
14825
14836
|
}
|
|
14826
14837
|
return exact;
|
|
14827
14838
|
};
|
|
14839
|
+
const exactCarry = (row) => FAILING.has(String(row.result)) || exactMatch(row) !== null;
|
|
14828
14840
|
const keyBasis = (row) => {
|
|
14829
14841
|
const basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
|
|
14830
14842
|
return basis === "carried" && !exactCarry(row) ? "stale" : basis;
|
|
14831
14843
|
};
|
|
14844
|
+
const rebaseNote = (row) => row.head && head && row.head !== head && !FAILING.has(String(row.result)) && keyBasis(row) === "carried" && exactMatch(row) === "rebase" ? { rule: "rebase", since: row.head, files: [] } : null;
|
|
14832
14845
|
const restacked = (row) => restackCarries(FAILING.has(String(row.result)) ? null : cwd, rows, branch, row.head, head);
|
|
14833
14846
|
const carryOf = (row) => {
|
|
14834
14847
|
if (keyBasis(row) !== "stale")
|
|
@@ -14906,7 +14919,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
14906
14919
|
independent: independence.length === 0,
|
|
14907
14920
|
accepted: reasons.length === 0,
|
|
14908
14921
|
reasons,
|
|
14909
|
-
carry: basis === "carried" ? carryOf(row) : null,
|
|
14922
|
+
carry: basis === "carried" ? rebaseNote(row) ?? carryOf(row) : null,
|
|
14910
14923
|
verdict: row
|
|
14911
14924
|
});
|
|
14912
14925
|
if (reasons.length === 0) {
|
|
@@ -25721,6 +25734,22 @@ var judgeVerdictText = (root, session, workerContext) => {
|
|
|
25721
25734
|
});
|
|
25722
25735
|
return `If this session verifies or reviews a candidate, record its verdict with exactly this command (pick the result and kind, live when you drove the real surface, name the candidate branch if it differs, say what you ran): \`${line}\`. Your shell already acts as your own Workit session ${childSessionId(session.parentID ?? "", session.id)}: pass no --session or --token, and never set WORKIT_SESSION_ID by hand.`;
|
|
25723
25736
|
};
|
|
25737
|
+
var injectModelSwitch = (root, session, directChildren, system) => {
|
|
25738
|
+
if (!session || !sameWorkspace(root, session.directory))
|
|
25739
|
+
return;
|
|
25740
|
+
const contract = session.parentID ? workerContextFor(root, session.id, session.parentID, directChildren) : getWorkitBootstrap();
|
|
25741
|
+
const taskContext = compactContextFor(root, session.id);
|
|
25742
|
+
const parts = [
|
|
25743
|
+
contract,
|
|
25744
|
+
taskContext && `<workit-task-context>${taskContext}</workit-task-context>`
|
|
25745
|
+
].filter(Boolean);
|
|
25746
|
+
if (parts.length === 0)
|
|
25747
|
+
return;
|
|
25748
|
+
system.push(textPart(`<workit-model-switch>
|
|
25749
|
+
${parts.join(`
|
|
25750
|
+
`)}
|
|
25751
|
+
</workit-model-switch>`));
|
|
25752
|
+
};
|
|
25724
25753
|
var injectHistoryOffer = (root, session, offeredSessions, system) => {
|
|
25725
25754
|
if (!session || session.parentID !== undefined || !sameWorkspace(root, session.directory) || offeredSessions.has(session.id))
|
|
25726
25755
|
return;
|
|
@@ -25804,6 +25833,60 @@ var childDenial = (identity) => `OpenCode child session has no validated Workit
|
|
|
25804
25833
|
|
|
25805
25834
|
// packages/workit-opencode/src/v2/shell.ts
|
|
25806
25835
|
import path44 from "node:path";
|
|
25836
|
+
|
|
25837
|
+
// packages/workit-core/src/hooks/repeat-failure.ts
|
|
25838
|
+
import { createHash as createHash18 } from "node:crypto";
|
|
25839
|
+
var SAME_CALL_FAILED = "Same call failed twice:";
|
|
25840
|
+
var MAX_SESSIONS = 256;
|
|
25841
|
+
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";
|
|
25842
|
+
var hashOf = (call) => createHash18("sha256").update(call).digest("hex").slice(0, 32);
|
|
25843
|
+
var record3 = (memo, sessionId, call, failed, next) => {
|
|
25844
|
+
const previous = memo.get(sessionId);
|
|
25845
|
+
memo.delete(sessionId);
|
|
25846
|
+
if (!failed)
|
|
25847
|
+
return null;
|
|
25848
|
+
const hash = hashOf(call);
|
|
25849
|
+
memo.set(sessionId, hash);
|
|
25850
|
+
if (memo.size > MAX_SESSIONS)
|
|
25851
|
+
memo.delete(memo.keys().next().value);
|
|
25852
|
+
return previous === hash && next ? `${SAME_CALL_FAILED} ${next}` : null;
|
|
25853
|
+
};
|
|
25854
|
+
var isFailure = (result) => result !== null && typeof result === "object" && result.ok === false && typeof result.error === "string";
|
|
25855
|
+
var correctedPayload = (tool, result) => {
|
|
25856
|
+
const example = /Example: (\{.*\})\s*$/.exec(result.error)?.[1];
|
|
25857
|
+
if (example)
|
|
25858
|
+
return `${tool} ${example}`;
|
|
25859
|
+
const guidance = result.details?.guidance;
|
|
25860
|
+
return typeof guidance === "string" && guidance ? guidance : null;
|
|
25861
|
+
};
|
|
25862
|
+
var escalateToolFailure = (memo, sessionId, tool, input, result) => {
|
|
25863
|
+
const failed = isFailure(result);
|
|
25864
|
+
const line = record3(memo, sessionId, `tool\x00${tool}\x00${stable(input)}`, failed, failed ? correctedPayload(tool, result) : null);
|
|
25865
|
+
return line && failed ? { ...result, error: `${result.error}
|
|
25866
|
+
${line}` } : result;
|
|
25867
|
+
};
|
|
25868
|
+
var WORKIT_COMMAND = /^\s*(?:[A-Za-z_][A-Za-z0-9_]*=\S*\s+)*workit\s/;
|
|
25869
|
+
var unblockOf = (output) => {
|
|
25870
|
+
const human = /^\s*unblock: (.+)$/m.exec(output)?.[1];
|
|
25871
|
+
if (human)
|
|
25872
|
+
return human.trim();
|
|
25873
|
+
const json = /"unblock":("(?:[^"\\]|\\.)*")/.exec(output)?.[1];
|
|
25874
|
+
if (!json)
|
|
25875
|
+
return null;
|
|
25876
|
+
try {
|
|
25877
|
+
return JSON.parse(json);
|
|
25878
|
+
} catch {
|
|
25879
|
+
return null;
|
|
25880
|
+
}
|
|
25881
|
+
};
|
|
25882
|
+
var repeatedCliFailure = (memo, sessionId, command, output) => {
|
|
25883
|
+
if (!WORKIT_COMMAND.test(command))
|
|
25884
|
+
return null;
|
|
25885
|
+
const failed = /^workit: /m.test(output) || /"ok":\s*false/.test(output);
|
|
25886
|
+
return record3(memo, sessionId, `cli\x00${command.trim()}`, failed, failed ? unblockOf(output) : null);
|
|
25887
|
+
};
|
|
25888
|
+
|
|
25889
|
+
// packages/workit-opencode/src/v2/shell.ts
|
|
25807
25890
|
var commandOf = (input) => {
|
|
25808
25891
|
if (input === null || typeof input !== "object")
|
|
25809
25892
|
return null;
|
|
@@ -25855,7 +25938,12 @@ var observeShellBefore = (root, event, workdirs) => {
|
|
|
25855
25938
|
noteRawCommit(input, shell.command, callKey(String(event.id), shell.command));
|
|
25856
25939
|
} catch {}
|
|
25857
25940
|
};
|
|
25858
|
-
var
|
|
25941
|
+
var outputText = (content) => typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => typeof part.text === "string" ? part.text : "").join(`
|
|
25942
|
+
`) : "";
|
|
25943
|
+
var appendText = (result, text) => typeof result.content === "string" ? { ...result, content: `${result.content}
|
|
25944
|
+
|
|
25945
|
+
${text}` } : Array.isArray(result.content) ? { ...result, content: [...result.content, { type: "text", text }] } : result;
|
|
25946
|
+
var observeShellResult = (root, event, failures = new Map) => {
|
|
25859
25947
|
try {
|
|
25860
25948
|
if (event.tool !== "shell" || event.status !== "completed")
|
|
25861
25949
|
return;
|
|
@@ -25866,17 +25954,14 @@ var observeShellResult = (root, event) => {
|
|
|
25866
25954
|
rawGitPost(input, shell.command, callKey(String(event.id), shell.command));
|
|
25867
25955
|
const nudge = shellNudge(input, shell.command);
|
|
25868
25956
|
const result = event.result;
|
|
25869
|
-
if (
|
|
25957
|
+
if (!result)
|
|
25870
25958
|
return;
|
|
25871
|
-
|
|
25872
|
-
|
|
25959
|
+
const repeated = repeatedCliFailure(failures, String(event.sessionID), shell.command, outputText(result.content));
|
|
25960
|
+
const extra = [nudge.kind === "context" ? nudge.text : null, repeated].filter((text) => text !== null);
|
|
25961
|
+
if (extra.length > 0)
|
|
25962
|
+
event.result = appendText(result, extra.join(`
|
|
25873
25963
|
|
|
25874
|
-
|
|
25875
|
-
else if (Array.isArray(result.content))
|
|
25876
|
-
event.result = {
|
|
25877
|
-
...result,
|
|
25878
|
-
content: [...result.content, { type: "text", text: nudge.text }]
|
|
25879
|
-
};
|
|
25964
|
+
`));
|
|
25880
25965
|
} catch {}
|
|
25881
25966
|
};
|
|
25882
25967
|
|
|
@@ -25968,15 +26053,15 @@ var injectSkillNudge = (root, session, messages, system) => {
|
|
|
25968
26053
|
};
|
|
25969
26054
|
|
|
25970
26055
|
// packages/workit-opencode/src/v2/stop.ts
|
|
25971
|
-
var
|
|
26056
|
+
var record4 = (value) => typeof value === "object" && value !== null ? value : {};
|
|
25972
26057
|
var lastAssistantText = (messages) => {
|
|
25973
26058
|
for (const message of messages.toReversed()) {
|
|
25974
|
-
if (
|
|
26059
|
+
if (record4(message).type !== "assistant")
|
|
25975
26060
|
continue;
|
|
25976
|
-
const content =
|
|
26061
|
+
const content = record4(message).content;
|
|
25977
26062
|
if (!Array.isArray(content))
|
|
25978
26063
|
return null;
|
|
25979
|
-
const text = content.filter((part) =>
|
|
26064
|
+
const text = content.filter((part) => record4(part).type === "text" && typeof record4(part).text === "string").map((part) => record4(part).text).join(`
|
|
25980
26065
|
`);
|
|
25981
26066
|
return text || null;
|
|
25982
26067
|
}
|
|
@@ -26147,6 +26232,28 @@ var pluginSourceFiles = [
|
|
|
26147
26232
|
])
|
|
26148
26233
|
].filter((file) => existsSync16(file)).toSorted();
|
|
26149
26234
|
|
|
26235
|
+
// packages/workit-core/src/hooks/model-switch.ts
|
|
26236
|
+
var MAX_SESSIONS2 = 256;
|
|
26237
|
+
var modelSwitched = (models, sessionId, model) => {
|
|
26238
|
+
if (!model)
|
|
26239
|
+
return false;
|
|
26240
|
+
const previous = models.get(sessionId);
|
|
26241
|
+
models.delete(sessionId);
|
|
26242
|
+
models.set(sessionId, model);
|
|
26243
|
+
if (models.size > MAX_SESSIONS2)
|
|
26244
|
+
models.delete(models.keys().next().value);
|
|
26245
|
+
return previous !== undefined && previous !== model;
|
|
26246
|
+
};
|
|
26247
|
+
var modelKey = (model) => {
|
|
26248
|
+
if (model === null || typeof model !== "object")
|
|
26249
|
+
return null;
|
|
26250
|
+
const { id, providerID, provider } = model;
|
|
26251
|
+
if (typeof id !== "string" || !id)
|
|
26252
|
+
return null;
|
|
26253
|
+
const source = typeof providerID === "string" ? providerID : provider;
|
|
26254
|
+
return typeof source === "string" && source ? `${source}/${id}` : id;
|
|
26255
|
+
};
|
|
26256
|
+
|
|
26150
26257
|
// packages/workit-opencode/src/v2/plugin.ts
|
|
26151
26258
|
var sessionFacts = async (ctx, sessionID) => {
|
|
26152
26259
|
try {
|
|
@@ -26185,6 +26292,8 @@ var setup = async (ctx) => {
|
|
|
26185
26292
|
const sourceMarker = markSourcesLoaded(pluginSourceFiles);
|
|
26186
26293
|
let staleSourcesWarned = false;
|
|
26187
26294
|
const historyOfferSessions = new Set;
|
|
26295
|
+
const sessionModels = new Map;
|
|
26296
|
+
const failureMemo = new Map;
|
|
26188
26297
|
const lifecycle = createV2Lifecycle({
|
|
26189
26298
|
root,
|
|
26190
26299
|
getSession: async (sessionID) => {
|
|
@@ -26223,9 +26332,10 @@ var setup = async (ctx) => {
|
|
|
26223
26332
|
input: spec.input,
|
|
26224
26333
|
options: { codemode: false },
|
|
26225
26334
|
execute: async (input, tool) => {
|
|
26335
|
+
const respond = (value) => resultContent(escalateToolFailure(failureMemo, String(tool.sessionID), spec.name, input, value));
|
|
26226
26336
|
const session = await sessionFacts(ctx, tool.sessionID);
|
|
26227
26337
|
if (session === null || !sameWorkspace(root, session.directory))
|
|
26228
|
-
return
|
|
26338
|
+
return respond(failure2("permission_denied", "OpenCode session location does not match the plugin checkout"));
|
|
26229
26339
|
if (spec.name === "workit_context")
|
|
26230
26340
|
return {
|
|
26231
26341
|
content: await contextTool.execute(input, { directory: root, sessionID: session.id })
|
|
@@ -26239,19 +26349,19 @@ var setup = async (ctx) => {
|
|
|
26239
26349
|
const directChild = lead !== null && lead.parentID === undefined && sameWorkspace(root, lead.directory);
|
|
26240
26350
|
const inspecting = childMayInspect(family, input);
|
|
26241
26351
|
if (!directChild || family === null || !(inspecting || childMayRecord(family, input)))
|
|
26242
|
-
return
|
|
26352
|
+
return respond(failure2("permission_denied", childDenial(identity)));
|
|
26243
26353
|
if (inspecting)
|
|
26244
|
-
return
|
|
26354
|
+
return respond(runFamily(family, input, { ...session, id: identity }, store, null, true));
|
|
26245
26355
|
if (childTaskMissing(store, input))
|
|
26246
|
-
return
|
|
26247
|
-
return
|
|
26356
|
+
return respond(failure2("not_found", "no task for this branch yet: the lead starts one (workit_task start) before a child records on it, or pass taskId"));
|
|
26357
|
+
return respond(runFamily(family, input, { ...session, id: identity }, store, null));
|
|
26248
26358
|
}
|
|
26249
26359
|
if (family !== null)
|
|
26250
|
-
return
|
|
26360
|
+
return respond(runFamily(family, input, session, store, workerId));
|
|
26251
26361
|
if (spec.name === "workit_init_apply") {
|
|
26252
26362
|
return { content: executeInitApply(input, root, initApplyRuntime) };
|
|
26253
26363
|
}
|
|
26254
|
-
return
|
|
26364
|
+
return respond(failure2("not_found", `unknown Workit tool ${spec.name}`));
|
|
26255
26365
|
}
|
|
26256
26366
|
});
|
|
26257
26367
|
}
|
|
@@ -26286,7 +26396,7 @@ var setup = async (ctx) => {
|
|
|
26286
26396
|
await ctx.tool.hook("execute.after", async (event) => {
|
|
26287
26397
|
if (event.tool === "shell") {
|
|
26288
26398
|
settleShellCall(pendingShells, { sessionID: String(event.sessionID), input: event.input });
|
|
26289
|
-
return observeShellResult(root, event);
|
|
26399
|
+
return observeShellResult(root, event, failureMemo);
|
|
26290
26400
|
}
|
|
26291
26401
|
if (event.tool !== "subagent")
|
|
26292
26402
|
return;
|
|
@@ -26307,6 +26417,8 @@ var setup = async (ctx) => {
|
|
|
26307
26417
|
injectHistoryOffer(root, session, historyOfferSessions, event.system);
|
|
26308
26418
|
if (session)
|
|
26309
26419
|
injectSkillNudge(root, session, event.messages, event.system);
|
|
26420
|
+
if (modelSwitched(sessionModels, String(event.sessionID), modelKey(event.model)))
|
|
26421
|
+
injectModelSwitch(root, session, lifecycle.directChildren, event.system);
|
|
26310
26422
|
if (!staleSourcesWarned) {
|
|
26311
26423
|
const changed = changedSourcesSinceLoad(sourceMarker);
|
|
26312
26424
|
if (changed.length > 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-opencode",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.31.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit OpenCode plugin — native tools, adaptive policy, and sixteen method skills",
|
|
6
6
|
"keywords": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"typecheck": "tsc --noEmit"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@brainervirus/workit-core": "^8.
|
|
42
|
+
"@brainervirus/workit-core": "^8.31.1",
|
|
43
43
|
"@opencode/plugin": "2.0.18"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|