opencode-orchestrator 1.3.4 → 1.3.6
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/README.md +76 -370
- package/dist/agents/prompts/02_discovery/agents/discovery_commander.d.ts +1 -5
- package/dist/agents/prompts/02_discovery/agents/discovery_planner.d.ts +1 -5
- package/dist/agents/prompts/03_planning/agents/planning_commander.d.ts +1 -5
- package/dist/agents/prompts/04_execution/agents/execution_commander.d.ts +1 -5
- package/dist/agents/prompts/04_execution/agents/execution_worker.d.ts +1 -5
- package/dist/agents/prompts/04_execution/execution_error_handling.d.ts +1 -5
- package/dist/agents/prompts/05_verification/agents/verification_commander.d.ts +1 -5
- package/dist/agents/prompts/05_verification/verification_build.d.ts +1 -5
- package/dist/agents/prompts/05_verification/verification_test.d.ts +1 -5
- package/dist/agents/prompts/06_mission/agents/mission_commander.d.ts +1 -5
- package/dist/agents/prompts/06_mission/mission_lifecycle.d.ts +1 -5
- package/dist/agents/prompts/07_agents/commander/commander_mandate.d.ts +1 -5
- package/dist/agents/prompts/07_agents/planner/planner_mandate.d.ts +1 -5
- package/dist/agents/prompts/07_agents/reviewer/reviewer_mandate.d.ts +1 -5
- package/dist/agents/prompts/07_agents/worker/worker_mandate.d.ts +1 -5
- package/dist/agents/prompts/08_tools/tools_code_editing.d.ts +1 -5
- package/dist/agents/prompts/08_tools/tools_testing.d.ts +1 -5
- package/dist/core/orchestrator/state.d.ts +1 -1
- package/dist/hooks/features/mission-loop.d.ts +1 -0
- package/dist/index.js +151 -41
- package/dist/plugin-handlers/interfaces/session-state.d.ts +3 -0
- package/dist/scripts/postinstall.js +21 -10
- package/dist/scripts/preuninstall.js +20 -11
- package/dist/shared/core/constants/limits.d.ts +1 -1
- package/dist/shared/loop/constants/loop.d.ts +1 -1
- package/dist/shared/loop/constants/mission-control.d.ts +2 -2
- package/dist/shared/loop/interfaces/mission-loop.d.ts +1 -1
- package/dist/shared/session/constants/events/index.d.ts +2 -0
- package/dist/shared/session/constants/events/session-events.d.ts +3 -0
- package/package.json +5 -5
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Placeholder for code editing in tools category.
|
|
3
|
-
* Generated to match PROMPT_ARCHITECTURE_PROPOSAL.md
|
|
4
|
-
*/
|
|
5
|
-
export declare const TOOLS_CODE_EDITING = "\n# TODO: Implement code editing specific prompt\n";
|
|
1
|
+
export declare const TOOLS_CODE_EDITING = "\n# Code Editing Tools\n\n- Read the surrounding file before editing so imports, exports, formatting, and local conventions stay coherent.\n- Use structured parsers or existing helpers when they are available for the file type.\n- After editing, re-open the changed file and verify the connected import/export and configuration paths.\n";
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Placeholder for testing in tools category.
|
|
3
|
-
* Generated to match PROMPT_ARCHITECTURE_PROPOSAL.md
|
|
4
|
-
*/
|
|
5
|
-
export declare const TOOLS_TESTING = "\n# TODO: Implement testing specific prompt\n";
|
|
1
|
+
export declare const TOOLS_TESTING = "\n# Testing Tools\n\n- Run the narrowest meaningful test first, then broaden when shared behavior or release artifacts are affected.\n- Prefer deterministic fixtures, isolated temporary directories, and explicit assertions over timing assumptions.\n- Record the exact command and observed result in the mission evidence.\n";
|
|
@@ -11,6 +11,7 @@ export declare class MissionControlHook implements AssistantDoneHook, ChatMessag
|
|
|
11
11
|
name: "MissionLoop";
|
|
12
12
|
execute(ctx: HookContext, text: string): Promise<any>;
|
|
13
13
|
private handleChatCommand;
|
|
14
|
+
private cancelMission;
|
|
14
15
|
private handleMissionProgress;
|
|
15
16
|
private buildContinuationResponse;
|
|
16
17
|
private handleMissionComplete;
|
package/dist/index.js
CHANGED
|
@@ -199,7 +199,7 @@ var init_limits = __esm({
|
|
|
199
199
|
"use strict";
|
|
200
200
|
LIMITS = {
|
|
201
201
|
/** Maximum mission loop iterations */
|
|
202
|
-
MAX_ITERATIONS:
|
|
202
|
+
MAX_ITERATIONS: 1e9,
|
|
203
203
|
/** Default scan limit for file listing */
|
|
204
204
|
DEFAULT_SCAN_LIMIT: 20,
|
|
205
205
|
/** Max message history to check for conclusion */
|
|
@@ -1477,6 +1477,9 @@ var init_session_events = __esm({
|
|
|
1477
1477
|
"use strict";
|
|
1478
1478
|
SESSION_EVENTS = {
|
|
1479
1479
|
IDLE: "session.idle",
|
|
1480
|
+
STATUS: "session.status",
|
|
1481
|
+
UPDATED: "session.updated",
|
|
1482
|
+
COMPACTED: "session.compacted",
|
|
1480
1483
|
BUSY: "session.busy",
|
|
1481
1484
|
ERROR: "session.error",
|
|
1482
1485
|
DELETED: "session.deleted",
|
|
@@ -36896,6 +36899,9 @@ function ensureSessionInitialized(sessions, sessionID, directory) {
|
|
|
36896
36899
|
startTime: now,
|
|
36897
36900
|
lastStepTime: now,
|
|
36898
36901
|
lastCompletedMessageID: void 0,
|
|
36902
|
+
lastUserMessageAt: void 0,
|
|
36903
|
+
lastAssistantCompletedAt: void 0,
|
|
36904
|
+
lastAbortAt: void 0,
|
|
36899
36905
|
tokens: { totalInput: 0, totalOutput: 0, estimatedCost: 0 }
|
|
36900
36906
|
};
|
|
36901
36907
|
if (directory) {
|
|
@@ -36950,6 +36956,14 @@ function isMissionActive(sessionID, directory) {
|
|
|
36950
36956
|
}
|
|
36951
36957
|
return false;
|
|
36952
36958
|
}
|
|
36959
|
+
function deactivateMissionState(sessionID) {
|
|
36960
|
+
const stateSession = state.sessions.get(sessionID);
|
|
36961
|
+
if (stateSession) {
|
|
36962
|
+
stateSession.enabled = false;
|
|
36963
|
+
}
|
|
36964
|
+
state.missionActive = false;
|
|
36965
|
+
log(`[SessionManager] Mission Deactivated: ${sessionID}`);
|
|
36966
|
+
}
|
|
36953
36967
|
var COST_PER_1K_INPUT = 3e-3;
|
|
36954
36968
|
var COST_PER_1K_OUTPUT = 0.015;
|
|
36955
36969
|
function updateSessionTokens(sessions, sessionID, inputLen, outputLen) {
|
|
@@ -37712,7 +37726,12 @@ var MissionControlHook = class {
|
|
|
37712
37726
|
// -------------------------------------------------------------------------------
|
|
37713
37727
|
async handleChatCommand(ctx, message) {
|
|
37714
37728
|
const parsed = detectSlashCommand(message);
|
|
37715
|
-
if (!parsed
|
|
37729
|
+
if (!parsed) return null;
|
|
37730
|
+
if (parsed.command === COMMAND_NAMES.CANCEL || parsed.command === COMMAND_NAMES.STOP) {
|
|
37731
|
+
await this.cancelMission(ctx);
|
|
37732
|
+
return { action: HOOK_ACTIONS.INTERCEPT };
|
|
37733
|
+
}
|
|
37734
|
+
if (parsed.command !== COMMAND_NAMES.TASK) return null;
|
|
37716
37735
|
const command = COMMANDS[parsed.command];
|
|
37717
37736
|
const { sessionID, sessions, directory } = ctx;
|
|
37718
37737
|
log(MISSION_MESSAGES.START_LOG);
|
|
@@ -37730,6 +37749,17 @@ var MissionControlHook = class {
|
|
|
37730
37749
|
}
|
|
37731
37750
|
return { action: HOOK_ACTIONS.PROCESS };
|
|
37732
37751
|
}
|
|
37752
|
+
async cancelMission(ctx) {
|
|
37753
|
+
const { sessionID, sessions, directory } = ctx;
|
|
37754
|
+
log(MISSION_MESSAGES.CANCEL_LOG);
|
|
37755
|
+
await cancelMissionLoop(directory, sessionID);
|
|
37756
|
+
deactivateMissionState(sessionID);
|
|
37757
|
+
clearSession2(sessionID);
|
|
37758
|
+
const session = sessions.get(sessionID);
|
|
37759
|
+
if (isRecord(session)) {
|
|
37760
|
+
session.active = false;
|
|
37761
|
+
}
|
|
37762
|
+
}
|
|
37733
37763
|
// -------------------------------------------------------------------------------
|
|
37734
37764
|
// 2. Done Logic: Check Completion & Auto-Continue
|
|
37735
37765
|
// -------------------------------------------------------------------------------
|
|
@@ -37782,6 +37812,9 @@ var MissionControlHook = class {
|
|
|
37782
37812
|
// 4. Helper: Build Continuation Response
|
|
37783
37813
|
// -------------------------------------------------------------------------------
|
|
37784
37814
|
buildContinuationResponse(session, sessionID) {
|
|
37815
|
+
if (!isMissionSessionState(session)) {
|
|
37816
|
+
return { action: HOOK_ACTIONS.CONTINUE };
|
|
37817
|
+
}
|
|
37785
37818
|
const now = Date.now();
|
|
37786
37819
|
const stepDuration = formatElapsedTime(session.lastStepTime, now);
|
|
37787
37820
|
const totalElapsed = formatElapsedTime(session.startTime, now);
|
|
@@ -37839,6 +37872,12 @@ var MissionControlHook = class {
|
|
|
37839
37872
|
}
|
|
37840
37873
|
}
|
|
37841
37874
|
};
|
|
37875
|
+
function isRecord(value) {
|
|
37876
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37877
|
+
}
|
|
37878
|
+
function isMissionSessionState(value) {
|
|
37879
|
+
return isRecord(value) && typeof value.step === "number" && typeof value.startTime === "number" && typeof value.lastStepTime === "number";
|
|
37880
|
+
}
|
|
37842
37881
|
|
|
37843
37882
|
// src/hooks/custom/strict-role-guard.ts
|
|
37844
37883
|
init_shared();
|
|
@@ -38557,6 +38596,13 @@ function handleSessionError2(sessionID, error95) {
|
|
|
38557
38596
|
}
|
|
38558
38597
|
cancelCountdown(sessionID);
|
|
38559
38598
|
}
|
|
38599
|
+
function handleAbort(sessionID) {
|
|
38600
|
+
const state2 = getState3(sessionID);
|
|
38601
|
+
state2.isAborting = true;
|
|
38602
|
+
state2.abortDetectedAt = Date.now();
|
|
38603
|
+
cancelCountdown(sessionID);
|
|
38604
|
+
log("[todo-continuation] Marked as aborting", { sessionID });
|
|
38605
|
+
}
|
|
38560
38606
|
function cleanupSession2(sessionID) {
|
|
38561
38607
|
cancelCountdown(sessionID);
|
|
38562
38608
|
sessionStates2.delete(sessionID);
|
|
@@ -41137,11 +41183,11 @@ var CONCURRENCY_KEYS = [
|
|
|
41137
41183
|
"providerConcurrency",
|
|
41138
41184
|
"modelConcurrency"
|
|
41139
41185
|
];
|
|
41140
|
-
function
|
|
41186
|
+
function isRecord2(value) {
|
|
41141
41187
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41142
41188
|
}
|
|
41143
41189
|
function readLimitMap(value) {
|
|
41144
|
-
if (!
|
|
41190
|
+
if (!isRecord2(value)) return void 0;
|
|
41145
41191
|
const result = {};
|
|
41146
41192
|
for (const [key, limit] of Object.entries(value)) {
|
|
41147
41193
|
if (typeof limit === "number" && Number.isFinite(limit) && limit >= 0) {
|
|
@@ -41151,7 +41197,7 @@ function readLimitMap(value) {
|
|
|
41151
41197
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
41152
41198
|
}
|
|
41153
41199
|
function extractConcurrencyConfig(source) {
|
|
41154
|
-
if (!
|
|
41200
|
+
if (!isRecord2(source)) return {};
|
|
41155
41201
|
const config3 = {};
|
|
41156
41202
|
if (typeof source.defaultConcurrency === "number" && source.defaultConcurrency >= 0) {
|
|
41157
41203
|
config3.defaultConcurrency = source.defaultConcurrency;
|
|
@@ -41165,7 +41211,7 @@ function extractConcurrencyConfig(source) {
|
|
|
41165
41211
|
return config3;
|
|
41166
41212
|
}
|
|
41167
41213
|
function hasConcurrencyConfig(source) {
|
|
41168
|
-
if (!
|
|
41214
|
+
if (!isRecord2(source)) return false;
|
|
41169
41215
|
return CONCURRENCY_KEYS.some((key) => source[key] !== void 0);
|
|
41170
41216
|
}
|
|
41171
41217
|
function mergeConcurrencyConfig(base, override) {
|
|
@@ -41189,14 +41235,14 @@ function mergeConcurrencyConfig(base, override) {
|
|
|
41189
41235
|
|
|
41190
41236
|
// src/core/config/plugin-options.ts
|
|
41191
41237
|
function parseOrchestratorPluginOptions(options) {
|
|
41192
|
-
const source =
|
|
41238
|
+
const source = isRecord3(options) ? options : {};
|
|
41193
41239
|
return {
|
|
41194
41240
|
concurrency: extractConcurrencyConfig(source),
|
|
41195
41241
|
missionLoop: readMissionLoopOptions(source.missionLoop)
|
|
41196
41242
|
};
|
|
41197
41243
|
}
|
|
41198
41244
|
function readMissionLoopOptions(value) {
|
|
41199
|
-
if (!
|
|
41245
|
+
if (!isRecord3(value)) return DEFAULT_MISSION_RUNTIME_OPTIONS;
|
|
41200
41246
|
return {
|
|
41201
41247
|
ledger: readBoolean(value.ledger, DEFAULT_MISSION_RUNTIME_OPTIONS.ledger),
|
|
41202
41248
|
markdownMemory: readBoolean(value.markdownMemory, DEFAULT_MISSION_RUNTIME_OPTIONS.markdownMemory),
|
|
@@ -41212,7 +41258,7 @@ function readBoolean(value, fallback) {
|
|
|
41212
41258
|
function readPositiveInteger(value, fallback) {
|
|
41213
41259
|
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : fallback;
|
|
41214
41260
|
}
|
|
41215
|
-
function
|
|
41261
|
+
function isRecord3(value) {
|
|
41216
41262
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41217
41263
|
}
|
|
41218
41264
|
|
|
@@ -41259,6 +41305,7 @@ function createChatMessageHandler(ctx) {
|
|
|
41259
41305
|
const sessionID = msgInput.sessionID;
|
|
41260
41306
|
const agentName = (msgInput.agent || "").toLowerCase();
|
|
41261
41307
|
log("[chat-message-handler] hook triggered", { sessionID, agent: agentName, textLength: originalText.length });
|
|
41308
|
+
markUserMessage(sessions, sessionID);
|
|
41262
41309
|
if (sessionID && !sessions.has(sessionID)) {
|
|
41263
41310
|
}
|
|
41264
41311
|
const hooks = HookRegistry.getInstance();
|
|
@@ -41269,6 +41316,7 @@ function createChatMessageHandler(ctx) {
|
|
|
41269
41316
|
};
|
|
41270
41317
|
const hookResult = await hooks.executeChat(hookContext, originalText);
|
|
41271
41318
|
if (hookResult.action === HOOK_ACTIONS.INTERCEPT) {
|
|
41319
|
+
parts.splice(0, parts.length);
|
|
41272
41320
|
return;
|
|
41273
41321
|
}
|
|
41274
41322
|
if (hookResult.modifiedMessage) {
|
|
@@ -41276,6 +41324,11 @@ function createChatMessageHandler(ctx) {
|
|
|
41276
41324
|
}
|
|
41277
41325
|
};
|
|
41278
41326
|
}
|
|
41327
|
+
function markUserMessage(sessions, sessionID) {
|
|
41328
|
+
const session = sessions.get(sessionID);
|
|
41329
|
+
if (!session) return;
|
|
41330
|
+
session.lastUserMessageAt = Date.now();
|
|
41331
|
+
}
|
|
41279
41332
|
|
|
41280
41333
|
// src/plugin-handlers/config-handler.ts
|
|
41281
41334
|
init_shared();
|
|
@@ -41327,7 +41380,7 @@ This plugin runs in "Claude Code Compatibility Mode".
|
|
|
41327
41380
|
}
|
|
41328
41381
|
|
|
41329
41382
|
// src/plugin-handlers/config-handler.ts
|
|
41330
|
-
function
|
|
41383
|
+
function isRecord4(value) {
|
|
41331
41384
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41332
41385
|
}
|
|
41333
41386
|
function isPermissionAction(value) {
|
|
@@ -41337,10 +41390,10 @@ function mergePermission(globalPermission, agentPermission) {
|
|
|
41337
41390
|
if (agentPermission === void 0) {
|
|
41338
41391
|
return globalPermission;
|
|
41339
41392
|
}
|
|
41340
|
-
if (
|
|
41393
|
+
if (isRecord4(globalPermission) && isRecord4(agentPermission)) {
|
|
41341
41394
|
return { ...globalPermission, ...agentPermission };
|
|
41342
41395
|
}
|
|
41343
|
-
if (isPermissionAction(globalPermission) &&
|
|
41396
|
+
if (isPermissionAction(globalPermission) && isRecord4(agentPermission)) {
|
|
41344
41397
|
return { "*": globalPermission, ...agentPermission };
|
|
41345
41398
|
}
|
|
41346
41399
|
return agentPermission;
|
|
@@ -42045,9 +42098,11 @@ async function handleMissionIdle(client, directory, sessionID, mainSessionID) {
|
|
|
42045
42098
|
}, MISSION_CONTROL.DEFAULT_COUNTDOWN_SECONDS * 1e3);
|
|
42046
42099
|
}
|
|
42047
42100
|
function handleUserMessage2(sessionID) {
|
|
42101
|
+
const state2 = sessionStateStore.getState(sessionID);
|
|
42102
|
+
state2.isAborting = false;
|
|
42048
42103
|
sessionStateStore.cancelCountdown(sessionID);
|
|
42049
42104
|
}
|
|
42050
|
-
function
|
|
42105
|
+
function handleAbort2(sessionID) {
|
|
42051
42106
|
const state2 = sessionStateStore.getState(sessionID);
|
|
42052
42107
|
state2.isAborting = true;
|
|
42053
42108
|
sessionStateStore.cancelCountdown(sessionID);
|
|
@@ -42148,7 +42203,7 @@ function createEventHandler(ctx) {
|
|
|
42148
42203
|
const error95 = event.properties?.error;
|
|
42149
42204
|
if (sessionID) {
|
|
42150
42205
|
handleSessionError2(sessionID, error95);
|
|
42151
|
-
|
|
42206
|
+
handleAbort2(sessionID);
|
|
42152
42207
|
}
|
|
42153
42208
|
if (sessionID && error95) {
|
|
42154
42209
|
const recovered = await handleSessionError(
|
|
@@ -42175,6 +42230,7 @@ function createEventHandler(ctx) {
|
|
|
42175
42230
|
if (sessionID && role === MESSAGE_ROLES.ASSISTANT) {
|
|
42176
42231
|
markRecoveryComplete(sessionID);
|
|
42177
42232
|
if (messageInfo?.id && messageInfo.time?.completed) {
|
|
42233
|
+
markAssistantCompleted(sessions, sessionID);
|
|
42178
42234
|
await handleCompletedAssistantMessage(ctx, sessionID, messageInfo.id);
|
|
42179
42235
|
}
|
|
42180
42236
|
}
|
|
@@ -42186,35 +42242,75 @@ function createEventHandler(ctx) {
|
|
|
42186
42242
|
if (event.type === SESSION_EVENTS.IDLE) {
|
|
42187
42243
|
const sessionID = event.properties?.sessionID || "";
|
|
42188
42244
|
if (sessionID) {
|
|
42189
|
-
|
|
42190
|
-
|
|
42191
|
-
|
|
42192
|
-
|
|
42193
|
-
|
|
42194
|
-
|
|
42195
|
-
|
|
42196
|
-
|
|
42197
|
-
directory,
|
|
42198
|
-
sessionID,
|
|
42199
|
-
sessionID
|
|
42200
|
-
).catch(() => {
|
|
42201
|
-
});
|
|
42202
|
-
} else {
|
|
42203
|
-
await handleSessionIdle(
|
|
42204
|
-
client,
|
|
42205
|
-
directory,
|
|
42206
|
-
sessionID,
|
|
42207
|
-
sessionID
|
|
42208
|
-
).catch(() => {
|
|
42209
|
-
});
|
|
42210
|
-
}
|
|
42211
|
-
}
|
|
42212
|
-
}, 500);
|
|
42213
|
-
}
|
|
42245
|
+
scheduleIdleContinuation(ctx, sessionID);
|
|
42246
|
+
}
|
|
42247
|
+
}
|
|
42248
|
+
if (event.type === SESSION_EVENTS.STATUS) {
|
|
42249
|
+
const properties = event.properties;
|
|
42250
|
+
const sessionID = properties?.sessionID ?? "";
|
|
42251
|
+
if (sessionID && properties?.status?.type === "idle") {
|
|
42252
|
+
scheduleIdleContinuation(ctx, sessionID);
|
|
42214
42253
|
}
|
|
42215
42254
|
}
|
|
42216
42255
|
};
|
|
42217
42256
|
}
|
|
42257
|
+
function markAssistantCompleted(sessions, sessionID) {
|
|
42258
|
+
const session = sessions.get(sessionID);
|
|
42259
|
+
if (!session) return;
|
|
42260
|
+
session.lastAssistantCompletedAt = Date.now();
|
|
42261
|
+
}
|
|
42262
|
+
function shouldContinueAfterIdle(session) {
|
|
42263
|
+
if (!session?.active || !session.lastAssistantCompletedAt) {
|
|
42264
|
+
return false;
|
|
42265
|
+
}
|
|
42266
|
+
if (session.lastUserMessageAt && session.lastAssistantCompletedAt < session.lastUserMessageAt) {
|
|
42267
|
+
return false;
|
|
42268
|
+
}
|
|
42269
|
+
if (session.lastAbortAt && session.lastAssistantCompletedAt < session.lastAbortAt) {
|
|
42270
|
+
return false;
|
|
42271
|
+
}
|
|
42272
|
+
return true;
|
|
42273
|
+
}
|
|
42274
|
+
function markAbort(sessions, sessionID) {
|
|
42275
|
+
const session = sessions.get(sessionID);
|
|
42276
|
+
if (session) {
|
|
42277
|
+
session.lastAbortAt = Date.now();
|
|
42278
|
+
}
|
|
42279
|
+
handleAbort(sessionID);
|
|
42280
|
+
handleAbort2(sessionID);
|
|
42281
|
+
}
|
|
42282
|
+
function scheduleIdleContinuation(ctx, sessionID) {
|
|
42283
|
+
const { client, directory, sessions } = ctx;
|
|
42284
|
+
if (!sessions.has(sessionID)) return;
|
|
42285
|
+
setTimeout(async () => {
|
|
42286
|
+
const session = sessions.get(sessionID);
|
|
42287
|
+
if (!shouldContinueAfterIdle(session)) {
|
|
42288
|
+
markAbort(sessions, sessionID);
|
|
42289
|
+
return;
|
|
42290
|
+
}
|
|
42291
|
+
if (isLoopActive(directory, sessionID)) {
|
|
42292
|
+
try {
|
|
42293
|
+
await handleMissionIdle(
|
|
42294
|
+
client,
|
|
42295
|
+
directory,
|
|
42296
|
+
sessionID,
|
|
42297
|
+
sessionID
|
|
42298
|
+
);
|
|
42299
|
+
} catch {
|
|
42300
|
+
}
|
|
42301
|
+
return;
|
|
42302
|
+
}
|
|
42303
|
+
try {
|
|
42304
|
+
await handleSessionIdle(
|
|
42305
|
+
client,
|
|
42306
|
+
directory,
|
|
42307
|
+
sessionID,
|
|
42308
|
+
sessionID
|
|
42309
|
+
);
|
|
42310
|
+
} catch {
|
|
42311
|
+
}
|
|
42312
|
+
}, 500);
|
|
42313
|
+
}
|
|
42218
42314
|
|
|
42219
42315
|
// src/plugin-handlers/tool-execute-handler.ts
|
|
42220
42316
|
init_logger();
|
|
@@ -42985,6 +43081,20 @@ Use \`delegate_task\` with background=true for parallel work.
|
|
|
42985
43081
|
// src/index.ts
|
|
42986
43082
|
var require2 = createRequire(import.meta.url);
|
|
42987
43083
|
var { version: PLUGIN_VERSION } = require2("../package.json");
|
|
43084
|
+
function isRecord5(value) {
|
|
43085
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
43086
|
+
}
|
|
43087
|
+
function readStringField(source, key) {
|
|
43088
|
+
const value = source[key];
|
|
43089
|
+
return typeof value === "string" ? value : void 0;
|
|
43090
|
+
}
|
|
43091
|
+
function readCreatedSessionID(properties) {
|
|
43092
|
+
if (!isRecord5(properties)) return void 0;
|
|
43093
|
+
const directID = readStringField(properties, "sessionID") ?? readStringField(properties, "id");
|
|
43094
|
+
if (directID) return directID;
|
|
43095
|
+
const info = properties.info;
|
|
43096
|
+
return isRecord5(info) ? readStringField(info, "sessionID") : void 0;
|
|
43097
|
+
}
|
|
42988
43098
|
var OrchestratorPlugin = async (input, options) => {
|
|
42989
43099
|
const { directory, client } = input;
|
|
42990
43100
|
const orchestratorOptions = parseOrchestratorPluginOptions(options);
|
|
@@ -43044,8 +43154,8 @@ var OrchestratorPlugin = async (input, options) => {
|
|
|
43044
43154
|
event: async (payload) => {
|
|
43045
43155
|
const result = await createEventHandler(handlerContext)(payload);
|
|
43046
43156
|
const { event } = payload;
|
|
43047
|
-
if (event.type === SESSION_EVENTS.CREATED
|
|
43048
|
-
const sessionID = event.properties
|
|
43157
|
+
if (event.type === SESSION_EVENTS.CREATED) {
|
|
43158
|
+
const sessionID = readCreatedSessionID(event.properties);
|
|
43049
43159
|
if (sessionID) {
|
|
43050
43160
|
todoSync.registerSession(sessionID);
|
|
43051
43161
|
}
|
|
@@ -8,6 +8,9 @@ export interface SessionState {
|
|
|
8
8
|
startTime: number;
|
|
9
9
|
lastStepTime: number;
|
|
10
10
|
lastCompletedMessageID?: string;
|
|
11
|
+
lastUserMessageAt?: number;
|
|
12
|
+
lastAssistantCompletedAt?: number;
|
|
13
|
+
lastAbortAt?: number;
|
|
11
14
|
tokens: {
|
|
12
15
|
totalInput: number;
|
|
13
16
|
totalOutput: number;
|
|
@@ -1459,8 +1459,22 @@ function formatError(err, context) {
|
|
|
1459
1459
|
return `Failed to ${context}: ${String(err)}`;
|
|
1460
1460
|
}
|
|
1461
1461
|
var PLUGIN_NAME = "opencode-orchestrator";
|
|
1462
|
-
function
|
|
1463
|
-
return
|
|
1462
|
+
function isRecord(value) {
|
|
1463
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1464
|
+
}
|
|
1465
|
+
function isPluginTuple(value) {
|
|
1466
|
+
return Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && isRecord(value[1]);
|
|
1467
|
+
}
|
|
1468
|
+
function isPluginEntry(value) {
|
|
1469
|
+
return typeof value === "string" || isPluginTuple(value);
|
|
1470
|
+
}
|
|
1471
|
+
function getPluginName(entry) {
|
|
1472
|
+
return typeof entry === "string" ? entry : entry[0];
|
|
1473
|
+
}
|
|
1474
|
+
function isOurPluginEntry(entry) {
|
|
1475
|
+
if (!isPluginEntry(entry)) return false;
|
|
1476
|
+
const pluginName = getPluginName(entry);
|
|
1477
|
+
return pluginName === PLUGIN_NAME || pluginName.startsWith(`${PLUGIN_NAME}@`);
|
|
1464
1478
|
}
|
|
1465
1479
|
function getConfigFileCandidates(configDir) {
|
|
1466
1480
|
return [join(configDir, "opencode.jsonc"), join(configDir, "opencode.json")];
|
|
@@ -1578,8 +1592,8 @@ function validateConfig(config) {
|
|
|
1578
1592
|
return false;
|
|
1579
1593
|
}
|
|
1580
1594
|
if (config.plugin) {
|
|
1581
|
-
for (const
|
|
1582
|
-
if (
|
|
1595
|
+
for (const entry of config.plugin) {
|
|
1596
|
+
if (!isPluginEntry(entry)) {
|
|
1583
1597
|
return false;
|
|
1584
1598
|
}
|
|
1585
1599
|
}
|
|
@@ -1672,10 +1686,7 @@ function registerInConfig(configDir) {
|
|
|
1672
1686
|
config["$schema"] = "https://opencode.ai/config.json";
|
|
1673
1687
|
}
|
|
1674
1688
|
}
|
|
1675
|
-
const hasPlugin = config.plugin.some((
|
|
1676
|
-
if (typeof p !== "string") return false;
|
|
1677
|
-
return isOurPluginEntry(p);
|
|
1678
|
-
});
|
|
1689
|
+
const hasPlugin = config.plugin.some((entry) => isOurPluginEntry(entry));
|
|
1679
1690
|
if (hasPlugin) {
|
|
1680
1691
|
log("Plugin already registered", { configFile });
|
|
1681
1692
|
return { success: false, backupFile };
|
|
@@ -1693,7 +1704,7 @@ function registerInConfig(configDir) {
|
|
|
1693
1704
|
throw new Error(`Verification parse failed: ${verifyParsed.parseError ?? "unknown parse error"}`);
|
|
1694
1705
|
}
|
|
1695
1706
|
const verifyConfig = verifyParsed.config;
|
|
1696
|
-
if (!verifyConfig.plugin?.some((
|
|
1707
|
+
if (!verifyConfig.plugin?.some((entry) => isOurPluginEntry(entry))) {
|
|
1697
1708
|
throw new Error("Verification failed: plugin not found after write");
|
|
1698
1709
|
}
|
|
1699
1710
|
} catch (verifyError) {
|
|
@@ -1760,7 +1771,7 @@ try {
|
|
|
1760
1771
|
continue;
|
|
1761
1772
|
}
|
|
1762
1773
|
targetConfigDir = configDir;
|
|
1763
|
-
if (existing.config.plugin?.some((
|
|
1774
|
+
if (existing.config.plugin?.some((entry) => isOurPluginEntry(entry))) {
|
|
1764
1775
|
alreadyRegistered = true;
|
|
1765
1776
|
log("Plugin already registered in this location", { configFile: existing.file });
|
|
1766
1777
|
break;
|
|
@@ -1459,8 +1459,22 @@ function formatError(err, context) {
|
|
|
1459
1459
|
return `Failed to ${context}: ${String(err)}`;
|
|
1460
1460
|
}
|
|
1461
1461
|
var PLUGIN_NAME = "opencode-orchestrator";
|
|
1462
|
-
function
|
|
1463
|
-
return
|
|
1462
|
+
function isRecord(value) {
|
|
1463
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1464
|
+
}
|
|
1465
|
+
function isPluginTuple(value) {
|
|
1466
|
+
return Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && isRecord(value[1]);
|
|
1467
|
+
}
|
|
1468
|
+
function isPluginEntry(value) {
|
|
1469
|
+
return typeof value === "string" || isPluginTuple(value);
|
|
1470
|
+
}
|
|
1471
|
+
function getPluginName(entry) {
|
|
1472
|
+
return typeof entry === "string" ? entry : entry[0];
|
|
1473
|
+
}
|
|
1474
|
+
function isOurPluginEntry(entry) {
|
|
1475
|
+
if (!isPluginEntry(entry)) return false;
|
|
1476
|
+
const pluginName = getPluginName(entry);
|
|
1477
|
+
return pluginName === PLUGIN_NAME || pluginName.startsWith(`${PLUGIN_NAME}@`);
|
|
1464
1478
|
}
|
|
1465
1479
|
function getConfigFileCandidates(configDir) {
|
|
1466
1480
|
return [join(configDir, "opencode.jsonc"), join(configDir, "opencode.json")];
|
|
@@ -1562,8 +1576,8 @@ function validateConfig(config) {
|
|
|
1562
1576
|
return false;
|
|
1563
1577
|
}
|
|
1564
1578
|
if (config.plugin) {
|
|
1565
|
-
for (const
|
|
1566
|
-
if (
|
|
1579
|
+
for (const entry of config.plugin) {
|
|
1580
|
+
if (!isPluginEntry(entry)) {
|
|
1567
1581
|
return false;
|
|
1568
1582
|
}
|
|
1569
1583
|
}
|
|
@@ -1646,10 +1660,7 @@ function removeFromConfig(configDir) {
|
|
|
1646
1660
|
}
|
|
1647
1661
|
const originalLength = config.plugin.length;
|
|
1648
1662
|
const originalPlugins = [...config.plugin];
|
|
1649
|
-
config.plugin = config.plugin.filter((
|
|
1650
|
-
if (typeof p !== "string") return true;
|
|
1651
|
-
return !isOurPluginEntry(p);
|
|
1652
|
-
});
|
|
1663
|
+
config.plugin = config.plugin.filter((entry) => !isOurPluginEntry(entry));
|
|
1653
1664
|
if (config.plugin.length === originalLength) {
|
|
1654
1665
|
log("Plugin not found in config", { configFile });
|
|
1655
1666
|
return { success: false, backupFile };
|
|
@@ -1671,9 +1682,7 @@ function removeFromConfig(configDir) {
|
|
|
1671
1682
|
throw new Error(`Verification parse failed: ${verifyParsed.parseError ?? "unknown parse error"}`);
|
|
1672
1683
|
}
|
|
1673
1684
|
const verifyConfig = verifyParsed.config;
|
|
1674
|
-
const stillHasPlugin = verifyConfig.plugin?.some(
|
|
1675
|
-
(p) => typeof p === "string" && isOurPluginEntry(p)
|
|
1676
|
-
);
|
|
1685
|
+
const stillHasPlugin = verifyConfig.plugin?.some((entry) => isOurPluginEntry(entry));
|
|
1677
1686
|
if (stillHasPlugin) {
|
|
1678
1687
|
throw new Error("Verification failed: plugin still present after removal");
|
|
1679
1688
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export declare const LIMITS: {
|
|
5
5
|
/** Maximum mission loop iterations */
|
|
6
|
-
readonly MAX_ITERATIONS:
|
|
6
|
+
readonly MAX_ITERATIONS: 1000000000;
|
|
7
7
|
/** Default scan limit for file listing */
|
|
8
8
|
readonly DEFAULT_SCAN_LIMIT: 20;
|
|
9
9
|
/** Max message history to check for conclusion */
|
|
@@ -11,7 +11,7 @@ export declare const LOOP: {
|
|
|
11
11
|
/** Window to consider abort as recent */
|
|
12
12
|
readonly ABORT_WINDOW_MS: number;
|
|
13
13
|
/** Maximum iterations for mission loop */
|
|
14
|
-
readonly DEFAULT_MAX_ITERATIONS:
|
|
14
|
+
readonly DEFAULT_MAX_ITERATIONS: 1000000000;
|
|
15
15
|
/** Rust tool timeout */
|
|
16
16
|
readonly RUST_TOOL_TIMEOUT_MS: number;
|
|
17
17
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Mission Control Configuration
|
|
3
3
|
*/
|
|
4
4
|
export declare const MISSION_CONTROL: {
|
|
5
|
-
readonly DEFAULT_MAX_ITERATIONS:
|
|
5
|
+
readonly DEFAULT_MAX_ITERATIONS: 1000000000;
|
|
6
6
|
readonly DEFAULT_COUNTDOWN_SECONDS: 3;
|
|
7
7
|
readonly STATE_FILE: "loop-state.json";
|
|
8
8
|
readonly STOP_COMMAND: "/stop";
|
|
@@ -11,7 +11,7 @@ export declare const MISSION_CONTROL: {
|
|
|
11
11
|
};
|
|
12
12
|
/** @deprecated Use MISSION_CONTROL instead */
|
|
13
13
|
export declare const MISSION: {
|
|
14
|
-
readonly DEFAULT_MAX_ITERATIONS:
|
|
14
|
+
readonly DEFAULT_MAX_ITERATIONS: 1000000000;
|
|
15
15
|
readonly DEFAULT_COUNTDOWN_SECONDS: 3;
|
|
16
16
|
readonly STATE_FILE: "loop-state.json";
|
|
17
17
|
readonly STOP_COMMAND: "/stop";
|
|
@@ -30,7 +30,7 @@ export interface MissionLoopState {
|
|
|
30
30
|
lastContinuationAt?: string;
|
|
31
31
|
}
|
|
32
32
|
export interface MissionLoopOptions {
|
|
33
|
-
/** Maximum iterations before stopping (default:
|
|
33
|
+
/** Maximum iterations before stopping (default: 1,000,000,000) */
|
|
34
34
|
maxIterations?: number;
|
|
35
35
|
/** Countdown seconds before auto-continue (default: 3) */
|
|
36
36
|
countdownSeconds?: number;
|
|
@@ -10,6 +10,8 @@ export declare const EVENT_TYPES: {
|
|
|
10
10
|
readonly CACHED: "document.cached";
|
|
11
11
|
readonly EXPIRED: "document.expired";
|
|
12
12
|
readonly IDLE: "session.idle";
|
|
13
|
+
readonly STATUS: "session.status";
|
|
14
|
+
readonly COMPACTED: "session.compacted";
|
|
13
15
|
readonly BUSY: "session.busy";
|
|
14
16
|
readonly ERROR: "session.error";
|
|
15
17
|
readonly DELETED: "session.deleted";
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export declare const SESSION_EVENTS: {
|
|
5
5
|
readonly IDLE: "session.idle";
|
|
6
|
+
readonly STATUS: "session.status";
|
|
7
|
+
readonly UPDATED: "session.updated";
|
|
8
|
+
readonly COMPACTED: "session.compacted";
|
|
6
9
|
readonly BUSY: "session.busy";
|
|
7
10
|
readonly ERROR: "session.error";
|
|
8
11
|
readonly DELETED: "session.deleted";
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-orchestrator",
|
|
3
3
|
"displayName": "OpenCode Orchestrator",
|
|
4
|
-
"description": "
|
|
5
|
-
"version": "1.3.
|
|
4
|
+
"description": "Multi-agent mission control for OpenCode with Commander, Planner, Worker, and Reviewer workflows.",
|
|
5
|
+
"version": "1.3.6",
|
|
6
6
|
"author": "agnusdei1207",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"sync:readme-version": "node scripts/sync-readme-version.mjs",
|
|
60
60
|
"version": "node scripts/sync-readme-version.mjs --stage",
|
|
61
61
|
"release:preflight": "node scripts/release-preflight.mjs",
|
|
62
|
-
"release:patch": "node scripts/release-auth-check.mjs && node scripts/release-version.mjs patch && npm run release:preflight && npm run docker:rust-dist && npm run publish:token",
|
|
63
|
-
"release:minor": "node scripts/release-auth-check.mjs && node scripts/release-version.mjs minor && npm run release:preflight && npm run docker:rust-dist && npm run publish:token",
|
|
64
|
-
"release:major": "node scripts/release-auth-check.mjs && node scripts/release-version.mjs major && npm run release:preflight && npm run docker:rust-dist && npm run publish:token",
|
|
62
|
+
"release:patch": "node scripts/release-auth-check.mjs && node scripts/release-version.mjs patch && npm run release:preflight && npm run docker:rust-dist && node scripts/release-sync-artifacts.mjs && npm run publish:token",
|
|
63
|
+
"release:minor": "node scripts/release-auth-check.mjs && node scripts/release-version.mjs minor && npm run release:preflight && npm run docker:rust-dist && node scripts/release-sync-artifacts.mjs && npm run publish:token",
|
|
64
|
+
"release:major": "node scripts/release-auth-check.mjs && node scripts/release-version.mjs major && npm run release:preflight && npm run docker:rust-dist && node scripts/release-sync-artifacts.mjs && npm run publish:token",
|
|
65
65
|
"release:dry-run": "node scripts/release-preflight.mjs --allow-dirty --skip-branch --skip-version-check",
|
|
66
66
|
"release:push-tags": "git push origin main && git push origin --tags",
|
|
67
67
|
"release:clean": "shx rm -rf dist bin && docker compose down -v",
|