Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
"email": "hi@okis.dev"
},
"description": "Multi-model orchestration marketplace for Claude Code.",
"version": "0.0.38",
"version": "0.0.39",
"plugins": [
{
"name": "grok",
"source": "./plugins/grok",
"displayName": "Grok Companion",
"description": "Local Grok CLI delegation: task, review, resumable history, best-of-n tournaments, background jobs, stats, and setup health checks.",
"version": "0.0.38",
"version": "0.0.39",
"author": {
"name": "Harry Yep"
},
Expand All @@ -34,7 +34,7 @@
"source": "./plugins/codex",
"displayName": "Codex Companion",
"description": "First party local Codex CLI delegation for tasks, reviews, resumable threads, and durable background jobs.",
"version": "0.0.38",
"version": "0.0.39",
"author": {
"name": "Harry Yep"
},
Expand All @@ -55,7 +55,7 @@
"source": "./plugins/fusion",
"displayName": "Fusion Orchestrator",
"description": "Multi-model orchestration: tier agents, routing rules, blind panel, ultra fleet, model config, and drift doctor.",
"version": "0.0.38",
"version": "0.0.39",
"author": {
"name": "Harry Yep"
},
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# changelog

## 0.0.39

- the over budget stop gate settles instead of looping when the harness has already reaped a worker's runtime task: a `cancel_requested` record whose runtime id is absent from a provided `background_tasks` array settles as `task_reaped` through the same `settleReapedWorker` helper the no task found escape hatch uses, because that hatch never fires for this class; `TaskStop` on a reaped id returns a tool_use_error, which emits neither PostToolUse nor PostToolUseFailure, so the demand was unsatisfiable (observed live: two user interrupted workers held eight consecutive blocking stop rounds while twelve errored `TaskStop` and `TaskOutput` calls delivered zero hook events, and only SessionEnd closed the records)
- reaped settlements keep the stop hook's single output document invariant: the notice rides the block reason while live cancellations or missing runtime failures still demand, and emits alone only when nothing blocks, so a blocking decision can never be lost to a second stdout document
- PreToolUse stamps `cancelAttemptCount` on matching non terminal main session workers when `TaskStop` or `TaskOutput` targets their runtime id, and a stop input without any `background_tasks` array settles a cancellation after two unobserved attempts, covering harness inputs that omit the runtime task list

## 0.0.38

- Fable and Opus 5 are peer orchestrators: the routing preamble drops the Opus as fallback framing (switching is lateral; posture, gates, fleet defaults, and delegation never condition on which of the two is active), the readme reframes `best[1m]` floating as lateral within the top tier, and doctor treats a deliberate Opus 5 session as healthy instead of a fallback to recover
Expand Down
2 changes: 1 addition & 1 deletion plugins/codex/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "codex",
"displayName": "Codex Companion",
"version": "0.0.38",
"version": "0.0.39",
"description": "First party local Codex CLI delegation for tasks, reviews, resumable threads, and durable background jobs.",
"author": {
"name": "Harry Yep"
Expand Down
2 changes: 1 addition & 1 deletion plugins/fusion/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "fusion",
"displayName": "Fusion Orchestrator",
"version": "0.0.38",
"version": "0.0.39",
"description": "Multi-model orchestration: tier agents, routing rules, blind panel, ultra fleet, model config, and drift doctor.",
"author": {
"name": "Harry Yep"
Expand Down
72 changes: 59 additions & 13 deletions plugins/fusion/scripts/worker-lifecycle.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,22 @@ function canonicalToolResponseUsage(response) {
}

function handlePreToolUse(input, env) {
if (!input.agent_id && ["TaskStop", "TaskOutput"].includes(input.tool_name) && typeof input.tool_input?.task_id === "string") {
const taskId = input.tool_input.task_id;
const now = new Date().toISOString();
for (const record of readWorkerRecords(env, { strict: true }).filter((candidate) => candidate.sessionId === input.session_id && !isTerminalWorkerStatus(candidate.transportStatus) && [candidate.backgroundTaskId, candidate.agentId].includes(taskId))) {
updateLifecycleWorkerRecord(record.taskId, env, (current) => {
if (!current || current.sessionId !== input.session_id || isTerminalWorkerStatus(current.transportStatus) || ![current.backgroundTaskId, current.agentId].includes(taskId)) {
return null;
}
return {
...current,
cancelAttemptCount: (current.cancelAttemptCount ?? 0) + 1,
lastCancelAttemptAt: now
};
});
}
}
if (input.tool_name === "Agent" || input.tool_name === "Task") {
const agentType = input.tool_input?.subagent_type;
if (PEER_WRAPPER_AGENTS.has(agentType)) {
Expand Down Expand Up @@ -959,6 +975,16 @@ function settleQueuedVerdict(record, now, env) {
}
}

function settleReapedWorker(current, now, env) {
return settleQueuedVerdict({
...markWorkerCollected(current, WORKER_COLLECTION_METHODS.TASK_REAPED, now),
transportStatus: "failed",
failureKind: "task_reaped",
finishedAt: now,
lastActivityAt: now
}, now, env);
}

function handlePostToolUse(input, env, failed = false) {
if (!input.agent_id && ["Read", "TaskOutput", "TaskStop"].includes(input.tool_name)) {
const taskId = typeof input.tool_input?.task_id === "string" ? input.tool_input.task_id : null;
Expand All @@ -971,13 +997,7 @@ function handlePostToolUse(input, env, failed = false) {
if (!current || current.sessionId !== input.session_id || isTerminalWorkerStatus(current.transportStatus) || ![current.backgroundTaskId, current.agentId].includes(taskId)) {
return null;
}
return settleQueuedVerdict({
...markWorkerCollected(current, WORKER_COLLECTION_METHODS.TASK_REAPED, now),
transportStatus: "failed",
failureKind: "task_reaped",
finishedAt: now,
lastActivityAt: now
}, now, env);
return settleReapedWorker(current, now, env);
});
}
return;
Expand Down Expand Up @@ -1849,7 +1869,8 @@ function clearSettleOnlyAdvisory(sessionId, env) {
}

function handleStop(input, env) {
const tasks = Array.isArray(input.background_tasks) ? input.background_tasks : [];
const hasBackgroundTasks = Array.isArray(input.background_tasks);
const tasks = hasBackgroundTasks ? input.background_tasks : [];
reconcileTaskNotifications(input, env);
const initialRecords = sessionRecords(input.session_id, env);
if (initialRecords.every((record) => !terminalCollectionPending(record)) && collectorStopGate(input, env)) {
Expand Down Expand Up @@ -1893,13 +1914,38 @@ function handleStop(input, env) {
return settleQueuedVerdict({ ...current, transportStatus: "failed", failureKind: current.failureKind ?? "owner_lost", finishedAt: now }, now, env);
});
}
const cancelIds = cancellations.map((record) => record.backgroundTaskId ?? record.agentId).filter(Boolean);
if (cancelIds.length === 0) {
writeOutput(blockStop(`Fusion task${missingRuntimeIds.length === 1 ? "" : "s"} ${missingRuntimeIds.map((record) => record.taskId).join(", ")} failed before a runtime task id was available. Report the failure before finishing.`));
const reapedTaskIds = [];
for (const record of cancellations.filter((candidate) => candidate.backgroundTaskId || candidate.agentId)) {
if (hasBackgroundTasks ? runtimeTaskForRecord(record, tasks) : (record.cancelAttemptCount ?? 0) < 2) {
continue;
}
let settled = false;
updateLifecycleWorkerRecord(record.taskId, env, (current) => {
if (!current || isTerminalWorkerStatus(current.transportStatus)) {
return null;
}
settled = true;
return settleReapedWorker(current, now, env);
});
if (settled) {
reapedTaskIds.push(record.taskId);
}
}
const reapedSentence = reapedTaskIds.length > 0 ? `Fusion task IDs ${reapedTaskIds.join(", ")} were settled as task_reaped because the harness no longer tracks their runtime tasks.` : "";
const reapedSuffix = reapedSentence ? ` ${reapedSentence}` : "";
const cancelIds = cancellations.filter((record) => !reapedTaskIds.includes(record.taskId)).map((record) => record.backgroundTaskId ?? record.agentId).filter(Boolean);
if (cancelIds.length > 0) {
writeOutput(blockStop(`Call TaskStop for over-budget task${cancelIds.length === 1 ? "" : "s"} ${cancelIds.join(", ")} and report the cancellation before finishing.${reapedSuffix}`));
return;
}
if (missingRuntimeIds.length > 0) {
writeOutput(blockStop(`Fusion task${missingRuntimeIds.length === 1 ? "" : "s"} ${missingRuntimeIds.map((record) => record.taskId).join(", ")} failed before a runtime task id was available. Report the failure before finishing.${reapedSuffix}`));
return;
}
if (reapedSentence) {
writeOutput(hookOutput("Stop", reapedSentence));
return;
}
writeOutput(blockStop(`Call TaskStop for over-budget task${cancelIds.length === 1 ? "" : "s"} ${cancelIds.join(", ")} and report the cancellation before finishing.`));
return;
}
const currentRecords = sessionRecords(input.session_id, env);
const inFlight = armInFlightRecords(currentRecords, tasks, env);
Expand Down
2 changes: 1 addition & 1 deletion plugins/grok/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "grok",
"displayName": "Grok Companion",
"version": "0.0.38",
"version": "0.0.39",
"description": "Local Grok CLI delegation: task, review, resumable history, background jobs, stats, and setup health checks.",
"author": {
"name": "Harry Yep"
Expand Down
Loading
Loading