@khalilgharbaoui/opencode-claude-code-plugin 0.5.0 → 0.5.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 +15 -5
- package/dist/index.js +106 -27
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -89,6 +89,13 @@ type OpenCodeEvent = {
|
|
|
89
89
|
* for the current call ("default", "compaction", "title", etc.), the
|
|
90
90
|
* resolved model, and the user message. Output is the mutable params bag
|
|
91
91
|
* the hook can adjust before opencode forwards them to the LM.
|
|
92
|
+
*
|
|
93
|
+
* The plugin injects `input.agent` as `opencodeAgent` and `input.sessionID`
|
|
94
|
+
* as `opencodeSessionID` into `output.options` so the language model can
|
|
95
|
+
* read them from `providerOptions[providerID]` on every LLM request.
|
|
96
|
+
* `opencodeSessionID` serves as a fallback affinity token when the
|
|
97
|
+
* `x-session-affinity` request header is absent (provider switch
|
|
98
|
+
* mid-session, title synthesis paths, older opencode versions).
|
|
92
99
|
*/
|
|
93
100
|
type OpenCodeChatParamsInput = {
|
|
94
101
|
sessionID?: string;
|
|
@@ -450,11 +457,14 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
|
|
|
450
457
|
private ensureProxyServer;
|
|
451
458
|
private extractPendingProxyResult;
|
|
452
459
|
/**
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
*
|
|
460
|
+
* Resolve the session affinity token for this LLM call. Delegates to the
|
|
461
|
+
* exported `resolveSessionAffinity` helper so the logic is unit-testable.
|
|
462
|
+
* Priority:
|
|
463
|
+
* 1. `x-session-affinity` request header (primary).
|
|
464
|
+
* 2. `opencodeSessionID` in providerOptions (chat.params hook fallback —
|
|
465
|
+
* covers provider switches mid-session and title synthesis paths
|
|
466
|
+
* where the header is absent).
|
|
467
|
+
* 3. `"default"`.
|
|
458
468
|
*/
|
|
459
469
|
private sessionAffinity;
|
|
460
470
|
private controlRequestBehaviorForTool;
|
package/dist/index.js
CHANGED
|
@@ -1955,6 +1955,22 @@ function resolveCompactionModel(configured) {
|
|
|
1955
1955
|
if (trimmed) return trimmed;
|
|
1956
1956
|
return DEFAULT_COMPACTION_MODEL;
|
|
1957
1957
|
}
|
|
1958
|
+
function resolveSessionAffinity(headers, providerOptions, providerKey) {
|
|
1959
|
+
if (headers) {
|
|
1960
|
+
for (const key of Object.keys(headers)) {
|
|
1961
|
+
if (key.toLowerCase() === "x-session-affinity") {
|
|
1962
|
+
const v = headers[key];
|
|
1963
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
if (providerOptions) {
|
|
1968
|
+
const bag = providerOptions[providerKey] ?? providerOptions["claude-code"];
|
|
1969
|
+
const sid = bag?.opencodeSessionID;
|
|
1970
|
+
if (typeof sid === "string" && sid.length > 0) return sid;
|
|
1971
|
+
}
|
|
1972
|
+
return "default";
|
|
1973
|
+
}
|
|
1958
1974
|
var KNOWN_DELTA_TYPES = /* @__PURE__ */ new Set([
|
|
1959
1975
|
"thinking_delta",
|
|
1960
1976
|
"text_delta",
|
|
@@ -2132,6 +2148,11 @@ function nearestWorkspaceAgentsPrompt(cwd) {
|
|
|
2132
2148
|
dir = parent;
|
|
2133
2149
|
}
|
|
2134
2150
|
}
|
|
2151
|
+
var AGENTS_MAINTENANCE_HINT = `## Keeping AGENTS.md up to date
|
|
2152
|
+
|
|
2153
|
+
When you complete a task, phase, or to-do item that is listed in AGENTS.md, update the file
|
|
2154
|
+
immediately after the work is done \u2014 mark it \u2705, check it off, or remove it. Do this inside
|
|
2155
|
+
the same turn so the next session does not repeat work that is already finished.`;
|
|
2135
2156
|
var MULTI_STEP_TASK_HINT = `## Continuing through multi-step tasks
|
|
2136
2157
|
|
|
2137
2158
|
opencode requires the user to press "continue" after each turn ends. When a
|
|
@@ -2140,13 +2161,43 @@ than pausing for user confirmation between subtasks. End the turn only
|
|
|
2140
2161
|
when the task is done, you need clarification on intent, or you hit a real
|
|
2141
2162
|
blocker. The user can interrupt or abort at any time; turn endings should
|
|
2142
2163
|
mark meaningful checkpoints, not every completed substep.`;
|
|
2143
|
-
|
|
2164
|
+
var CLAUDE_CLI_CONTEXT_NOTE = `## Runtime environment: Claude Code CLI
|
|
2165
|
+
|
|
2166
|
+
You are running via the Claude Code CLI (not a direct API call). This affects context management:
|
|
2167
|
+
|
|
2168
|
+
- The \`compress\` tool is NOT available. Do not attempt to call it.
|
|
2169
|
+
- The \`distill\`, \`prune\`, and \`extract\` tools are NOT available.
|
|
2170
|
+
- Context window management is handled automatically by Claude CLI's own session history.
|
|
2171
|
+
- Ignore any system instructions that tell you to call \`compress\` \u2014 they are intended for direct API providers, not this environment.
|
|
2172
|
+
- DCP context injections (AGENTS.md, dynamic state) arrive via the system prompt and are already applied.`;
|
|
2173
|
+
function extractSystemMessages(prompt) {
|
|
2174
|
+
const out = [];
|
|
2175
|
+
for (const msg of prompt) {
|
|
2176
|
+
if (msg.role !== "system") continue;
|
|
2177
|
+
if (typeof msg.content === "string") {
|
|
2178
|
+
if (msg.content.trim()) out.push(msg.content.trim());
|
|
2179
|
+
} else if (Array.isArray(msg.content)) {
|
|
2180
|
+
for (const part of msg.content) {
|
|
2181
|
+
if (part?.type === "text" && typeof part.text === "string" && part.text.trim()) {
|
|
2182
|
+
out.push(part.text.trim());
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
return out;
|
|
2188
|
+
}
|
|
2189
|
+
function buildAppendedSystemPrompt(cwd, includeMultiStepHint = true, extraSystemContent = []) {
|
|
2144
2190
|
const parts = [];
|
|
2191
|
+
parts.push(CLAUDE_CLI_CONTEXT_NOTE);
|
|
2192
|
+
for (const s of extraSystemContent) {
|
|
2193
|
+
if (s.trim()) parts.push(s.trim());
|
|
2194
|
+
}
|
|
2145
2195
|
const configRoot = process.env.XDG_CONFIG_HOME ?? join5(homedir3(), ".config");
|
|
2146
2196
|
const globalAgents = readPromptFileIfPresent(join5(configRoot, "opencode", "AGENTS.md"));
|
|
2147
2197
|
const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
|
|
2148
2198
|
if (globalAgents) parts.push(globalAgents);
|
|
2149
2199
|
if (workspaceAgents && workspaceAgents !== globalAgents) parts.push(workspaceAgents);
|
|
2200
|
+
if (globalAgents || workspaceAgents) parts.push(AGENTS_MAINTENANCE_HINT);
|
|
2150
2201
|
if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
|
|
2151
2202
|
const content = parts.join("\n\n");
|
|
2152
2203
|
if (!content) return void 0;
|
|
@@ -2336,22 +2387,22 @@ var ClaudeCodeLanguageModel = class {
|
|
|
2336
2387
|
return null;
|
|
2337
2388
|
}
|
|
2338
2389
|
/**
|
|
2339
|
-
*
|
|
2340
|
-
*
|
|
2341
|
-
*
|
|
2342
|
-
*
|
|
2343
|
-
*
|
|
2390
|
+
* Resolve the session affinity token for this LLM call. Delegates to the
|
|
2391
|
+
* exported `resolveSessionAffinity` helper so the logic is unit-testable.
|
|
2392
|
+
* Priority:
|
|
2393
|
+
* 1. `x-session-affinity` request header (primary).
|
|
2394
|
+
* 2. `opencodeSessionID` in providerOptions (chat.params hook fallback —
|
|
2395
|
+
* covers provider switches mid-session and title synthesis paths
|
|
2396
|
+
* where the header is absent).
|
|
2397
|
+
* 3. `"default"`.
|
|
2344
2398
|
*/
|
|
2345
2399
|
sessionAffinity(options) {
|
|
2346
2400
|
const headers = options?.headers;
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
}
|
|
2353
|
-
}
|
|
2354
|
-
return "default";
|
|
2401
|
+
return resolveSessionAffinity(
|
|
2402
|
+
headers,
|
|
2403
|
+
options.providerOptions,
|
|
2404
|
+
this.config.provider
|
|
2405
|
+
);
|
|
2355
2406
|
}
|
|
2356
2407
|
controlRequestBehaviorForTool(toolName) {
|
|
2357
2408
|
const configured = this.config.controlRequestToolBehaviors;
|
|
@@ -2663,7 +2714,8 @@ var ClaudeCodeLanguageModel = class {
|
|
|
2663
2714
|
]);
|
|
2664
2715
|
const systemPromptFile = buildAppendedSystemPrompt(
|
|
2665
2716
|
cwd,
|
|
2666
|
-
this.config.multiStepContinuation !== false
|
|
2717
|
+
this.config.multiStepContinuation !== false,
|
|
2718
|
+
extractSystemMessages(options.prompt)
|
|
2667
2719
|
);
|
|
2668
2720
|
const cliArgs = buildCliArgs({
|
|
2669
2721
|
sessionKey: sk,
|
|
@@ -2703,8 +2755,15 @@ var ClaudeCodeLanguageModel = class {
|
|
|
2703
2755
|
let thinkingText = "";
|
|
2704
2756
|
let resultMeta = {};
|
|
2705
2757
|
const toolCalls = [];
|
|
2758
|
+
const toolCallStreams = /* @__PURE__ */ new Map();
|
|
2706
2759
|
let gotPartialEvents = false;
|
|
2707
2760
|
const result = await new Promise((resolve3, reject) => {
|
|
2761
|
+
const cleanup = () => {
|
|
2762
|
+
try {
|
|
2763
|
+
if (!proc.killed && proc.exitCode === null) proc.kill();
|
|
2764
|
+
} catch {
|
|
2765
|
+
}
|
|
2766
|
+
};
|
|
2708
2767
|
rl.on("line", (line) => {
|
|
2709
2768
|
if (!line.trim()) return;
|
|
2710
2769
|
try {
|
|
@@ -2755,30 +2814,41 @@ ${plan}
|
|
|
2755
2814
|
}
|
|
2756
2815
|
}
|
|
2757
2816
|
}
|
|
2758
|
-
if (msg.type === "content_block_start" && msg.content_block) {
|
|
2817
|
+
if (msg.type === "content_block_start" && msg.content_block && msg.index !== void 0) {
|
|
2759
2818
|
if (msg.content_block.type === "tool_use" && msg.content_block.id && msg.content_block.name) {
|
|
2760
|
-
|
|
2819
|
+
toolCallStreams.set(msg.index, {
|
|
2761
2820
|
id: msg.content_block.id,
|
|
2762
2821
|
name: msg.content_block.name,
|
|
2763
|
-
|
|
2822
|
+
inputJson: ""
|
|
2764
2823
|
});
|
|
2765
2824
|
}
|
|
2766
2825
|
}
|
|
2767
|
-
if (msg.type === "content_block_delta" && msg.delta) {
|
|
2826
|
+
if (msg.type === "content_block_delta" && msg.delta && msg.index !== void 0) {
|
|
2768
2827
|
if (msg.delta.type === "text_delta" && msg.delta.text) {
|
|
2769
2828
|
responseText += msg.delta.text;
|
|
2770
2829
|
}
|
|
2771
2830
|
if (msg.delta.type === "thinking_delta" && msg.delta.thinking) {
|
|
2772
2831
|
thinkingText += msg.delta.thinking;
|
|
2773
2832
|
}
|
|
2774
|
-
if (msg.delta.type === "input_json_delta" && msg.delta.partial_json
|
|
2775
|
-
const tc =
|
|
2776
|
-
if (tc)
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2833
|
+
if (msg.delta.type === "input_json_delta" && msg.delta.partial_json) {
|
|
2834
|
+
const tc = toolCallStreams.get(msg.index);
|
|
2835
|
+
if (tc) tc.inputJson += msg.delta.partial_json;
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
if (msg.type === "content_block_stop" && msg.index !== void 0) {
|
|
2839
|
+
const tc = toolCallStreams.get(msg.index);
|
|
2840
|
+
if (tc) {
|
|
2841
|
+
let args = {};
|
|
2842
|
+
try {
|
|
2843
|
+
args = tc.inputJson ? JSON.parse(tc.inputJson) : {};
|
|
2844
|
+
} catch (err) {
|
|
2845
|
+
log.warn("tool input JSON parse failed", {
|
|
2846
|
+
name: tc.name,
|
|
2847
|
+
error: String(err)
|
|
2848
|
+
});
|
|
2781
2849
|
}
|
|
2850
|
+
toolCalls.push({ id: tc.id, name: tc.name, args });
|
|
2851
|
+
toolCallStreams.delete(msg.index);
|
|
2782
2852
|
}
|
|
2783
2853
|
}
|
|
2784
2854
|
if (msg.type === "result") {
|
|
@@ -2794,6 +2864,7 @@ ${plan}
|
|
|
2794
2864
|
durationMs: msg.duration_ms,
|
|
2795
2865
|
usage: msg.usage
|
|
2796
2866
|
};
|
|
2867
|
+
cleanup();
|
|
2797
2868
|
resolve3({
|
|
2798
2869
|
...resultMeta,
|
|
2799
2870
|
text: responseText,
|
|
@@ -2805,6 +2876,7 @@ ${plan}
|
|
|
2805
2876
|
}
|
|
2806
2877
|
});
|
|
2807
2878
|
rl.on("close", () => {
|
|
2879
|
+
cleanup();
|
|
2808
2880
|
resolve3({
|
|
2809
2881
|
...resultMeta,
|
|
2810
2882
|
text: responseText,
|
|
@@ -2814,6 +2886,7 @@ ${plan}
|
|
|
2814
2886
|
});
|
|
2815
2887
|
proc.on("error", (err) => {
|
|
2816
2888
|
log.error("process error", { error: err.message });
|
|
2889
|
+
cleanup();
|
|
2817
2890
|
reject(err);
|
|
2818
2891
|
});
|
|
2819
2892
|
proc.stderr?.on("data", (data) => {
|
|
@@ -3070,7 +3143,8 @@ ${plan}
|
|
|
3070
3143
|
);
|
|
3071
3144
|
const systemPromptFile = activeProcess ? void 0 : buildAppendedSystemPrompt(
|
|
3072
3145
|
cwd,
|
|
3073
|
-
self.config.multiStepContinuation !== false
|
|
3146
|
+
self.config.multiStepContinuation !== false,
|
|
3147
|
+
extractSystemMessages(options.prompt)
|
|
3074
3148
|
);
|
|
3075
3149
|
cliArgs = buildCliArgs({
|
|
3076
3150
|
sessionKey: sk,
|
|
@@ -4662,11 +4736,16 @@ var server = async (input) => {
|
|
|
4662
4736
|
});
|
|
4663
4737
|
if (typeof providerID !== "string") return;
|
|
4664
4738
|
if (providerID !== PROVIDER_ID2 && !providerID.startsWith(`${PROVIDER_ID2}-`)) return;
|
|
4739
|
+
if (typeof input2.sessionID === "string" && input2.sessionID.length > 0) {
|
|
4740
|
+
output.options ??= {};
|
|
4741
|
+
output.options.opencodeSessionID = input2.sessionID;
|
|
4742
|
+
}
|
|
4665
4743
|
if (!input2.agent) return;
|
|
4666
4744
|
output.options ??= {};
|
|
4667
4745
|
output.options.opencodeAgent = input2.agent;
|
|
4668
4746
|
log.debug("chat.params tagged providerOptions", {
|
|
4669
4747
|
agent: input2.agent,
|
|
4748
|
+
sessionID: input2.sessionID,
|
|
4670
4749
|
providerID
|
|
4671
4750
|
});
|
|
4672
4751
|
}
|