opencode-async-agent 1.0.1 → 1.0.2
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/async-agent.js +361 -4
- package/package.json +1 -1
package/dist/async-agent.js
CHANGED
|
@@ -38,10 +38,106 @@ function formatDuration(startedAt, completedAt) {
|
|
|
38
38
|
var MAX_RUN_TIME_MS = 15 * 60 * 1e3;
|
|
39
39
|
|
|
40
40
|
// src/plugin/manager.ts
|
|
41
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
42
|
+
import { join } from "path";
|
|
41
43
|
function parseModel(model) {
|
|
42
44
|
const [providerID, ...rest] = model.split("/");
|
|
43
45
|
return { providerID, modelID: rest.join("/") };
|
|
44
46
|
}
|
|
47
|
+
var ANALYSIS_PROMPT = `You are a session analyst. Analyze the following AI task execution comprehensively so the main agent can make informed next decisions.
|
|
48
|
+
|
|
49
|
+
## Analysis Criteria
|
|
50
|
+
|
|
51
|
+
### 1. Anything AI Missed Based on Initial Prompt
|
|
52
|
+
- Compare the original user prompt against what was actually accomplished
|
|
53
|
+
- Identify any requirements, questions, or requests that were never addressed
|
|
54
|
+
- List promises made by the agent that were left unfulfilled
|
|
55
|
+
|
|
56
|
+
### 2. Wrong Doings
|
|
57
|
+
- Identify incorrect assumptions or bad approaches taken
|
|
58
|
+
- Note any factual errors or wrong technical decisions
|
|
59
|
+
- Call out misinterpretations of the original prompt
|
|
60
|
+
|
|
61
|
+
### 3. Gave Up / Shortcuts
|
|
62
|
+
- Did the agent abandon parts of the task prematurely?
|
|
63
|
+
- Were steps skipped or incomplete solutions used?
|
|
64
|
+
- Did the agent stop without exhausting reasonable options?
|
|
65
|
+
- Any signs of "good enough" attitude instead of thorough completion?
|
|
66
|
+
|
|
67
|
+
### 4. Messed Up
|
|
68
|
+
- Did the agent break existing functionality?
|
|
69
|
+
- Were new problems introduced during the task?
|
|
70
|
+
- Any destructive actions or unintended side effects?
|
|
71
|
+
|
|
72
|
+
### 5. Good Points / Choices
|
|
73
|
+
- What technical decisions were sound and should be replicated?
|
|
74
|
+
- What approaches worked well that future tasks should follow?
|
|
75
|
+
- Notable strengths in this session's execution
|
|
76
|
+
|
|
77
|
+
### 6. Session Ended Properly or Stream Cut Out
|
|
78
|
+
- **Proper finish:** Agent concluded with clear result or summary
|
|
79
|
+
- **Stream cut out:** Session interrupted mid-task with no conclusion
|
|
80
|
+
- **Ambiguous end:** Final state unclear or incomplete explanation
|
|
81
|
+
|
|
82
|
+
### 7. Overall Status on the Session
|
|
83
|
+
- Give the main agent a complete picture of what happened
|
|
84
|
+
- Was this session successful, partial, or a failure?
|
|
85
|
+
- Is the output reliable enough to base next decisions on?
|
|
86
|
+
|
|
87
|
+
## Output Format
|
|
88
|
+
|
|
89
|
+
Provide your analysis in **markdown** with these exact sections:
|
|
90
|
+
|
|
91
|
+
### Summary
|
|
92
|
+
[2-3 sentence summary of what happened]
|
|
93
|
+
|
|
94
|
+
### What the AI Missed Based on Initial Prompt
|
|
95
|
+
[List anything not covered from the original prompt]
|
|
96
|
+
|
|
97
|
+
### Wrong Doings
|
|
98
|
+
[Incorrect assumptions, bad approaches, factual errors]
|
|
99
|
+
|
|
100
|
+
### Gave Up / Shortcuts
|
|
101
|
+
[Premature abandonment, skipped steps, incomplete solutions]
|
|
102
|
+
|
|
103
|
+
### Messed Up
|
|
104
|
+
[Broke things, created new problems, unintended side effects]
|
|
105
|
+
|
|
106
|
+
### Good Points / Choices
|
|
107
|
+
[Sound decisions, approaches worth replicating, notable strengths]
|
|
108
|
+
|
|
109
|
+
### Session Completion
|
|
110
|
+
- **Status:** [Proper finish / Stream cut out / Ambiguous]
|
|
111
|
+
- **Details:** [explanation of how the session ended]
|
|
112
|
+
|
|
113
|
+
### Overall Status
|
|
114
|
+
[Complete assessment: Is this session's output reliable for next decisions? What's the verdict?]
|
|
115
|
+
|
|
116
|
+
### Next Action for Main Agent
|
|
117
|
+
[Specific recommendation on what the main agent should do next based on this session's outcome]
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Session Data
|
|
122
|
+
|
|
123
|
+
### Initial User Prompt
|
|
124
|
+
\`\`\`
|
|
125
|
+
\${initialPrompt}
|
|
126
|
+
\`\`\`
|
|
127
|
+
|
|
128
|
+
### Full Conversation
|
|
129
|
+
\`\`\`
|
|
130
|
+
\${formattedMessages}
|
|
131
|
+
\`\`\`
|
|
132
|
+
|
|
133
|
+
### Session Metadata
|
|
134
|
+
- Agent: \${agent}
|
|
135
|
+
- Model: \${model}
|
|
136
|
+
- Duration: \${duration}
|
|
137
|
+
- Status: \${status}
|
|
138
|
+
- Started: \${startTime}
|
|
139
|
+
- Completed: \${completedTime}
|
|
140
|
+
`;
|
|
45
141
|
var DelegationManager = class {
|
|
46
142
|
delegations = /* @__PURE__ */ new Map();
|
|
47
143
|
client;
|
|
@@ -54,6 +150,29 @@ var DelegationManager = class {
|
|
|
54
150
|
calculateDuration(delegation) {
|
|
55
151
|
return formatDuration(delegation.startedAt, delegation.completedAt);
|
|
56
152
|
}
|
|
153
|
+
// ---- Parent session model ----
|
|
154
|
+
async getParentModel(parentSessionID) {
|
|
155
|
+
try {
|
|
156
|
+
const messagesResult = await this.client.session.messages({
|
|
157
|
+
path: { id: parentSessionID }
|
|
158
|
+
});
|
|
159
|
+
const messageData = messagesResult.data;
|
|
160
|
+
if (!messageData || messageData.length === 0) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const lastUserMessage = [...messageData].reverse().find((m) => m.info.role === "user");
|
|
164
|
+
if (!lastUserMessage || !lastUserMessage.info.model) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const model = lastUserMessage.info.model;
|
|
168
|
+
const modelString = `${model.providerID}/${model.modelID}`;
|
|
169
|
+
await this.debugLog(`Got parent model: ${modelString}`);
|
|
170
|
+
return modelString;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
this.log.debug(`Failed to get parent model: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
57
176
|
// ---- Core operations ----
|
|
58
177
|
async delegate(input) {
|
|
59
178
|
await this.debugLog(`delegate() called`);
|
|
@@ -80,12 +199,14 @@ ${available || "(none)"}`
|
|
|
80
199
|
throw new Error("Failed to create delegation session");
|
|
81
200
|
}
|
|
82
201
|
const sessionID = sessionResult.data.id;
|
|
202
|
+
const parentModel = await this.getParentModel(input.parentSessionID);
|
|
83
203
|
const delegation = {
|
|
84
204
|
id: sessionID,
|
|
85
205
|
sessionID,
|
|
86
206
|
parentSessionID: input.parentSessionID,
|
|
87
207
|
parentMessageID: input.parentMessageID,
|
|
88
208
|
parentAgent: input.parentAgent,
|
|
209
|
+
parentModel,
|
|
89
210
|
prompt: input.prompt,
|
|
90
211
|
agent: input.agent,
|
|
91
212
|
model: input.model,
|
|
@@ -279,6 +400,14 @@ ${available || "(none)"}`
|
|
|
279
400
|
Use delegation_list() to see available delegations.`);
|
|
280
401
|
}
|
|
281
402
|
if (delegation.status === "running") {
|
|
403
|
+
if (args.ai) {
|
|
404
|
+
return `Delegation "${args.id}" is still running.
|
|
405
|
+
|
|
406
|
+
Status: ${delegation.status}
|
|
407
|
+
Started: ${delegation.startedAt.toISOString()}
|
|
408
|
+
|
|
409
|
+
Wait for completion notification, then call delegation_read() again. AI analysis only available for completed sessions.`;
|
|
410
|
+
}
|
|
282
411
|
return `Delegation "${args.id}" is still running.
|
|
283
412
|
|
|
284
413
|
Status: ${delegation.status}
|
|
@@ -296,6 +425,16 @@ Error: ${delegation.error}`;
|
|
|
296
425
|
Duration: ${delegation.duration}`;
|
|
297
426
|
return statusMessage;
|
|
298
427
|
}
|
|
428
|
+
if (args.ai) {
|
|
429
|
+
let model = args.ai_model;
|
|
430
|
+
if (!model) {
|
|
431
|
+
model = delegation.parentModel || await this.getDefaultModel();
|
|
432
|
+
if (!model) {
|
|
433
|
+
return "\u274C ai_model required when ai=true and no default model configured (parent session has no model)";
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return await this.analyzeSessionWithAI(delegation, model);
|
|
437
|
+
}
|
|
299
438
|
if (!args.mode || args.mode === "simple") {
|
|
300
439
|
return await this.getSimpleResult(delegation);
|
|
301
440
|
}
|
|
@@ -528,6 +667,221 @@ To inspect session content(human): opencode -s ${delegation.id}`;
|
|
|
528
667
|
async debugLog(msg) {
|
|
529
668
|
this.log.debug(msg);
|
|
530
669
|
}
|
|
670
|
+
// ---- AI Analysis ----
|
|
671
|
+
async getDefaultModel() {
|
|
672
|
+
try {
|
|
673
|
+
const result = await this.client.config.get();
|
|
674
|
+
const config = result.data;
|
|
675
|
+
return config?.model || null;
|
|
676
|
+
} catch (error) {
|
|
677
|
+
this.log.debug(`Failed to get default model: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
getModelInfo(modelId) {
|
|
682
|
+
const [provider, ...rest] = modelId.split("/");
|
|
683
|
+
if (!provider) {
|
|
684
|
+
throw new Error(`Invalid model format: "${modelId}". Expected "provider/model"`);
|
|
685
|
+
}
|
|
686
|
+
const modelIdOnly = rest.join("/");
|
|
687
|
+
try {
|
|
688
|
+
const modelJsonPath = join(process.env.HOME || "", ".cache", "opencode", "models.json");
|
|
689
|
+
const content = readFileSync(modelJsonPath, "utf-8");
|
|
690
|
+
const modelsData = JSON.parse(content);
|
|
691
|
+
if (!modelsData[provider]) {
|
|
692
|
+
throw new Error(`Provider "${provider}" not found in models.json`);
|
|
693
|
+
}
|
|
694
|
+
const providerData = modelsData[provider];
|
|
695
|
+
const apiUrl = providerData.api || providerData.baseUrl;
|
|
696
|
+
if (!apiUrl) {
|
|
697
|
+
throw new Error(`No API URL found for provider "${provider}"`);
|
|
698
|
+
}
|
|
699
|
+
return { provider, apiUrl, modelId: modelIdOnly };
|
|
700
|
+
} catch (error) {
|
|
701
|
+
if (error instanceof Error) throw error;
|
|
702
|
+
throw new Error(`Failed to parse models.json: ${error}`);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
getApiKey(provider) {
|
|
706
|
+
try {
|
|
707
|
+
const authJsonPath = join(process.env.HOME || "", ".local", "share", "opencode", "auth.json");
|
|
708
|
+
const content = readFileSync(authJsonPath, "utf-8");
|
|
709
|
+
const authData = JSON.parse(content);
|
|
710
|
+
if (!authData[provider]) {
|
|
711
|
+
throw new Error(`Provider "${provider}" not found in auth.json`);
|
|
712
|
+
}
|
|
713
|
+
const providerAuth = authData[provider];
|
|
714
|
+
if (providerAuth.type !== "api") {
|
|
715
|
+
throw new Error(`Provider "${provider}" is not an API key type`);
|
|
716
|
+
}
|
|
717
|
+
return providerAuth.key;
|
|
718
|
+
} catch (error) {
|
|
719
|
+
if (error instanceof Error) throw error;
|
|
720
|
+
throw new Error(`Failed to parse auth.json: ${error}`);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
formatSessionForAI(messages, delegation) {
|
|
724
|
+
let initialPrompt = delegation.prompt;
|
|
725
|
+
const parts = [];
|
|
726
|
+
for (const msg of messages) {
|
|
727
|
+
const role = msg.info.role.toUpperCase();
|
|
728
|
+
const timestamp = msg.info.time?.created ? new Date(msg.info.time.created).toISOString() : "unknown";
|
|
729
|
+
parts.push(`[${role}] ${timestamp}`);
|
|
730
|
+
for (const part of msg.parts) {
|
|
731
|
+
switch (part.type) {
|
|
732
|
+
case "text":
|
|
733
|
+
if (part.text) {
|
|
734
|
+
parts.push(part.text.trim());
|
|
735
|
+
}
|
|
736
|
+
break;
|
|
737
|
+
case "reasoning":
|
|
738
|
+
case "thinking":
|
|
739
|
+
const thinkingText = part.thinking || part.text || "";
|
|
740
|
+
if (thinkingText) {
|
|
741
|
+
parts.push(`[REASONING] ${thinkingText.slice(0, 2e3)}`);
|
|
742
|
+
}
|
|
743
|
+
break;
|
|
744
|
+
case "tool":
|
|
745
|
+
if (part.state) {
|
|
746
|
+
const toolInput = part.state.status === "pending" || part.state.status === "running" ? JSON.stringify(part.state.input || {}) : JSON.stringify(part.state.input || {});
|
|
747
|
+
parts.push(`[TOOL CALL] ${part.tool}: ${toolInput}`);
|
|
748
|
+
}
|
|
749
|
+
break;
|
|
750
|
+
case "tool_result":
|
|
751
|
+
const content = part.content || part.output || "";
|
|
752
|
+
parts.push(`[TOOL RESULT] ${content}`);
|
|
753
|
+
break;
|
|
754
|
+
case "file":
|
|
755
|
+
parts.push(`[FILE] ${part.filename || "unknown file"} (${part.mime})`);
|
|
756
|
+
break;
|
|
757
|
+
case "patch":
|
|
758
|
+
parts.push(`[PATCH] Code diff applied`);
|
|
759
|
+
break;
|
|
760
|
+
case "snapshot":
|
|
761
|
+
parts.push(`[SNAPSHOT] State snapshot`);
|
|
762
|
+
break;
|
|
763
|
+
case "agent":
|
|
764
|
+
parts.push(`[AGENT] Switched to: ${part.name || "unknown"}`);
|
|
765
|
+
break;
|
|
766
|
+
default:
|
|
767
|
+
parts.push(`[${part.type}] ${JSON.stringify(part).slice(0, 200)}`);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
parts.push("");
|
|
771
|
+
}
|
|
772
|
+
return `# Full Conversation
|
|
773
|
+
|
|
774
|
+
${parts.join("\n")}`;
|
|
775
|
+
}
|
|
776
|
+
async callAIForAnalysis(apiUrl, apiKey, model, prompt, timeoutMs = 6e4) {
|
|
777
|
+
const controller = new AbortController();
|
|
778
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
779
|
+
try {
|
|
780
|
+
const url = apiUrl.endsWith("/") ? `${apiUrl}chat/completions` : `${apiUrl}/chat/completions`;
|
|
781
|
+
const response = await fetch(url, {
|
|
782
|
+
method: "POST",
|
|
783
|
+
headers: {
|
|
784
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
785
|
+
"Content-Type": "application/json"
|
|
786
|
+
},
|
|
787
|
+
body: JSON.stringify({
|
|
788
|
+
model,
|
|
789
|
+
messages: [{ role: "user", content: prompt }],
|
|
790
|
+
max_tokens: 4e3,
|
|
791
|
+
temperature: 0.3
|
|
792
|
+
}),
|
|
793
|
+
signal: controller.signal
|
|
794
|
+
});
|
|
795
|
+
clearTimeout(timeoutId);
|
|
796
|
+
if (!response.ok) {
|
|
797
|
+
const errorText = await response.text();
|
|
798
|
+
throw new Error(`API request failed: ${response.status} ${response.statusText}
|
|
799
|
+
${errorText}`);
|
|
800
|
+
}
|
|
801
|
+
const data = await response.json();
|
|
802
|
+
if (!data.choices || !data.choices[0] || !data.choices[0].message) {
|
|
803
|
+
throw new Error("Invalid API response format");
|
|
804
|
+
}
|
|
805
|
+
return data.choices[0].message.content;
|
|
806
|
+
} catch (error) {
|
|
807
|
+
if (error.name === "AbortError") {
|
|
808
|
+
throw new Error("AI analysis timed out after 60 seconds");
|
|
809
|
+
}
|
|
810
|
+
throw error;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
logAnalysis(delegationId, model, result, error, duration) {
|
|
814
|
+
if (process.env.OC_ASYNC_DEBUG !== "true") {
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
try {
|
|
818
|
+
const logDir = join(process.env.HOME || "", ".cache", "opencode-delegation-ai");
|
|
819
|
+
if (!existsSync(logDir)) {
|
|
820
|
+
mkdirSync(logDir, { recursive: true });
|
|
821
|
+
}
|
|
822
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
823
|
+
const filename = `analysis-${delegationId}-${timestamp}.json`;
|
|
824
|
+
const logEntry = {
|
|
825
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
826
|
+
delegationId,
|
|
827
|
+
model,
|
|
828
|
+
status: error ? "error" : "success",
|
|
829
|
+
durationMs: duration,
|
|
830
|
+
result: error ? void 0 : result,
|
|
831
|
+
error: error || void 0
|
|
832
|
+
};
|
|
833
|
+
writeFileSync(join(logDir, filename), JSON.stringify(logEntry, null, 2));
|
|
834
|
+
} catch (err) {
|
|
835
|
+
this.log.debug(`Failed to log analysis: ${err}`);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
async analyzeSessionWithAI(delegation, model) {
|
|
839
|
+
const startTime = Date.now();
|
|
840
|
+
try {
|
|
841
|
+
const modelInfo = this.getModelInfo(model);
|
|
842
|
+
const apiKey = this.getApiKey(modelInfo.provider);
|
|
843
|
+
const messagesResult = await this.client.session.messages({
|
|
844
|
+
path: { id: delegation.sessionID }
|
|
845
|
+
});
|
|
846
|
+
const messageData = messagesResult.data;
|
|
847
|
+
if (!messageData || messageData.length === 0) {
|
|
848
|
+
return `Delegation "${delegation.id}" has no messages to analyze.`;
|
|
849
|
+
}
|
|
850
|
+
const formattedMessages = this.formatSessionForAI(messageData, delegation);
|
|
851
|
+
const initialPrompt = messageData.find((m) => m.info.role === "user")?.parts.filter((p) => p.type === "text").map((p) => p.text).join("\n") || delegation.prompt;
|
|
852
|
+
const sessionMetadata = `
|
|
853
|
+
### Session Metadata
|
|
854
|
+
- Agent: ${delegation.agent}
|
|
855
|
+
- Model: ${delegation.model || "unknown"}
|
|
856
|
+
- Duration: ${delegation.duration || "N/A"}
|
|
857
|
+
- Status: ${delegation.status}
|
|
858
|
+
- Started: ${delegation.startedAt.toISOString()}
|
|
859
|
+
- Completed: ${delegation.completedAt?.toISOString() || "N/A"}
|
|
860
|
+
`;
|
|
861
|
+
const fullPrompt = ANALYSIS_PROMPT.replace("${initialPrompt}", initialPrompt).replace("${formattedMessages}", formattedMessages).replace("${agent}", delegation.agent).replace("${model}", delegation.model || "unknown").replace("${duration}", delegation.duration || "N/A").replace("${status}", delegation.status).replace("${startTime}", delegation.startedAt.toISOString()).replace("${completedTime}", delegation.completedAt?.toISOString() || "N/A");
|
|
862
|
+
const analysis = await this.callAIForAnalysis(modelInfo.apiUrl, apiKey, modelInfo.modelId, fullPrompt);
|
|
863
|
+
const duration = Date.now() - startTime;
|
|
864
|
+
this.logAnalysis(delegation.id, model, analysis, void 0, duration);
|
|
865
|
+
const header = `# AI Analysis for Delegation: ${delegation.id}
|
|
866
|
+
|
|
867
|
+
**Agent:** ${delegation.agent}
|
|
868
|
+
**Analysis Model:** ${model}
|
|
869
|
+
**Duration:** ${delegation.duration || "N/A"}
|
|
870
|
+
**Analysis Time:** ${(duration / 1e3).toFixed(2)}s
|
|
871
|
+
|
|
872
|
+
---
|
|
873
|
+
|
|
874
|
+
`;
|
|
875
|
+
return header + analysis;
|
|
876
|
+
} catch (error) {
|
|
877
|
+
const duration = Date.now() - startTime;
|
|
878
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
879
|
+
this.logAnalysis(delegation.id, model, "", errorMessage, duration);
|
|
880
|
+
return `\u274C AI analysis failed:
|
|
881
|
+
|
|
882
|
+
${errorMessage}`;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
531
885
|
};
|
|
532
886
|
|
|
533
887
|
// src/plugin/tools.ts
|
|
@@ -598,6 +952,7 @@ function createDelegationRead(manager) {
|
|
|
598
952
|
Modes:
|
|
599
953
|
- simple (default): Returns just the final result
|
|
600
954
|
- full: Returns all messages in the session with timestamps
|
|
955
|
+
- ai (requires ai=true): Use AI to analyze and summarize the entire session execution
|
|
601
956
|
|
|
602
957
|
Use filters to get specific parts of the conversation.`,
|
|
603
958
|
args: {
|
|
@@ -606,7 +961,9 @@ Use filters to get specific parts of the conversation.`,
|
|
|
606
961
|
include_thinking: tool.schema.boolean().optional().describe("Include thinking/reasoning blocks in full mode"),
|
|
607
962
|
include_tools: tool.schema.boolean().optional().describe("Include tool results in full mode"),
|
|
608
963
|
since_message_id: tool.schema.string().optional().describe("Return only messages after this message ID (full mode only)"),
|
|
609
|
-
limit: tool.schema.number().optional().describe("Max messages to return, capped at 100 (full mode only)")
|
|
964
|
+
limit: tool.schema.number().optional().describe("Max messages to return, capped at 100 (full mode only)"),
|
|
965
|
+
ai: tool.schema.boolean().optional().describe("Use AI to analyze and summarize the session"),
|
|
966
|
+
ai_model: tool.schema.string().optional().describe("Model for AI analysis (e.g. 'minimax/MiniMax-M2.5'). Required when ai=true if no default model configured")
|
|
610
967
|
},
|
|
611
968
|
async execute(args, toolCtx) {
|
|
612
969
|
if (!toolCtx?.sessionID) {
|
|
@@ -749,11 +1106,11 @@ You WILL be notified via \`<system-reminder>\`. Polling wastes tokens.
|
|
|
749
1106
|
async function readBgAgentsConfig() {
|
|
750
1107
|
const { homedir } = await import("os");
|
|
751
1108
|
const { readFile } = await import("fs/promises");
|
|
752
|
-
const { join } = await import("path");
|
|
753
|
-
const configDir =
|
|
1109
|
+
const { join: join2 } = await import("path");
|
|
1110
|
+
const configDir = join2(homedir(), ".config", "opencode");
|
|
754
1111
|
for (const name of ["async-agents.md", "async-agent.md"]) {
|
|
755
1112
|
try {
|
|
756
|
-
return await readFile(
|
|
1113
|
+
return await readFile(join2(configDir, name), "utf-8");
|
|
757
1114
|
} catch {
|
|
758
1115
|
}
|
|
759
1116
|
}
|