@khalilgharbaoui/opencode-claude-code-plugin 0.59.0 → 0.60.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/index.d.ts +7 -0
- package/dist/index.js +291 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +76 -5
package/dist/index.d.ts
CHANGED
|
@@ -1511,6 +1511,13 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
1511
1511
|
* claim taken: a choice must never land on the wrong child (h #g227).
|
|
1512
1512
|
*/
|
|
1513
1513
|
private resolveDispatchChoice;
|
|
1514
|
+
/**
|
|
1515
|
+
* The model the parent named for THIS session in its own `task` call, or
|
|
1516
|
+
* undefined (h #g254). The same two lookups as `resolveDispatchChoice`, and
|
|
1517
|
+
* the same reason for the order: a held record first, and the parent fetched
|
|
1518
|
+
* only for a child whose agent and first message match a recorded request.
|
|
1519
|
+
*/
|
|
1520
|
+
private resolveExplicitTaskModel;
|
|
1514
1521
|
/**
|
|
1515
1522
|
* Whether the ExitPlanMode approval bridge is live for this turn: the
|
|
1516
1523
|
* operator opted in AND opencode's registry actually has the `question`
|
package/dist/index.js
CHANGED
|
@@ -55,6 +55,7 @@ var V2_TOOLS = {
|
|
|
55
55
|
name: "subagent",
|
|
56
56
|
input: (input) => defined({
|
|
57
57
|
agent: input.subagent_type ?? input.agent,
|
|
58
|
+
model: input.model,
|
|
58
59
|
description: input.description,
|
|
59
60
|
prompt: input.prompt,
|
|
60
61
|
sessionID: input.task_id ?? input.sessionID,
|
|
@@ -2613,9 +2614,12 @@ function withoutAccountMarker(modelId) {
|
|
|
2613
2614
|
}
|
|
2614
2615
|
function qualifyModelName(wanted, referenceModelId) {
|
|
2615
2616
|
const base = withoutAccountMarker(wanted.trim());
|
|
2616
|
-
if (!
|
|
2617
|
+
if (!isRegisteredModelName(base)) return null;
|
|
2617
2618
|
return `${base}${accountMarker(referenceModelId)}`;
|
|
2618
2619
|
}
|
|
2620
|
+
function isRegisteredModelName(name) {
|
|
2621
|
+
return name.length > 0 && Object.hasOwn(defaultModels, name);
|
|
2622
|
+
}
|
|
2619
2623
|
function resolveAgentModel(agent, modelId, overrides) {
|
|
2620
2624
|
if (!agent) return modelId;
|
|
2621
2625
|
const record = (overrides?.records ?? registry)[agent];
|
|
@@ -3252,17 +3256,20 @@ function dispatchTasksFromCalls(calls, batchTasks) {
|
|
|
3252
3256
|
const out = [];
|
|
3253
3257
|
for (const call of calls) {
|
|
3254
3258
|
if (call.toolName === "task") {
|
|
3255
|
-
out.push(taskFromInput(call.toolCallId, null, call.input));
|
|
3259
|
+
if (!namesOwnModel(call.input)) out.push(taskFromInput(call.toolCallId, null, call.input));
|
|
3256
3260
|
continue;
|
|
3257
3261
|
}
|
|
3258
3262
|
if (call.toolName === "task_batch") {
|
|
3259
3263
|
for (const [index, task] of batchTasks(call.input).entries()) {
|
|
3260
|
-
out.push(taskFromInput(call.toolCallId, index, task));
|
|
3264
|
+
if (!namesOwnModel(task)) out.push(taskFromInput(call.toolCallId, index, task));
|
|
3261
3265
|
}
|
|
3262
3266
|
}
|
|
3263
3267
|
}
|
|
3264
3268
|
return out;
|
|
3265
3269
|
}
|
|
3270
|
+
function namesOwnModel(input) {
|
|
3271
|
+
return typeof input.model === "string" && input.model.trim() !== "";
|
|
3272
|
+
}
|
|
3266
3273
|
function taskFromInput(callId, batchIndex, input) {
|
|
3267
3274
|
return {
|
|
3268
3275
|
callId,
|
|
@@ -4718,6 +4725,13 @@ var TASK_INPUT_PROPERTIES = {
|
|
|
4718
4725
|
type: "string",
|
|
4719
4726
|
description: "The type of specialized agent to use for this task"
|
|
4720
4727
|
},
|
|
4728
|
+
// After the agent type, where opencode 2's own `subagent` tool puts it
|
|
4729
|
+
// (after `prompt`): first in the list it was the field the model read first.
|
|
4730
|
+
// Stripped on a host whose `task` has no such field (`applyTaskModelSupport`).
|
|
4731
|
+
model: {
|
|
4732
|
+
type: "string",
|
|
4733
|
+
description: "NEVER set this unless the user explicitly asked for a particular model or variant for this subagent. The exact opencode model reference, providerID/modelID, or providerID/modelID#variant to pick a variant. Use only an id the user gave you or one this conversation already used; never guess one. Omit it and opencode picks the model as usual."
|
|
4734
|
+
},
|
|
4721
4735
|
task_id: {
|
|
4722
4736
|
type: "string",
|
|
4723
4737
|
description: "Set this only if you mean to resume a previous task: pass the prior task_id to continue the same subagent session instead of creating a fresh one."
|
|
@@ -4740,6 +4754,71 @@ var TASK_INPUT_PROPERTIES = {
|
|
|
4740
4754
|
var TASK_INPUT_PROPERTIES_NO_BACKGROUND = Object.fromEntries(
|
|
4741
4755
|
Object.entries(TASK_INPUT_PROPERTIES).filter(([name]) => name !== "background")
|
|
4742
4756
|
);
|
|
4757
|
+
function liveTaskSupportsModel(taskParameters, dialect = "v1") {
|
|
4758
|
+
if (dialect === "v2") return true;
|
|
4759
|
+
const properties = taskParameters?.properties;
|
|
4760
|
+
if (!properties || typeof properties !== "object") return false;
|
|
4761
|
+
return "model" in properties;
|
|
4762
|
+
}
|
|
4763
|
+
function applyTaskModelSupport(tools, supported) {
|
|
4764
|
+
if (supported) return tools;
|
|
4765
|
+
const withoutModel = (schema) => {
|
|
4766
|
+
const properties = schema?.properties;
|
|
4767
|
+
if (!properties || typeof properties !== "object") return schema;
|
|
4768
|
+
return {
|
|
4769
|
+
...schema,
|
|
4770
|
+
properties: Object.fromEntries(
|
|
4771
|
+
Object.entries(properties).filter(([name]) => name !== "model")
|
|
4772
|
+
)
|
|
4773
|
+
};
|
|
4774
|
+
};
|
|
4775
|
+
return tools.map((tool) => {
|
|
4776
|
+
if (tool.name === "task") return { ...tool, inputSchema: withoutModel(tool.inputSchema) };
|
|
4777
|
+
if (tool.name !== TASK_BATCH_TOOL_NAME) return tool;
|
|
4778
|
+
const properties = tool.inputSchema.properties;
|
|
4779
|
+
const tasks = properties?.tasks;
|
|
4780
|
+
if (!tasks?.items || typeof tasks.items !== "object") return tool;
|
|
4781
|
+
return {
|
|
4782
|
+
...tool,
|
|
4783
|
+
inputSchema: {
|
|
4784
|
+
...tool.inputSchema,
|
|
4785
|
+
properties: {
|
|
4786
|
+
...properties,
|
|
4787
|
+
tasks: { ...tasks, items: withoutModel(tasks.items) }
|
|
4788
|
+
}
|
|
4789
|
+
}
|
|
4790
|
+
};
|
|
4791
|
+
});
|
|
4792
|
+
}
|
|
4793
|
+
function taskSchemaOffersModel(def) {
|
|
4794
|
+
const properties = def.inputSchema.properties;
|
|
4795
|
+
const schema = def.name === TASK_BATCH_TOOL_NAME ? properties?.tasks?.items : def.inputSchema;
|
|
4796
|
+
const fields = schema?.properties;
|
|
4797
|
+
return !!fields && typeof fields === "object" && "model" in fields;
|
|
4798
|
+
}
|
|
4799
|
+
function completeTaskModels(def, input, complete) {
|
|
4800
|
+
if (!complete || !taskSchemaOffersModel(def)) return;
|
|
4801
|
+
const tasks = def.name === TASK_BATCH_TOOL_NAME ? taskBatchTasks(input) : [input];
|
|
4802
|
+
for (const task of tasks) {
|
|
4803
|
+
if (task.model !== void 0) task.model = complete(task.model);
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4806
|
+
function taskModelInputError(def, input, guard) {
|
|
4807
|
+
const tasks = def.name === TASK_BATCH_TOOL_NAME ? taskBatchTasks(input) : [input];
|
|
4808
|
+
const offered = taskSchemaOffersModel(def);
|
|
4809
|
+
for (const task of tasks) {
|
|
4810
|
+
if (task.model === void 0) continue;
|
|
4811
|
+
if (!offered) {
|
|
4812
|
+
return "This opencode's task tool takes no per-call `model` (opencode 1.x has none), so no tasks were started. Omit `model`.";
|
|
4813
|
+
}
|
|
4814
|
+
if (typeof task.model !== "string") {
|
|
4815
|
+
return "`model` must be a string, providerID/modelID or providerID/modelID#variant, so no tasks were started.";
|
|
4816
|
+
}
|
|
4817
|
+
const refusal = guard?.(task.model);
|
|
4818
|
+
if (refusal) return refusal;
|
|
4819
|
+
}
|
|
4820
|
+
return null;
|
|
4821
|
+
}
|
|
4743
4822
|
var DEFAULT_PROXY_TOOLS = [
|
|
4744
4823
|
{
|
|
4745
4824
|
name: "bash",
|
|
@@ -5032,7 +5111,8 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
5032
5111
|
const params = request.params ?? {};
|
|
5033
5112
|
const toolName = String(params.name ?? "");
|
|
5034
5113
|
const input = params.arguments ?? {};
|
|
5035
|
-
|
|
5114
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
5115
|
+
if (!tool) {
|
|
5036
5116
|
writeJson(res, {
|
|
5037
5117
|
jsonrpc: "2.0",
|
|
5038
5118
|
id: requestId,
|
|
@@ -5050,6 +5130,14 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
5050
5130
|
return;
|
|
5051
5131
|
}
|
|
5052
5132
|
}
|
|
5133
|
+
if (toolName === "task" || toolName === TASK_BATCH_TOOL_NAME) {
|
|
5134
|
+
completeTaskModels(tool, input, options.completeTaskModel);
|
|
5135
|
+
const problem = taskModelInputError(tool, input, options.taskModelGuard);
|
|
5136
|
+
if (problem) {
|
|
5137
|
+
writeToolCallResult(res, requestId, { kind: "error", message: problem });
|
|
5138
|
+
return;
|
|
5139
|
+
}
|
|
5140
|
+
}
|
|
5053
5141
|
const interceptor = interceptors?.get(toolName);
|
|
5054
5142
|
if (interceptor) {
|
|
5055
5143
|
let intercepted;
|
|
@@ -5808,6 +5896,127 @@ function clearLedger(sessionId) {
|
|
|
5808
5896
|
ledgers.delete(sessionId);
|
|
5809
5897
|
}
|
|
5810
5898
|
|
|
5899
|
+
// src/task-model.ts
|
|
5900
|
+
var TASK_MODEL_REFERENCE = /^([^/#]+)\/([^#]+)(?:#([^#]+))?$/;
|
|
5901
|
+
function parseTaskModelReference(value) {
|
|
5902
|
+
if (typeof value !== "string") return null;
|
|
5903
|
+
const match = TASK_MODEL_REFERENCE.exec(value.trim());
|
|
5904
|
+
if (!match) return null;
|
|
5905
|
+
const [, providerID, modelID, variant] = match;
|
|
5906
|
+
return variant ? { providerID, modelID, variant } : { providerID, modelID };
|
|
5907
|
+
}
|
|
5908
|
+
function completeTaskModelReference(value, own) {
|
|
5909
|
+
if (typeof value !== "string") return value;
|
|
5910
|
+
const trimmed = value.trim();
|
|
5911
|
+
if (!trimmed || trimmed.includes("/")) return value;
|
|
5912
|
+
const hash = trimmed.indexOf("#");
|
|
5913
|
+
const id = hash === -1 ? trimmed : trimmed.slice(0, hash);
|
|
5914
|
+
const variant = hash === -1 ? void 0 : trimmed.slice(hash + 1);
|
|
5915
|
+
if (variant !== void 0 && (!variant || variant.includes("#"))) return value;
|
|
5916
|
+
const at = id.indexOf("@");
|
|
5917
|
+
const base = at === -1 ? id : id.slice(0, at);
|
|
5918
|
+
if (at !== -1 && id.slice(at + 1) !== own.accountMarker) return value;
|
|
5919
|
+
if (!own.isRegistered(base)) return value;
|
|
5920
|
+
const modelID = own.accountMarker ? `${base}@${own.accountMarker}` : base;
|
|
5921
|
+
return `${own.providerID}/${modelID}${variant ? `#${variant}` : ""}`;
|
|
5922
|
+
}
|
|
5923
|
+
function taskModelAccountRefusal(input) {
|
|
5924
|
+
if (!input.groups || input.crossGroup) return null;
|
|
5925
|
+
const reference = parseTaskModelReference(input.model);
|
|
5926
|
+
if (!reference) return null;
|
|
5927
|
+
const target = input.providers.get(reference.providerID);
|
|
5928
|
+
if (target === void 0) return null;
|
|
5929
|
+
if (accountsShareGroup(input.sourceAccount, target, input.groups)) return null;
|
|
5930
|
+
log.notice("task model refused: its account is in another account group", {
|
|
5931
|
+
account: target,
|
|
5932
|
+
reason: "another-account-group"
|
|
5933
|
+
});
|
|
5934
|
+
return `The model "${input.model}" runs on the Claude account "${target}", which is outside this conversation's account group, so no tasks were started. A subagent is handed task text that can quote this conversation, and accountGroups keeps that text inside one group. Omit \`model\`, or name a model on an account in this conversation's group.`;
|
|
5935
|
+
}
|
|
5936
|
+
var CLAIM_TTL_MS2 = 15 * 6e4;
|
|
5937
|
+
var MAX_CLAIMS2 = 512;
|
|
5938
|
+
var MAX_SESSION_MODELS = 512;
|
|
5939
|
+
var claims2 = [];
|
|
5940
|
+
var sessionModels = /* @__PURE__ */ new Map();
|
|
5941
|
+
function pruneClaims2(now) {
|
|
5942
|
+
for (let i = claims2.length - 1; i >= 0; i--) {
|
|
5943
|
+
if (now - claims2[i].createdAt > CLAIM_TTL_MS2) claims2.splice(i, 1);
|
|
5944
|
+
}
|
|
5945
|
+
while (claims2.length > MAX_CLAIMS2) claims2.shift();
|
|
5946
|
+
}
|
|
5947
|
+
function holdSessionModel(sessionId, model) {
|
|
5948
|
+
sessionModels.delete(sessionId);
|
|
5949
|
+
sessionModels.set(sessionId, model);
|
|
5950
|
+
while (sessionModels.size > MAX_SESSION_MODELS) {
|
|
5951
|
+
const oldest = sessionModels.keys().next().value;
|
|
5952
|
+
if (oldest === void 0) break;
|
|
5953
|
+
sessionModels.delete(oldest);
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
function noteExplicitTaskModels(parentSessionId, tasks, now = Date.now()) {
|
|
5957
|
+
let noted = 0;
|
|
5958
|
+
for (const task of tasks) {
|
|
5959
|
+
const reference = parseTaskModelReference(task.model);
|
|
5960
|
+
if (!reference) continue;
|
|
5961
|
+
const resumed = typeof task.task_id === "string" ? task.task_id.trim() : "";
|
|
5962
|
+
if (resumed) {
|
|
5963
|
+
holdSessionModel(resumed, reference);
|
|
5964
|
+
noted++;
|
|
5965
|
+
continue;
|
|
5966
|
+
}
|
|
5967
|
+
if (!parentSessionId || parentSessionId === "default") continue;
|
|
5968
|
+
pruneClaims2(now);
|
|
5969
|
+
claims2.push({
|
|
5970
|
+
parentSessionId,
|
|
5971
|
+
agent: typeof task.subagent_type === "string" ? task.subagent_type : "general",
|
|
5972
|
+
prompt: typeof task.prompt === "string" ? task.prompt : "",
|
|
5973
|
+
model: reference,
|
|
5974
|
+
createdAt: now
|
|
5975
|
+
});
|
|
5976
|
+
noted++;
|
|
5977
|
+
}
|
|
5978
|
+
return noted;
|
|
5979
|
+
}
|
|
5980
|
+
function explicitTaskModelFor(sessionId) {
|
|
5981
|
+
return sessionModels.get(sessionId);
|
|
5982
|
+
}
|
|
5983
|
+
function hasExplicitTaskModelCandidate(agent, prompt, now = Date.now()) {
|
|
5984
|
+
if (!agent || prompt === void 0 || claims2.length === 0) return false;
|
|
5985
|
+
pruneClaims2(now);
|
|
5986
|
+
return claims2.some(
|
|
5987
|
+
(claim) => claim.agent === agent && promptMatchesClaim(claim.prompt, prompt)
|
|
5988
|
+
);
|
|
5989
|
+
}
|
|
5990
|
+
function claimExplicitTaskModel(input) {
|
|
5991
|
+
const held = sessionModels.get(input.sessionId);
|
|
5992
|
+
if (held) return held;
|
|
5993
|
+
if (!input.agent || input.prompt === void 0 || !input.parentSessionId) return void 0;
|
|
5994
|
+
pruneClaims2(input.now ?? Date.now());
|
|
5995
|
+
const belongsHere = (claim2) => claim2.parentSessionId === input.parentSessionId && claim2.agent === input.agent;
|
|
5996
|
+
const childPrompt = input.prompt;
|
|
5997
|
+
let index = claims2.findIndex((claim2) => belongsHere(claim2) && claim2.prompt === childPrompt);
|
|
5998
|
+
if (index === -1) {
|
|
5999
|
+
index = claims2.findIndex(
|
|
6000
|
+
(claim2) => belongsHere(claim2) && promptMatchesClaim(claim2.prompt, childPrompt)
|
|
6001
|
+
);
|
|
6002
|
+
}
|
|
6003
|
+
if (index === -1) return void 0;
|
|
6004
|
+
const [claim] = claims2.splice(index, 1);
|
|
6005
|
+
holdSessionModel(input.sessionId, claim.model);
|
|
6006
|
+
log.info("explicit task model claimed by its child session", {
|
|
6007
|
+
sessionId: input.sessionId,
|
|
6008
|
+
agent: input.agent,
|
|
6009
|
+
model: claim.model.modelID
|
|
6010
|
+
});
|
|
6011
|
+
return claim.model;
|
|
6012
|
+
}
|
|
6013
|
+
function explicitTaskModelApplies(record, modelId) {
|
|
6014
|
+
return record !== void 0 && record.modelID === modelId;
|
|
6015
|
+
}
|
|
6016
|
+
function forgetExplicitTaskModel(sessionId) {
|
|
6017
|
+
sessionModels.delete(sessionId);
|
|
6018
|
+
}
|
|
6019
|
+
|
|
5811
6020
|
// src/compression-store.ts
|
|
5812
6021
|
var MAX_COMPRESSION_ENTRIES = 32;
|
|
5813
6022
|
var compressions = /* @__PURE__ */ new Map();
|
|
@@ -7820,6 +8029,7 @@ function deleteActiveProcessesForSession(sessionID) {
|
|
|
7820
8029
|
for (const key of released) clearBackgroundTasks(key);
|
|
7821
8030
|
forgetSessionChoice(sessionID);
|
|
7822
8031
|
forgetDispatchChoicesForSession(sessionID);
|
|
8032
|
+
forgetExplicitTaskModel(sessionID);
|
|
7823
8033
|
return released;
|
|
7824
8034
|
}
|
|
7825
8035
|
var STOPPED_TURN_MESSAGE = "opencode stopped the turn while it was running its proxy tool calls";
|
|
@@ -8907,6 +9117,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
8907
9117
|
"subagent dispatch answer was not recognised; dispatching with the defaults",
|
|
8908
9118
|
"subagent dispatch answer was not recognised; keeping the default",
|
|
8909
9119
|
"subagent dispatch form was dismissed; dispatching with the defaults",
|
|
9120
|
+
"task model refused: its account is in another account group",
|
|
8910
9121
|
"task_batch resolving with child results missing",
|
|
8911
9122
|
"the interactive transport has not been measured on this Claude Code version; it reads the TUI's screen and transcript, which a new release can change. If a turn hangs or a dialog is not handled, run /claude-code-doctor bundle and report it",
|
|
8912
9123
|
"the interactive turn did not take a forwarded user message",
|
|
@@ -15001,7 +15212,7 @@ async function resolvePlanModeQuestion(config, compactionMode, loadLiveToolInfo)
|
|
|
15001
15212
|
}
|
|
15002
15213
|
return active;
|
|
15003
15214
|
}
|
|
15004
|
-
async function ensureProxyServer(config, tools, sessionKeyForCalls, interceptCompress, callerSessionId) {
|
|
15215
|
+
async function ensureProxyServer(config, tools, sessionKeyForCalls, interceptCompress, callerSessionId, sourceAccount) {
|
|
15005
15216
|
const timeoutOverrides = config.proxyToolTimeoutMs;
|
|
15006
15217
|
const interceptors = /* @__PURE__ */ new Map();
|
|
15007
15218
|
if (tools.some((t) => t.name === TASK_STATUS_TOOL_NAME)) {
|
|
@@ -15031,8 +15242,40 @@ async function ensureProxyServer(config, tools, sessionKeyForCalls, interceptCom
|
|
|
15031
15242
|
}
|
|
15032
15243
|
const cap = config.maxBackgroundSubagents;
|
|
15033
15244
|
const admit = cap !== void 0 && tools.some((t) => t.name === "task" || t.name === TASK_BATCH_TOOL_NAME) ? (toolName, input) => admitBackgroundDispatch(toolName, input, cap) : void 0;
|
|
15034
|
-
const
|
|
15245
|
+
const accountProviders = accountProviderMap(config.failoverAccounts);
|
|
15246
|
+
const groups = config.accountGroups ?? null;
|
|
15247
|
+
const caller = normalizeAccountName(sourceAccount ?? config.account ?? DEFAULT_ACCOUNT);
|
|
15248
|
+
const taskModelGuard = groups && accountProviders.size > 0 ? (model) => taskModelAccountRefusal({
|
|
15249
|
+
model,
|
|
15250
|
+
sourceAccount: caller,
|
|
15251
|
+
providers: accountProviders,
|
|
15252
|
+
groups,
|
|
15253
|
+
crossGroup: config.subagentDispatchCrossGroup === true
|
|
15254
|
+
}) : void 0;
|
|
15255
|
+
const ownModelMarker = accountModelSuffix(config.account ?? DEFAULT_ACCOUNT);
|
|
15256
|
+
const completeTaskModel = (model) => {
|
|
15257
|
+
const completed = completeTaskModelReference(model, {
|
|
15258
|
+
providerID: config.provider,
|
|
15259
|
+
accountMarker: ownModelMarker,
|
|
15260
|
+
isRegistered: isRegisteredModelName
|
|
15261
|
+
});
|
|
15262
|
+
if (completed !== model) {
|
|
15263
|
+
log.info("bare task model completed to this conversation's own provider", {
|
|
15264
|
+
model: completed
|
|
15265
|
+
});
|
|
15266
|
+
}
|
|
15267
|
+
return completed;
|
|
15268
|
+
};
|
|
15269
|
+
const srv = await createProxyMcpServer(tools, timeoutOverrides, interceptors, {
|
|
15270
|
+
admit,
|
|
15271
|
+
taskModelGuard,
|
|
15272
|
+
completeTaskModel
|
|
15273
|
+
});
|
|
15035
15274
|
srv.calls.on("call", (call) => {
|
|
15275
|
+
if (call.toolName === "task") noteExplicitTaskModels(callerSessionId, [call.input]);
|
|
15276
|
+
else if (call.toolName === TASK_BATCH_TOOL_NAME) {
|
|
15277
|
+
noteExplicitTaskModels(callerSessionId, taskBatchTasks(call.input));
|
|
15278
|
+
}
|
|
15036
15279
|
queuePendingProxyCall(sessionKeyForCalls, call, timeoutOverrides);
|
|
15037
15280
|
});
|
|
15038
15281
|
return srv;
|
|
@@ -17000,6 +17243,26 @@ var ClaudeCodeLanguageModel = class {
|
|
|
17000
17243
|
parentSessionId: await fetchSessionParentId(affinity)
|
|
17001
17244
|
});
|
|
17002
17245
|
}
|
|
17246
|
+
/**
|
|
17247
|
+
* The model the parent named for THIS session in its own `task` call, or
|
|
17248
|
+
* undefined (h #g254). The same two lookups as `resolveDispatchChoice`, and
|
|
17249
|
+
* the same reason for the order: a held record first, and the parent fetched
|
|
17250
|
+
* only for a child whose agent and first message match a recorded request.
|
|
17251
|
+
*/
|
|
17252
|
+
async resolveExplicitTaskModel(options, affinity) {
|
|
17253
|
+
if (affinity === "default") return void 0;
|
|
17254
|
+
const held = explicitTaskModelFor(affinity);
|
|
17255
|
+
if (held) return held;
|
|
17256
|
+
const agent = this.getOpencodeAgent(options);
|
|
17257
|
+
const prompt = firstUserText(options.prompt);
|
|
17258
|
+
if (!hasExplicitTaskModelCandidate(agent, prompt)) return void 0;
|
|
17259
|
+
return claimExplicitTaskModel({
|
|
17260
|
+
sessionId: affinity,
|
|
17261
|
+
agent,
|
|
17262
|
+
prompt,
|
|
17263
|
+
parentSessionId: await fetchSessionParentId(affinity)
|
|
17264
|
+
});
|
|
17265
|
+
}
|
|
17003
17266
|
/**
|
|
17004
17267
|
* Whether the ExitPlanMode approval bridge is live for this turn: the
|
|
17005
17268
|
* operator opted in AND opencode's registry actually has the `question`
|
|
@@ -17173,13 +17436,14 @@ var ClaudeCodeLanguageModel = class {
|
|
|
17173
17436
|
* Create a proxy MCP server for a single active Claude process/session.
|
|
17174
17437
|
* The process lifecycle owns the server lifecycle via session-manager.
|
|
17175
17438
|
*/
|
|
17176
|
-
async ensureProxyServer(tools, sessionKeyForCalls, interceptCompress, callerSessionId) {
|
|
17439
|
+
async ensureProxyServer(tools, sessionKeyForCalls, interceptCompress, callerSessionId, sourceAccount) {
|
|
17177
17440
|
return ensureProxyServer(
|
|
17178
17441
|
this.config,
|
|
17179
17442
|
tools,
|
|
17180
17443
|
sessionKeyForCalls,
|
|
17181
17444
|
interceptCompress,
|
|
17182
|
-
callerSessionId
|
|
17445
|
+
callerSessionId,
|
|
17446
|
+
sourceAccount
|
|
17183
17447
|
);
|
|
17184
17448
|
}
|
|
17185
17449
|
/**
|
|
@@ -17570,17 +17834,19 @@ var ClaudeCodeLanguageModel = class {
|
|
|
17570
17834
|
const affinity = this.sessionAffinity(options);
|
|
17571
17835
|
const cwd = await resolveSpawnCwdForSession(this.config.cwd, affinity);
|
|
17572
17836
|
const compactionMode = this.isCompactionCall(options);
|
|
17573
|
-
const
|
|
17837
|
+
const explicitTaskModel = compactionMode ? void 0 : await this.resolveExplicitTaskModel(options, affinity);
|
|
17838
|
+
const explicitModelApplies = explicitTaskModelApplies(explicitTaskModel, this.modelId);
|
|
17839
|
+
const dispatchChoice = compactionMode || explicitModelApplies ? void 0 : await this.resolveDispatchChoice(options, affinity);
|
|
17574
17840
|
const dispatchModelId = dispatchChoice?.model ? qualifyModelName(dispatchChoice.model, this.modelId) : null;
|
|
17575
|
-
const effectiveModelId = compactionMode ? this.resolveCompactionModel() : attempt?.modelOverride ?? dispatchModelId ?? resolveAgentModel(
|
|
17841
|
+
const effectiveModelId = compactionMode ? this.resolveCompactionModel() : attempt?.modelOverride ?? dispatchModelId ?? (explicitModelApplies ? this.modelId : resolveAgentModel(
|
|
17576
17842
|
this.getOpencodeAgent(options),
|
|
17577
17843
|
this.modelId
|
|
17578
|
-
);
|
|
17844
|
+
));
|
|
17579
17845
|
if (attempt) attempt.modelId = effectiveModelId;
|
|
17580
|
-
const reasoningEffort = compactionMode ? void 0 : dispatchChoice?.effort ?? resolveAgentEffort(
|
|
17846
|
+
const reasoningEffort = compactionMode ? void 0 : dispatchChoice?.effort ?? (explicitModelApplies && explicitTaskModel?.variant ? this.getReasoningEffort(options.providerOptions) : resolveAgentEffort(
|
|
17581
17847
|
this.getOpencodeAgent(options),
|
|
17582
17848
|
this.getReasoningEffort(options.providerOptions)
|
|
17583
|
-
);
|
|
17849
|
+
));
|
|
17584
17850
|
const promptCacheTtl = compactionMode ? void 0 : resolveAgentCacheTtl(this.getOpencodeAgent(options));
|
|
17585
17851
|
const context = [
|
|
17586
17852
|
this.config.provider,
|
|
@@ -18610,6 +18876,16 @@ var ClaudeCodeLanguageModel = class {
|
|
|
18610
18876
|
backgroundSubagentsSupported,
|
|
18611
18877
|
hostDialect
|
|
18612
18878
|
);
|
|
18879
|
+
const taskModelSupported = liveTaskSupportsModel(
|
|
18880
|
+
liveToolInfo.taskParameters,
|
|
18881
|
+
hostDialect
|
|
18882
|
+
);
|
|
18883
|
+
enrichedProxy = applyTaskModelSupport(enrichedProxy, taskModelSupported);
|
|
18884
|
+
log.info("task model gate", {
|
|
18885
|
+
supported: taskModelSupported,
|
|
18886
|
+
registryResolved: liveToolInfo.resolved,
|
|
18887
|
+
hostApi: hostDialect
|
|
18888
|
+
});
|
|
18613
18889
|
recordBackgroundSubagentGate({
|
|
18614
18890
|
supported: backgroundSubagentsSupported,
|
|
18615
18891
|
hostApi: hostDialect,
|
|
@@ -18664,7 +18940,8 @@ var ClaudeCodeLanguageModel = class {
|
|
|
18664
18940
|
combinedProxyTools,
|
|
18665
18941
|
sk,
|
|
18666
18942
|
pluginCompressEnabled,
|
|
18667
|
-
affinity
|
|
18943
|
+
affinity,
|
|
18944
|
+
sourceAccount
|
|
18668
18945
|
);
|
|
18669
18946
|
}
|
|
18670
18947
|
const questionProxyActive = enrichedProxy?.some((t) => t.name === "question") ?? false;
|