opencode-immune 1.0.19 → 1.0.20
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 +42 -31
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -902,44 +902,37 @@ function createEventHandler(state) {
|
|
|
902
902
|
}
|
|
903
903
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
904
904
|
// HOOK 9: MULTI-CYCLE AUTOMATION (PRE_COMMIT + CYCLE_COMPLETE)
|
|
905
|
+
// Uses experimental.text.complete to detect signal markers in assistant output.
|
|
906
|
+
// chat.message only fires for user messages (output.message is UserMessage),
|
|
907
|
+
// so signal detection MUST use text.complete which fires for assistant text.
|
|
905
908
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
906
909
|
const MAX_CYCLES = 10;
|
|
907
910
|
const PRE_COMMIT_MARKER = "0-ULTRAWORK: PRE_COMMIT";
|
|
908
911
|
const CYCLE_COMPLETE_MARKER = "0-ULTRAWORK: CYCLE_COMPLETE";
|
|
909
912
|
const NEXT_TASK_PATTERN = /Next task:\s*(.+)/;
|
|
910
|
-
const RATE_LIMIT_MESSAGE_PATTERN = /too many requests|rate_limit|rate limit/i;
|
|
911
913
|
const ALL_CYCLES_COMPLETE_MARKER = "0-ULTRAWORK: ALL_CYCLES_COMPLETE";
|
|
912
914
|
/**
|
|
913
|
-
*
|
|
915
|
+
* experimental.text.complete: scans completed assistant text for signal markers.
|
|
914
916
|
*
|
|
915
917
|
* PRE_COMMIT → executes /commit via client.session.command()
|
|
916
918
|
* CYCLE_COMPLETE → creates a new session and sends bootstrap prompt
|
|
919
|
+
* ALL_CYCLES_COMPLETE → clears ultrawork marker
|
|
917
920
|
*/
|
|
918
|
-
function
|
|
921
|
+
function createTextCompleteHandler(state) {
|
|
919
922
|
return async (input, output) => {
|
|
920
923
|
const sessionID = input.sessionID;
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
let messageContent = "";
|
|
924
|
-
for (const p of parts) {
|
|
925
|
-
if ("type" in p && p.type === "text" && "text" in p) {
|
|
926
|
-
messageContent += " " + p.text;
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
messageContent = messageContent.trim();
|
|
930
|
-
if (!messageContent)
|
|
924
|
+
const text = output.text ?? "";
|
|
925
|
+
if (!text)
|
|
931
926
|
return;
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
if (messageContent.includes(ALL_CYCLES_COMPLETE_MARKER)) {
|
|
927
|
+
// ── ALL_CYCLES_COMPLETE: clear ultrawork marker ──
|
|
928
|
+
if (text.includes(ALL_CYCLES_COMPLETE_MARKER)) {
|
|
935
929
|
await clearUltraworkMarker(state);
|
|
936
|
-
console.log("[opencode-immune] Multi-Cycle: ALL_CYCLES_COMPLETE detected, ultrawork marker cleared.");
|
|
930
|
+
console.log("[opencode-immune] Multi-Cycle: ALL_CYCLES_COMPLETE detected in text.complete, ultrawork marker cleared.");
|
|
937
931
|
}
|
|
938
|
-
// ── PRE_COMMIT: execute /commit
|
|
939
|
-
if (
|
|
932
|
+
// ── PRE_COMMIT: execute /commit ──
|
|
933
|
+
if (text.includes(PRE_COMMIT_MARKER) && !state.commitPending && sessionID) {
|
|
940
934
|
state.commitPending = true;
|
|
941
|
-
console.log("[opencode-immune] Multi-Cycle: PRE_COMMIT detected, executing /commit...");
|
|
942
|
-
// Small delay to let the message finish rendering
|
|
935
|
+
console.log("[opencode-immune] Multi-Cycle: PRE_COMMIT detected in text.complete, executing /commit...");
|
|
943
936
|
setTimeout(async () => {
|
|
944
937
|
try {
|
|
945
938
|
await state.input.client.session.command({
|
|
@@ -959,29 +952,26 @@ function createMultiCycleHandler(state) {
|
|
|
959
952
|
}
|
|
960
953
|
}, 2_000);
|
|
961
954
|
}
|
|
962
|
-
// ── CYCLE_COMPLETE: create new session
|
|
963
|
-
if (
|
|
955
|
+
// ── CYCLE_COMPLETE: create new session ──
|
|
956
|
+
if (text.includes(CYCLE_COMPLETE_MARKER)) {
|
|
964
957
|
state.cycleCount++;
|
|
965
958
|
if (state.cycleCount >= MAX_CYCLES) {
|
|
966
959
|
console.log(`[opencode-immune] Multi-Cycle: MAX_CYCLES (${MAX_CYCLES}) reached. Not creating new session.`);
|
|
967
960
|
await clearUltraworkMarker(state);
|
|
968
961
|
return;
|
|
969
962
|
}
|
|
970
|
-
|
|
971
|
-
const taskMatch = messageContent.match(NEXT_TASK_PATTERN);
|
|
963
|
+
const taskMatch = text.match(NEXT_TASK_PATTERN);
|
|
972
964
|
const nextTask = taskMatch?.[1]?.trim() ?? "Continue processing task backlog";
|
|
973
|
-
console.log(`[opencode-immune] Multi-Cycle: CYCLE_COMPLETE detected (cycle ${state.cycleCount}/${MAX_CYCLES}). ` +
|
|
965
|
+
console.log(`[opencode-immune] Multi-Cycle: CYCLE_COMPLETE detected in text.complete (cycle ${state.cycleCount}/${MAX_CYCLES}). ` +
|
|
974
966
|
`Creating new session for: "${nextTask}"`);
|
|
975
967
|
// Delay to let commit finish
|
|
976
968
|
setTimeout(async () => {
|
|
977
969
|
try {
|
|
978
|
-
// Create a new session
|
|
979
970
|
const createResult = await state.input.client.session.create({
|
|
980
971
|
body: {
|
|
981
972
|
title: `Ultrawork Cycle ${state.cycleCount + 1}`,
|
|
982
973
|
},
|
|
983
974
|
});
|
|
984
|
-
// Extract new session ID from the response
|
|
985
975
|
const newSessionData = createResult?.data;
|
|
986
976
|
const newSessionID = newSessionData?.id;
|
|
987
977
|
if (!newSessionID) {
|
|
@@ -990,7 +980,6 @@ function createMultiCycleHandler(state) {
|
|
|
990
980
|
}
|
|
991
981
|
console.log(`[opencode-immune] Multi-Cycle: New session created: ${newSessionID}`);
|
|
992
982
|
await addManagedUltraworkSession(state, newSessionID);
|
|
993
|
-
// Send bootstrap prompt to the new session
|
|
994
983
|
await state.input.client.session.promptAsync({
|
|
995
984
|
body: {
|
|
996
985
|
agent: ULTRAWORK_AGENT,
|
|
@@ -1008,14 +997,35 @@ function createMultiCycleHandler(state) {
|
|
|
1008
997
|
catch (err) {
|
|
1009
998
|
console.error("[opencode-immune] Multi-Cycle: Failed to create new session or send prompt:", err);
|
|
1010
999
|
}
|
|
1011
|
-
}, 8_000);
|
|
1000
|
+
}, 8_000);
|
|
1012
1001
|
}
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* chat.message part: handles rate-limit text detection in managed root sessions.
|
|
1006
|
+
* NOTE: chat.message fires for user messages only (output.message = UserMessage).
|
|
1007
|
+
* Signal detection (PRE_COMMIT/CYCLE_COMPLETE) moved to experimental.text.complete.
|
|
1008
|
+
*/
|
|
1009
|
+
function createMultiCycleHandler(state) {
|
|
1010
|
+
return async (input, output) => {
|
|
1011
|
+
const sessionID = input.sessionID;
|
|
1012
|
+
// Extract text content from parts
|
|
1013
|
+
const parts = output.parts ?? [];
|
|
1014
|
+
let messageContent = "";
|
|
1015
|
+
for (const p of parts) {
|
|
1016
|
+
if ("type" in p && p.type === "text" && "text" in p) {
|
|
1017
|
+
messageContent += " " + p.text;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
messageContent = messageContent.trim();
|
|
1021
|
+
if (!messageContent)
|
|
1022
|
+
return;
|
|
1013
1023
|
if (!isManagedRootUltraworkSession(state, sessionID))
|
|
1014
1024
|
return;
|
|
1015
1025
|
const managedSession = getManagedSession(state, sessionID);
|
|
1016
1026
|
// Rate-limit message detection (only for managed root sessions)
|
|
1027
|
+
const RATE_LIMIT_MESSAGE_PATTERN = /too many requests|rate_limit|rate limit/i;
|
|
1017
1028
|
if (sessionID &&
|
|
1018
|
-
messageRole === "assistant" &&
|
|
1019
1029
|
RATE_LIMIT_MESSAGE_PATTERN.test(messageContent)) {
|
|
1020
1030
|
if (managedSession && !managedSession.fallbackModel) {
|
|
1021
1031
|
await setSessionFallbackModel(state, sessionID, RATE_LIMIT_FALLBACK_MODEL);
|
|
@@ -1166,6 +1176,7 @@ async function server(input) {
|
|
|
1166
1176
|
"tool.execute.after": withErrorBoundary("tool.execute.after", compositeToolAfter(toolAfterHandlers)),
|
|
1167
1177
|
"experimental.chat.system.transform": withErrorBoundary("experimental.chat.system.transform", createSystemTransform(state)),
|
|
1168
1178
|
"experimental.session.compacting": withErrorBoundary("experimental.session.compacting", createCompactionHandler(state)),
|
|
1179
|
+
"experimental.text.complete": withErrorBoundary("experimental.text.complete", createTextCompleteHandler(state)),
|
|
1169
1180
|
};
|
|
1170
1181
|
}
|
|
1171
1182
|
exports.default = {
|