@ebony-vinyl/dsh-our-free-model 2.7.4 → 2.8.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/README.md +23 -8
- package/client.js +894 -6
- package/cordis.patch.yml +1 -1
- package/index.js +248 -11
- package/package.json +6 -2
- package/src/adapter.js +54 -6
- package/src/aggregate.js +53 -13
- package/src/bun-fetch.js +33 -10
- package/src/catalog.js +238 -58
- package/src/core.js +838 -0
- package/src/egress.js +204 -3
- package/src/engine-parts/win32-x64.000.part +0 -0
- package/src/engine-parts/win32-x64.001.part +0 -0
- package/src/engine-parts/win32-x64.002.part +0 -0
- package/src/engine-parts/win32-x64.003.part +0 -0
- package/src/engine-parts/win32-x64.004.part +0 -0
- package/src/engine-parts/win32-x64.005.part +0 -0
- package/src/engine-parts/win32-x64.006.part +0 -0
- package/src/extensions.js +1888 -0
- package/src/feed.js +6 -0
- package/src/http.js +15 -2
- package/src/kilo.js +55 -5
- package/src/magpie-engine.js +61 -2
- package/src/magpie-models.js +31 -3
- package/src/model-test.js +3 -1
- package/src/o2a.js +956 -0
- package/src/open-external.js +43 -5
- package/src/plugins.js +1240 -0
- package/src/providers/registry.json +72 -30
- package/src/semver.js +40 -0
- package/src/store.js +17 -1
- package/src/stream.js +39 -4
- package/src/subscription-completion.js +12 -3
- package/src/updater.js +5 -33
- package/src/upstream.js +283 -363
- package/vendor/channel-pack/NOTICE.md +1 -1
- package/vendor/channel-pack/pack.js +7242 -3952
- package/vendor/magpie/NOTICE.md +159 -6
- package/vendor/magpie/engine.json +58 -27
- package/src/core/completion.js +0 -196
- package/src/core/runtime.js +0 -456
- package/src/core/stats.js +0 -98
- package/src/engine-parts/win32-x64.007.part +0 -0
- package/src/engine-parts/win32-x64.008.part +0 -0
package/src/o2a.js
ADDED
|
@@ -0,0 +1,956 @@
|
|
|
1
|
+
// src/o2a/version.js
|
|
2
|
+
var O2A_VERSION = "1.4.7";
|
|
3
|
+
var O2A_COMMIT = "b296a61d0e3ef93b4df58e298be2c4e34e397322";
|
|
4
|
+
var O2A_REFERENCE = "https://github.com/jasonxu114514/opencode2api";
|
|
5
|
+
var V2_CLIENT_VERSION = "2.2.3";
|
|
6
|
+
var CLIENT_UA = `opencode/latest/${V2_CLIENT_VERSION}/cli`;
|
|
7
|
+
var V2_COMPLETION_LIMIT = 32e3;
|
|
8
|
+
var ANONYMOUS_CREDENTIAL = "public";
|
|
9
|
+
|
|
10
|
+
// src/o2a/identity.js
|
|
11
|
+
import crypto from "node:crypto";
|
|
12
|
+
var CANONICAL_SESSION_RE = /^ses_[0-9a-f]{12}[0-9A-Za-z]{14}$/;
|
|
13
|
+
var DESCENDING_TIME_RE = /^[0-9a-f]{9}f[c-f][0-9a-f]$/;
|
|
14
|
+
var BASE62_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
15
|
+
var SESSION_TIMESTAMP_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
16
|
+
var BOOT_NONCE = crypto.randomBytes(16);
|
|
17
|
+
function isOfficialSessionID(signal) {
|
|
18
|
+
return typeof signal === "string" && CANONICAL_SESSION_RE.test(signal) && DESCENDING_TIME_RE.test(signal.slice(4, 16));
|
|
19
|
+
}
|
|
20
|
+
function canonicalSessionID(signal, now = Date.now()) {
|
|
21
|
+
if (isOfficialSessionID(signal)) return signal;
|
|
22
|
+
const digest = crypto.createHash("sha256").update(Buffer.concat([Buffer.from("ses\0"), BOOT_NONCE, Buffer.from([0]), Buffer.from(String(signal))])).digest();
|
|
23
|
+
const timePart = descendingTimePart(now, digest);
|
|
24
|
+
const randomPart = base62Fixed(digest.subarray(6, 16), 14);
|
|
25
|
+
return `ses_${timePart}${randomPart}`;
|
|
26
|
+
}
|
|
27
|
+
var SESSION_PHASE_MS = 2n ** 36n;
|
|
28
|
+
function sessionPhaseMs(id) {
|
|
29
|
+
if (!isOfficialSessionID(id)) return null;
|
|
30
|
+
const inverted = BigInt(`0x${id.slice(4, 16)}`);
|
|
31
|
+
const current = ~inverted & 0xffffffffffffn;
|
|
32
|
+
return Number(current >> 12n);
|
|
33
|
+
}
|
|
34
|
+
function currentTimePhaseMs(now = Date.now()) {
|
|
35
|
+
return now % Number(SESSION_PHASE_MS);
|
|
36
|
+
}
|
|
37
|
+
function phaseAgeMs(phase, now = Date.now()) {
|
|
38
|
+
const nowPhase = BigInt(now % Number(SESSION_PHASE_MS));
|
|
39
|
+
const age = (nowPhase - BigInt(phase)) % SESSION_PHASE_MS;
|
|
40
|
+
return Number((age + SESSION_PHASE_MS) % SESSION_PHASE_MS);
|
|
41
|
+
}
|
|
42
|
+
function descendingTimePart(now = Date.now(), hash) {
|
|
43
|
+
const offset = ((hash[0] << 24 | hash[1] << 16 | hash[2] << 8 | hash[3]) >>> 0) % SESSION_TIMESTAMP_WINDOW_MS;
|
|
44
|
+
const midnight = Math.floor(now / 864e5) * 864e5;
|
|
45
|
+
const base = midnight - offset;
|
|
46
|
+
const inverted = ~(BigInt(base) << 12n | 1n) & 0xffffffffffffn;
|
|
47
|
+
return inverted.toString(16).padStart(12, "0");
|
|
48
|
+
}
|
|
49
|
+
function base62Fixed(bytes, width) {
|
|
50
|
+
let n = 0n;
|
|
51
|
+
for (const byte of bytes) n = n << 8n | BigInt(byte);
|
|
52
|
+
const out = new Array(width);
|
|
53
|
+
for (let i = width - 1; i >= 0; i -= 1) {
|
|
54
|
+
out[i] = BASE62_ALPHABET[Number(n % 62n)];
|
|
55
|
+
n /= 62n;
|
|
56
|
+
}
|
|
57
|
+
return out.join("");
|
|
58
|
+
}
|
|
59
|
+
function randomID(prefix, size) {
|
|
60
|
+
return `${prefix}_${crypto.randomBytes(size).toString("hex")}`;
|
|
61
|
+
}
|
|
62
|
+
function randomProjectID() {
|
|
63
|
+
return crypto.randomBytes(20).toString("hex");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/o2a/tool-renames.js
|
|
67
|
+
import crypto2 from "node:crypto";
|
|
68
|
+
var CLIENT_TOOL_PREFIX = "clint_";
|
|
69
|
+
var CLIENT_TOOL_DIGEST_LENGTH = 12;
|
|
70
|
+
var TOOL_WIRE_NAME_LIMIT = 64;
|
|
71
|
+
var TOOL_NAME_SEPARATORS = "_.- \r\n";
|
|
72
|
+
var SEPARATOR_SET = new Set(TOOL_NAME_SEPARATORS);
|
|
73
|
+
function foldToolName(name) {
|
|
74
|
+
let out = "";
|
|
75
|
+
for (const char of String(name ?? "").toLowerCase()) {
|
|
76
|
+
if (!SEPARATOR_SET.has(char)) out += char;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
function clientToolWireName(name, used) {
|
|
81
|
+
const taken = used instanceof Set ? used : /* @__PURE__ */ new Set();
|
|
82
|
+
const digest = crypto2.createHash("sha256").update(String(name)).digest("hex");
|
|
83
|
+
let clean = digest.slice(0, CLIENT_TOOL_DIGEST_LENGTH);
|
|
84
|
+
const max = TOOL_WIRE_NAME_LIMIT - CLIENT_TOOL_PREFIX.length;
|
|
85
|
+
if (clean.length > max) clean = clean.slice(0, max);
|
|
86
|
+
const base = CLIENT_TOOL_PREFIX + clean;
|
|
87
|
+
if (!taken.has(base)) {
|
|
88
|
+
taken.add(base);
|
|
89
|
+
return base;
|
|
90
|
+
}
|
|
91
|
+
for (let suffix = 2; ; suffix += 1) {
|
|
92
|
+
const tag = `_${suffix}`;
|
|
93
|
+
const cut = Math.min(TOOL_WIRE_NAME_LIMIT - tag.length, base.length);
|
|
94
|
+
const candidate = base.slice(0, cut) + tag;
|
|
95
|
+
if (!taken.has(candidate)) {
|
|
96
|
+
taken.add(candidate);
|
|
97
|
+
return candidate;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
var OFFICIAL_TOOL_ALIASES = Object.freeze({
|
|
102
|
+
edit: ["edit"],
|
|
103
|
+
glob: ["glob"],
|
|
104
|
+
grep: ["grep"],
|
|
105
|
+
question: ["question", "askuserquestion"],
|
|
106
|
+
read: ["read", "view"],
|
|
107
|
+
shell: ["shell", "bash"],
|
|
108
|
+
skill: ["skill"],
|
|
109
|
+
subagent: ["subagent", "agent", "task"],
|
|
110
|
+
webfetch: ["webfetch", "fetch"],
|
|
111
|
+
websearch: ["websearch"],
|
|
112
|
+
write: ["write"],
|
|
113
|
+
patch: ["patch", "applypatch"]
|
|
114
|
+
});
|
|
115
|
+
var FOLDED_OFFICIAL_ALIASES = (() => {
|
|
116
|
+
const index = /* @__PURE__ */ new Map();
|
|
117
|
+
for (const [official, spellings] of Object.entries(OFFICIAL_TOOL_ALIASES)) {
|
|
118
|
+
for (const spelling of spellings) {
|
|
119
|
+
const folded = foldToolName(spelling);
|
|
120
|
+
if (!index.has(folded)) index.set(folded, official);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return index;
|
|
124
|
+
})();
|
|
125
|
+
function officialForClientName(name) {
|
|
126
|
+
if (typeof name !== "string" || name === "") return null;
|
|
127
|
+
return FOLDED_OFFICIAL_ALIASES.get(foldToolName(name)) ?? null;
|
|
128
|
+
}
|
|
129
|
+
var ToolRenameMap = class extends Map {
|
|
130
|
+
constructor() {
|
|
131
|
+
super();
|
|
132
|
+
this.#byClient = /* @__PURE__ */ new Map();
|
|
133
|
+
this.#byCase = /* @__PURE__ */ new Map();
|
|
134
|
+
this.#byShape = /* @__PURE__ */ new Map();
|
|
135
|
+
}
|
|
136
|
+
/** Records one client→wire mapping (`Add`). Conflicts keep the first allocation. */
|
|
137
|
+
add(client, wire) {
|
|
138
|
+
if (!client || !wire || client === wire) return;
|
|
139
|
+
if (this.#byClient.has(client)) return;
|
|
140
|
+
if (this.has(wire)) return;
|
|
141
|
+
this.#byClient.set(client, wire);
|
|
142
|
+
this.set(wire, client);
|
|
143
|
+
bindFolded(this.#byCase, String(client).toLowerCase(), client);
|
|
144
|
+
bindFolded(this.#byShape, foldToolName(client), client);
|
|
145
|
+
}
|
|
146
|
+
/** The wire name registered for an exact client name (`Wire`). */
|
|
147
|
+
wireOf(client) {
|
|
148
|
+
return this.#byClient.get(client);
|
|
149
|
+
}
|
|
150
|
+
/** Whether any wire name starts with `prefix` (`HasPrefix`). */
|
|
151
|
+
hasPrefix(prefix) {
|
|
152
|
+
if (!prefix) return false;
|
|
153
|
+
for (const wire of this.keys()) {
|
|
154
|
+
if (wire.startsWith(prefix)) return true;
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
/** Whether a wire name is longer than `prefix` and starts with it (`HasLongerPrefix`). */
|
|
159
|
+
hasLongerPrefix(prefix) {
|
|
160
|
+
if (!prefix) return false;
|
|
161
|
+
for (const wire of this.keys()) {
|
|
162
|
+
if (wire.length > prefix.length && wire.startsWith(prefix)) return true;
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Map a name the upstream reported back to the name the client declared
|
|
168
|
+
* (`Resolve`): an exact wire name restores; a `clint_` prefix is stripped as
|
|
169
|
+
* best-effort recovery; a name that differs from exactly one declared client
|
|
170
|
+
* tool in case, or in case and separators, folds onto that tool. A name two
|
|
171
|
+
* tools fold to is never guessed between.
|
|
172
|
+
*/
|
|
173
|
+
resolve(wire) {
|
|
174
|
+
if (!wire) return wire;
|
|
175
|
+
const exact = this.get(wire);
|
|
176
|
+
if (exact !== void 0) return exact;
|
|
177
|
+
let client = wire;
|
|
178
|
+
if (client.startsWith(CLIENT_TOOL_PREFIX)) {
|
|
179
|
+
const rest = client.slice(CLIENT_TOOL_PREFIX.length);
|
|
180
|
+
if (rest !== "") client = rest;
|
|
181
|
+
}
|
|
182
|
+
if (this.#byClient.has(client)) return client;
|
|
183
|
+
const caseMatch = this.#byCase.get(client.toLowerCase());
|
|
184
|
+
if (caseMatch !== void 0) return caseMatch !== "" ? caseMatch : client;
|
|
185
|
+
const shapeMatch = this.#byShape.get(foldToolName(client));
|
|
186
|
+
if (shapeMatch !== void 0 && shapeMatch !== "") return shapeMatch;
|
|
187
|
+
return client;
|
|
188
|
+
}
|
|
189
|
+
#byClient;
|
|
190
|
+
#byCase;
|
|
191
|
+
#byShape;
|
|
192
|
+
};
|
|
193
|
+
function bindFolded(folded, key, client) {
|
|
194
|
+
if (!key) return;
|
|
195
|
+
const existing = folded.get(key);
|
|
196
|
+
if (existing === void 0) {
|
|
197
|
+
folded.set(key, client);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (existing !== client) folded.set(key, "");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/o2a/asset-source.js
|
|
204
|
+
var OFFICIAL_PROMPT_V2 = `You are opencode, an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
|
|
205
|
+
|
|
206
|
+
IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident that the URLs are for helping the user with programming. You may use URLs provided by the user in their messages or local files.
|
|
207
|
+
|
|
208
|
+
If the user asks for help or wants to give feedback inform them of the following:
|
|
209
|
+
- /help: Get help with using opencode
|
|
210
|
+
- To give feedback, users should report the issue at https://github.com/anomalyco/opencode/issues
|
|
211
|
+
|
|
212
|
+
When the user directly asks about opencode (eg 'can opencode do...', 'does opencode have...') or asks in second person (eg 'are you able...', 'can you do...'), first use the webfetch tool to gather information to answer the question from opencode docs at https://opencode.ai
|
|
213
|
+
|
|
214
|
+
# Tone and style
|
|
215
|
+
You should be concise, direct, and to the point. When you run a non-trivial bash command, you should explain what the command does and why you are running it, to make sure the user understands what you are doing (this is especially important when you are running a command that will make changes to the user's system).
|
|
216
|
+
Remember that your output will be displayed on a command line interface. Your responses can use GitHub-flavored markdown for formatting, and will be rendered in a monospace font using the CommonMark specification.
|
|
217
|
+
Output text to communicate with the user; all text you output outside of tool use is displayed to the user. Only use tools to complete tasks. Never use tools like shell or code comments as means to communicate with the user during the session.
|
|
218
|
+
If you cannot or will not help the user with something, please do not say why or what it could lead to, since this comes across as preachy and annoying. Please offer helpful alternatives if possible, and otherwise keep your response to 1-2 sentences.
|
|
219
|
+
Only use emojis if the user explicitly requests it. Avoid using emojis in all communication unless asked.
|
|
220
|
+
IMPORTANT: You should minimize output tokens as much as possible while maintaining helpfulness, quality, and accuracy. Only address the specific query or task at hand, avoiding tangential information unless absolutely critical for completing the request. If you can answer in 1-3 sentences or a short paragraph, please do.
|
|
221
|
+
IMPORTANT: You should NOT answer with unnecessary preamble or postamble (such as explaining your code or summarizing your action), unless the user asks you to.
|
|
222
|
+
IMPORTANT: Keep your responses short, since they will be displayed on a command line interface. You MUST answer concisely with fewer than 4 lines (not including tool use or code generation), unless user asks for detail. Answer the user's question directly, without elaboration, explanation, or details. One word answers are best. Avoid introductions, conclusions, and explanations. You MUST avoid text before/after your response, such as "The answer is <answer>.", "Here is the content of the file..." or "Based on the information provided, the answer is..." or "Here is what I will do next...". Here are some examples to demonstrate appropriate verbosity:
|
|
223
|
+
<example>
|
|
224
|
+
user: what is 2+2?
|
|
225
|
+
assistant: 4
|
|
226
|
+
</example>
|
|
227
|
+
|
|
228
|
+
<example>
|
|
229
|
+
user: is 11 a prime number?
|
|
230
|
+
assistant: Yes
|
|
231
|
+
</example>
|
|
232
|
+
|
|
233
|
+
<example>
|
|
234
|
+
user: what command should I run to list files in the current directory?
|
|
235
|
+
assistant: ls
|
|
236
|
+
</example>
|
|
237
|
+
|
|
238
|
+
<example>
|
|
239
|
+
user: what command should I run to watch files in the current directory?
|
|
240
|
+
assistant: [use the ls tool to list the files in the current directory, then read docs/commands in the relevant file to find out how to watch files]
|
|
241
|
+
npm run dev
|
|
242
|
+
</example>
|
|
243
|
+
|
|
244
|
+
<example>
|
|
245
|
+
user: what files are in the directory src/?
|
|
246
|
+
assistant: [runs ls and sees foo.c, bar.c, baz.c]
|
|
247
|
+
user: which file contains the implementation of foo?
|
|
248
|
+
assistant: src/foo.c
|
|
249
|
+
</example>
|
|
250
|
+
|
|
251
|
+
<example>
|
|
252
|
+
user: write tests for new feature
|
|
253
|
+
assistant: [uses grep and glob search tools to find where similar tests are defined, uses concurrent read file tool use blocks in one tool call to read relevant files at the same time, uses edit file tool to write new tests]
|
|
254
|
+
</example>
|
|
255
|
+
|
|
256
|
+
# Proactiveness
|
|
257
|
+
You are allowed to be proactive, but only when the user asks you to do something. You should strive to strike a balance between:
|
|
258
|
+
1. Doing the right thing when asked, including taking actions and follow-up actions
|
|
259
|
+
2. Not surprising the user with actions you take without asking
|
|
260
|
+
For example, if the user asks you how to approach something, you should do your best to answer their question first, and not immediately jump into taking actions.
|
|
261
|
+
3. Do not add additional code explanation summary unless requested by the user. After working on a file, just stop, rather than providing an explanation of what you did.
|
|
262
|
+
|
|
263
|
+
# Following conventions
|
|
264
|
+
When making changes to files, first understand the file's code conventions. Mimic code style, use existing libraries and utilities, and follow existing patterns.
|
|
265
|
+
- NEVER assume that a given library is available, even if it is well known. Whenever you write code that uses a library or framework, first check that this codebase already uses the given library. For example, you might look at neighboring files, or check the package.json (or cargo.toml, and so on depending on the language).
|
|
266
|
+
- When you create a new component, first look at existing components to see how they're written; then consider framework choice, naming conventions, typing, and other conventions.
|
|
267
|
+
- When you edit a piece of code, first look at the code's surrounding context (especially its imports) to understand the code's choice of frameworks and libraries. Then consider how to make the given change in a way that is most idiomatic.
|
|
268
|
+
- Always follow security best practices. Never introduce code that exposes or logs secrets and keys. Never commit secrets or keys to the repository.
|
|
269
|
+
|
|
270
|
+
# Code style
|
|
271
|
+
- IMPORTANT: DO NOT ADD ***ANY*** COMMENTS unless asked
|
|
272
|
+
|
|
273
|
+
# Doing tasks
|
|
274
|
+
The user will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended:
|
|
275
|
+
- Use the available search tools to understand the codebase and the user's query. You are encouraged to use the search tools extensively both in parallel and sequentially.
|
|
276
|
+
- Implement the solution using all tools available to you
|
|
277
|
+
- Verify the solution if possible with tests. NEVER assume specific test framework or test script. Check the README or search codebase to determine the testing approach.
|
|
278
|
+
- VERY IMPORTANT: When you have completed a task, you MUST run the lint and typecheck commands (e.g. npm run lint, npm run typecheck, ruff, etc.) with shell if they were provided to you to ensure your code is correct. If you are unable to find the correct command, ask the user for the command to run and if they supply it, proactively suggest writing it to AGENTS.md so that you will know to run it next time.
|
|
279
|
+
NEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise the user will feel that you are being too proactive.
|
|
280
|
+
|
|
281
|
+
- Tool results and user messages may include <system-reminder> tags. <system-reminder> tags contain useful information and reminders. They are NOT part of the user's provided input or the tool result.
|
|
282
|
+
|
|
283
|
+
# Tool usage policy
|
|
284
|
+
- When doing file search, prefer to use the subagent tool in order to reduce context usage.
|
|
285
|
+
- You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. When making multiple bash tool calls, you MUST send a single message with multiple tools calls to run the calls in parallel. For example, if you need to run "git status" and "git diff", send a single message with two tool calls to run the calls in parallel.
|
|
286
|
+
|
|
287
|
+
You MUST answer concisely with fewer than 4 lines of text (not including tool use or code generation), unless user asks for detail.
|
|
288
|
+
|
|
289
|
+
IMPORTANT: Before you begin work, think about what the code you're editing is supposed to do based on the filenames directory structure.
|
|
290
|
+
|
|
291
|
+
# Code References
|
|
292
|
+
|
|
293
|
+
When referencing specific functions or pieces of code include the pattern \`file_path:line_number\` to allow the user to easily navigate to the source code location.
|
|
294
|
+
|
|
295
|
+
<example>
|
|
296
|
+
user: Where are errors from the client handled?
|
|
297
|
+
assistant: Clients are marked as failed in the \`connectToServer\` function in src/services/process.ts:712.
|
|
298
|
+
</example>
|
|
299
|
+
|
|
300
|
+
`;
|
|
301
|
+
var OFFICIAL_TOOLS_V2 = '{\n "edit": {\n "description": "Edit the contents of a file by finding and replacing exact text. When editing text from Read output, preserve the exact indentation (tabs or spaces) and omit the line-number prefix, such as `1: `. Never include the prefix in oldString or newString. Write line breaks as \\\\n; the file\'s existing CRLF or LF line endings are matched and preserved. To change line endings themselves, make oldString and newString differ only in \\\\r; that edit applies to the exact bytes. If there is no exact match, the edit retries while ignoring typographic quote, dash, and space variants, then trailing whitespace on each line. The edit fails if oldString is not found. By default, oldString must identify a UNIQUE location. Multiple matches FAIL unless replaceAll is true. Add more surrounding context to disambiguate, or set replaceAll to true to replace every occurrence. Use replaceAll when the change should apply to every occurrence, such as renaming a variable.",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "newString": {\n "description": "Text to replace oldString with (must differ from oldString)",\n "type": "string"\n },\n "oldString": {\n "description": "Exact text to find and replace",\n "type": "string"\n },\n "path": {\n "description": "File to edit",\n "type": "string"\n },\n "replaceAll": {\n "description": "Whether to replace every occurrence of oldString. When false, oldString must match exactly once. Defaults to false.",\n "type": "boolean"\n }\n },\n "required": ["path", "oldString", "newString"],\n "type": "object"\n }\n },\n "execute": {\n "description": "Run JavaScript in a confined Code Mode runtime to script tool calls and HTTP requests and compose their results.\\n`fetch` is available for HTTP requests. Imports, direct filesystem access, and timers are unavailable; all other external access goes through `tools`.\\nWithin `{ code }`, the only callable tools are those explicitly listed in the Code Mode catalog instructions or returned by the `search` function. Inside `{ code }`, ignore tools shown outside the Code Mode catalog. They are not available in the Code Mode runtime.\\nCall tools through `tools` using only exact paths and signatures from the catalog. Do not infer or normalize tool names; preserve bracket notation such as `tools.<namespace>[\\"tool-name\\"](input)`.\\nPrefer an explicit `return`; if omitted, the final top-level expression becomes the result.\\nAwait every call whose completion matters; pending calls are interrupted when execution ends. Run independent calls concurrently with `Promise.allSettled`.",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "code": {\n "type": "string"\n }\n },\n "required": ["code"],\n "type": "object"\n }\n },\n "glob": {\n "description": "Search file paths using a glob pattern (examples: \\"**/*.ts\\", \\"src/**/*.tsx\\").",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "hidden": {\n "description": "Include hidden files and directories (default: false).",\n "type": "boolean"\n },\n "limit": {\n "description": "Maximum number of matching files to return (default: 100)",\n "exclusiveMinimum": 0,\n "type": "integer"\n },\n "path": {\n "description": "Directory to search. Defaults to the current working directory.",\n "type": "string"\n },\n "pattern": {\n "description": "Glob pattern to match files against",\n "type": "string"\n }\n },\n "required": ["pattern"],\n "type": "object"\n }\n },\n "grep": {\n "description": "Search file contents using ripgrep\'s regular expression syntax or literal text matching. Use it to locate specific code, symbols, or text patterns, and narrow searches with `path` or `include`. Returns matching file paths, line numbers, and line previews.",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "caseSensitive": {\n "description": "Use case-sensitive matching (default: true).",\n "type": "boolean"\n },\n "include": {\n "description": "Glob pattern to filter files (for example, \\"*.js\\" or \\"*.{ts,tsx}\\")",\n "type": "string"\n },\n "limit": {\n "description": "Maximum number of matching lines to return (default: 100)",\n "exclusiveMinimum": 0,\n "type": "integer"\n },\n "literal": {\n "description": "Treat `pattern` as exact text instead of a regular expression (default: false).",\n "type": "boolean"\n },\n "path": {\n "description": "File or directory to search. Defaults to the current working directory.",\n "type": "string"\n },\n "pattern": {\n "description": "Regular expression or literal text to match in file contents.",\n "minLength": 1,\n "type": "string"\n }\n },\n "required": ["pattern"],\n "type": "object"\n }\n },\n "question": {\n "description": "Use this tool when you need to ask the user questions during execution. This allows you to:\\n1. Gather user preferences or requirements\\n2. Clarify ambiguous instructions\\n3. Get decisions on implementation choices as you work\\n4. Offer choices to the user about what direction to take.\\n\\nUsage notes:\\n- A \\"Type your own answer\\" option is added automatically; don\'t include a separate option for free form answers\\n- Set `multiple: true` to allow selecting more than one option\\n- If you recommend a specific option, make that the first option in the list and add \\"(Recommended)\\" at the end of the label",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "questions": {\n "description": "Questions to ask",\n "items": {\n "additionalProperties": false,\n "properties": {\n "header": {\n "description": "Very short label (max 30 chars)",\n "type": "string"\n },\n "multiple": {\n "type": "boolean"\n },\n "options": {\n "description": "Available choices",\n "items": {\n "additionalProperties": false,\n "properties": {\n "description": {\n "description": "Explanation of choice",\n "type": "string"\n },\n "label": {\n "description": "Display text (1-5 words, concise)",\n "type": "string"\n }\n },\n "required": ["label", "description"],\n "type": "object"\n },\n "type": "array"\n },\n "question": {\n "description": "Complete question",\n "type": "string"\n }\n },\n "required": ["question", "header", "options"],\n "type": "object"\n },\n "minItems": 1,\n "type": "array"\n }\n },\n "required": ["questions"],\n "type": "object"\n }\n },\n "read": {\n "description": "Read the contents of a file or directory. Supports text files, images, and PDFs. Images and PDFs are presented directly to the model. Each text line is prefixed by its 1-based line number as <line>: <content>. The prefix is for reference and is not part of the file content. Directory entries are returned one per line. Use offset and limit to read large files or directories in sections. Prefer one larger read over many small slices, and use grep to find specific content in large files.",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "limit": {\n "description": "The maximum number of lines or directory entries to read (defaults to and capped at 2000)",\n "minimum": 0,\n "type": "integer"\n },\n "offset": {\n "description": "The line or directory entry to start reading from (1-based)",\n "minimum": 0,\n "type": "integer"\n },\n "path": {\n "description": "File or directory to read",\n "type": "string"\n }\n },\n "required": ["path"],\n "type": "object"\n }\n },\n "shell": {\n "description": "Execute a shell command and return its output. Commands run on Linux using bash. Quote file paths containing spaces or special characters. Prefer dedicated tools over shell commands when possible. When output is large, the full result is saved to a file and a truncated preview is returned. Rely on automatic truncation unless filtering the output is more useful. Commands accept an optional timeout, background commands have no timeout by default. Background commands return immediately, and you will be notified when they complete.",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "background": {\n "description": "Run the command in the background and return immediately (useful for dev servers and long-running builds). You do not need to use \'&\' at the end of the command when using this parameter. You will be notified when it completes. DO NOT poll for completion.",\n "type": "boolean"\n },\n "command": {\n "description": "Shell command string to execute",\n "type": "string"\n },\n "timeout": {\n "description": "Timeout in milliseconds. Set to 0 to disable the timeout. Defaults to 120000 for foreground commands. Background commands have no timeout by default.",\n "minimum": 0,\n "type": "integer"\n },\n "workdir": {\n "description": "Working directory to execute the command in. Defaults to the current working directory. When possible, avoid changing directories in the command and set the working directory here instead.",\n "type": "string"\n }\n },\n "required": ["command"],\n "type": "object"\n }\n },\n "skill": {\n "description": "Load a specialized skill\'s instructions and resources into the current conversation when the task at hand matches its description.\\n\\nThe skill ID must match an available skill or a skill explicitly referenced by the user.",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "id": {\n "description": "The ID of an available skill or a skill explicitly referenced by the user",\n "type": "string"\n }\n },\n "required": ["id"],\n "type": "object"\n }\n },\n "subagent": {\n "description": "Spawns an agent in a child session to work on the specified task.\\nThe output includes a sessionID you can pass back later to continue that specific conversation with the subagent.\\nNew child sessions start with fresh context, so include all relevant context and instructions when you don\'t pass a sessionID.\\nForeground (default) runs the subagent to completion and returns its final response.\\nBackground mode (background=true) launches it asynchronously and returns immediately; you are notified when it finishes.\\nUse background only for independent work that can run while you continue elsewhere.\\n\\nAvailable subagents:\\n- explore: Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. \\"src/components/**/*.tsx\\"), search code for keywords (eg. \\"API endpoints\\"), or answer questions about the codebase (eg. \\"how do API endpoints work?\\"). When calling this agent, specify the desired thoroughness level: \\"quick\\" for basic searches, \\"medium\\" for moderate exploration, or \\"very thorough\\" for comprehensive analysis across multiple locations and naming conventions.\\n- general: General-purpose agent for researching complex questions and executing multi-step tasks. Use this agent to execute multiple units of work in parallel.",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "agent": {\n "description": "The type of specialized agent to use for this task. If the user asks for a subagent by a name that is not one of the available subagents, they most likely mean a model: pick a suitable agent and pass the name through the model parameter instead.",\n "type": "string"\n },\n "background": {\n "description": "Run the subagent in the background and return immediately. You will be notified when it completes. DO NOT sleep, poll, or proactively check on its progress.",\n "type": "boolean"\n },\n "description": {\n "description": "A short 3-5 word label for the task, displayed to the user",\n "type": "string"\n },\n "model": {\n "description": "NEVER set this unless the user explicitly asks for a particular model or variant. The value is written as \\"providerID/modelID\\", or \\"providerID/modelID#variant\\" to include a variant. Do not guess the ID: look the model up with the models tool, filtering to your own provider first.",\n "type": "string"\n },\n "prompt": {\n "description": "The task for the subagent to perform",\n "type": "string"\n },\n "sessionID": {\n "description": "Continue a specific previous subagent conversation by passing its sessionID. Calls without a sessionID start a new conversation.",\n "pattern": "^ses",\n "type": "string"\n }\n },\n "required": ["agent", "description", "prompt"],\n "type": "object"\n }\n },\n "webfetch": {\n "description": "Fetch content from an HTTP or HTTPS URL and return it as text, markdown, or HTML. Markdown is the default.\\n\\nUse a more targeted tool when one is available. This tool is read-only. Large text results may be replaced with a preview while the complete output is retained in managed storage.",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "format": {\n "description": "The format to return the content in. Defaults to markdown.",\n "enum": ["text", "markdown", "html"],\n "type": "string"\n },\n "timeout": {\n "description": "Optional timeout in seconds (maximum: 120)",\n "exclusiveMinimum": 0,\n "maximum": 120,\n "type": "number"\n },\n "url": {\n "description": "The HTTP or HTTPS URL to fetch content from",\n "type": "string"\n }\n },\n "required": ["url"],\n "type": "object"\n }\n },\n "websearch": {\n "description": "Search the web using the user\'s selected search integration. Use this for current information beyond knowledge cutoff.\\n\\nThe current year is 2026. Use this year when searching for recent information or current events.",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "query": {\n "description": "Websearch query",\n "type": "string"\n }\n },\n "required": ["query"],\n "type": "object"\n }\n },\n "write": {\n "description": "Writes a file to the local filesystem, overwriting if one exists.\\n\\nMissing parent directories are created automatically.\\n\\nUse this tool to create new files or overwrite existing files. For partial changes, use the edit tool instead.",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "content": {\n "description": "Content to write to the file",\n "type": "string"\n },\n "path": {\n "description": "Path to the file to write to",\n "type": "string"\n }\n },\n "required": ["path", "content"],\n "type": "object"\n }\n },\n "patch": {\n "description": "Use the `patch` tool to edit files. Your patch language is a stripped\u2011down, file\u2011oriented diff format designed to be easy to parse and safe to apply. You can think of it as a high\u2011level envelope:\\n\\n*** Begin Patch\\n[ one or more file sections ]\\n*** End Patch\\n\\nWithin that envelope, you get a sequence of file operations.\\nYou MUST include a header to specify the action you are taking.\\nEach operation starts with one of three headers:\\n\\n*** Add File: <path> - create a new file. Every following line is a + line (the initial contents).\\n*** Delete File: <path> - remove an existing file. Nothing follows.\\n*** Update File: <path> - patch an existing file in place (optionally with a rename).\\n\\nExample patch:\\n\\n```\\n*** Begin Patch\\n*** Add File: hello.txt\\n+Hello world\\n*** Update File: src/app.py\\n*** Move to: src/main.py\\n@@ def greet():\\n-print(\\"Hi\\")\\n+print(\\"Hello, world!\\")\\n*** Delete File: obsolete.txt\\n*** End Patch\\n```\\n\\nIt is important to remember:\\n\\n- You must include a header with your intended action (Add/Delete/Update)\\n- You must prefix new lines with `+` even when creating a new file",\n "parameters": {\n "additionalProperties": false,\n "properties": {\n "patchText": {\n "description": "The full patch text describing add, update, and delete operations",\n "type": "string"\n }\n },\n "required": ["patchText"],\n "type": "object"\n }\n }\n}\n';
|
|
302
|
+
function officialPromptText() {
|
|
303
|
+
return OFFICIAL_PROMPT_V2;
|
|
304
|
+
}
|
|
305
|
+
function officialToolsText() {
|
|
306
|
+
return OFFICIAL_TOOLS_V2;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/o2a/official-tools.js
|
|
310
|
+
var V2_OFFICIAL_TOOL_ORDER = Object.freeze([
|
|
311
|
+
"edit",
|
|
312
|
+
"execute",
|
|
313
|
+
"glob",
|
|
314
|
+
"grep",
|
|
315
|
+
"question",
|
|
316
|
+
"read",
|
|
317
|
+
"shell",
|
|
318
|
+
"skill",
|
|
319
|
+
"subagent",
|
|
320
|
+
"webfetch",
|
|
321
|
+
"websearch",
|
|
322
|
+
"write",
|
|
323
|
+
"patch"
|
|
324
|
+
]);
|
|
325
|
+
var officialTools;
|
|
326
|
+
function loadOfficialTools() {
|
|
327
|
+
if (officialTools !== void 0) return officialTools;
|
|
328
|
+
officialTools = /* @__PURE__ */ new Map();
|
|
329
|
+
try {
|
|
330
|
+
const parsed = JSON.parse(officialToolsText());
|
|
331
|
+
for (const [name, definition] of Object.entries(parsed)) {
|
|
332
|
+
if (definition && typeof definition.description === "string" && definition.parameters) {
|
|
333
|
+
officialTools.set(name, definition);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} catch {
|
|
337
|
+
}
|
|
338
|
+
return officialTools;
|
|
339
|
+
}
|
|
340
|
+
function officialToolDefinition(name) {
|
|
341
|
+
return loadOfficialTools().get(name);
|
|
342
|
+
}
|
|
343
|
+
function v2ShellDescription(base, platform = process.platform) {
|
|
344
|
+
if (platform !== "win32") return base;
|
|
345
|
+
return base.replace("on Linux using bash", "on Windows using powershell");
|
|
346
|
+
}
|
|
347
|
+
function officialTool(shape, name) {
|
|
348
|
+
let description = `Agent tool ${name}`;
|
|
349
|
+
let parameters = { type: "object", properties: {} };
|
|
350
|
+
const definition = officialToolDefinition(name);
|
|
351
|
+
if (definition !== void 0) {
|
|
352
|
+
description = definition.description;
|
|
353
|
+
if (definition.parameters != null) parameters = structuredClone(definition.parameters);
|
|
354
|
+
}
|
|
355
|
+
if (name === "shell") description = v2ShellDescription(description);
|
|
356
|
+
if (shape === "claude") {
|
|
357
|
+
return { name, description, input_schema: parameters };
|
|
358
|
+
}
|
|
359
|
+
if (shape === "flat") {
|
|
360
|
+
return { type: "function", name, description, parameters };
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
type: "function",
|
|
364
|
+
function: { name, description, parameters, strict: false }
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
function officialToolset(shape) {
|
|
368
|
+
return V2_OFFICIAL_TOOL_ORDER.map((name) => officialTool(shape, name));
|
|
369
|
+
}
|
|
370
|
+
function renamableToolName(shape, entry) {
|
|
371
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
|
|
372
|
+
if (shape === "chat") {
|
|
373
|
+
const name2 = entry.function?.name;
|
|
374
|
+
return typeof name2 === "string" && name2 !== "" ? name2 : null;
|
|
375
|
+
}
|
|
376
|
+
const type = typeof entry.type === "string" ? entry.type : "";
|
|
377
|
+
if (shape === "flat") {
|
|
378
|
+
if (type !== "" && type !== "function" && type !== "custom") return null;
|
|
379
|
+
} else if (shape === "claude") {
|
|
380
|
+
if (type !== "" && type !== "custom") return null;
|
|
381
|
+
}
|
|
382
|
+
const name = entry.name;
|
|
383
|
+
return typeof name === "string" && name !== "" ? name : null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/o2a/official-prompt.js
|
|
387
|
+
var promptCache;
|
|
388
|
+
function officialPrompt() {
|
|
389
|
+
if (promptCache !== void 0) return promptCache;
|
|
390
|
+
promptCache = officialPromptText();
|
|
391
|
+
return promptCache;
|
|
392
|
+
}
|
|
393
|
+
var OFFICIAL_PROMPT_MARKER = "You are opencode, an interactive CLI tool";
|
|
394
|
+
var OFFICIAL_SKILLS = `Skills provide specialized instructions and workflows for specific tasks.
|
|
395
|
+
Use the skill tool to load a skill when a task matches its description.
|
|
396
|
+
<available_skills>
|
|
397
|
+
<skill>
|
|
398
|
+
<name>customize-opencode</name>
|
|
399
|
+
<description>Use ONLY when the user is editing or creating opencode's own configuration: opencode.json, opencode.jsonc, files under .opencode/, or files under ~/.config/opencode/. Also use when creating or fixing opencode agents, subagents, skills, plugins, MCP servers, or permission rules. Do not use for the user's own application code, or for any project that is not configuring opencode itself.</description>
|
|
400
|
+
<location><built-in></location>
|
|
401
|
+
</skill>
|
|
402
|
+
</available_skills>`;
|
|
403
|
+
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
404
|
+
var WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
405
|
+
var cachedWorkDir;
|
|
406
|
+
function workDir() {
|
|
407
|
+
if (cachedWorkDir !== void 0) return cachedWorkDir;
|
|
408
|
+
let directory = "";
|
|
409
|
+
try {
|
|
410
|
+
directory = process.cwd();
|
|
411
|
+
} catch {
|
|
412
|
+
directory = "";
|
|
413
|
+
}
|
|
414
|
+
directory = String(directory).trim();
|
|
415
|
+
if (directory === "" || directory === ".") directory = "/workspace";
|
|
416
|
+
cachedWorkDir = directory;
|
|
417
|
+
return directory;
|
|
418
|
+
}
|
|
419
|
+
function officialEnvironment(model, directory = workDir()) {
|
|
420
|
+
const apiModel = String(model ?? "").replace(/^opencode\//, "").trim() || "unknown";
|
|
421
|
+
const platform = process.platform === "win32" ? "win32" : process.platform;
|
|
422
|
+
const now = /* @__PURE__ */ new Date();
|
|
423
|
+
const date = `${WEEKDAYS[now.getUTCDay()]} ${MONTHS[now.getUTCMonth()]} ${String(now.getUTCDate()).padStart(2, "0")} ${now.getUTCFullYear()}`;
|
|
424
|
+
return [
|
|
425
|
+
`You are powered by the model named ${apiModel}. The exact model ID is opencode/${apiModel}`,
|
|
426
|
+
"Here is some useful information about the environment you are running in:",
|
|
427
|
+
"<env>",
|
|
428
|
+
` Working directory: ${directory}`,
|
|
429
|
+
` Workspace root folder: ${directory}`,
|
|
430
|
+
" Is directory a git repo: yes",
|
|
431
|
+
` Platform: ${platform}`,
|
|
432
|
+
` Today's date: ${date}`,
|
|
433
|
+
"</env>"
|
|
434
|
+
].join("\n");
|
|
435
|
+
}
|
|
436
|
+
function officialSystemPrompt(model, directory) {
|
|
437
|
+
const base = officialPrompt();
|
|
438
|
+
if (base === "") return "";
|
|
439
|
+
return `${base}${officialEnvironment(model, directory)}
|
|
440
|
+
${OFFICIAL_SKILLS}`;
|
|
441
|
+
}
|
|
442
|
+
function textFragments(value) {
|
|
443
|
+
if (typeof value === "string") return value.trim() === "" ? [] : [value];
|
|
444
|
+
if (Array.isArray(value)) return value.flatMap((item) => textFragments(item));
|
|
445
|
+
if (value && typeof value === "object") {
|
|
446
|
+
if (typeof value.text === "string") return textFragments(value.text);
|
|
447
|
+
if (value.content !== void 0) return textFragments(value.content);
|
|
448
|
+
}
|
|
449
|
+
return [];
|
|
450
|
+
}
|
|
451
|
+
function carriesOpenCodePrompt(value) {
|
|
452
|
+
return textFragments(value).some((part) => part.includes(OFFICIAL_PROMPT_MARKER));
|
|
453
|
+
}
|
|
454
|
+
function injectOfficialPrompt(payload, shape, { workDir: workDir2, model } = {}) {
|
|
455
|
+
if (!payload || typeof payload !== "object") return false;
|
|
456
|
+
if (shape !== "chat" && shape !== "flat" && shape !== "claude") return false;
|
|
457
|
+
const prompt = officialSystemPrompt(model ?? payload.model, workDir2);
|
|
458
|
+
if (prompt === "") return false;
|
|
459
|
+
if (shape === "claude") return injectAnthropicPrompt(payload, prompt);
|
|
460
|
+
if (shape === "flat") return injectResponsesPrompt(payload, prompt);
|
|
461
|
+
return injectChatPrompt(payload, prompt);
|
|
462
|
+
}
|
|
463
|
+
function injectChatPrompt(payload, prompt) {
|
|
464
|
+
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
465
|
+
let anchorIndex = -1;
|
|
466
|
+
for (let i = 0; i < messages.length; i += 1) {
|
|
467
|
+
const message = messages[i];
|
|
468
|
+
if (!message || typeof message !== "object") continue;
|
|
469
|
+
const role = message.role;
|
|
470
|
+
if (role !== "system" && role !== "developer") continue;
|
|
471
|
+
if (carriesOpenCodePrompt(message.content)) return false;
|
|
472
|
+
if (anchorIndex === -1) anchorIndex = i;
|
|
473
|
+
}
|
|
474
|
+
if (anchorIndex === -1) {
|
|
475
|
+
payload.messages = [{ role: "system", content: prompt }, ...messages];
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
const anchor = messages[anchorIndex];
|
|
479
|
+
if (Array.isArray(anchor.content)) {
|
|
480
|
+
messages[anchorIndex] = { ...anchor, content: [{ type: "text", text: prompt }, ...anchor.content] };
|
|
481
|
+
} else if (typeof anchor.content === "string" && anchor.content.trim() !== "") {
|
|
482
|
+
messages[anchorIndex] = { ...anchor, content: `${prompt}
|
|
483
|
+
|
|
484
|
+
${anchor.content}` };
|
|
485
|
+
} else {
|
|
486
|
+
messages[anchorIndex] = { ...anchor, content: prompt };
|
|
487
|
+
}
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
function injectAnthropicPrompt(payload, prompt) {
|
|
491
|
+
const exists = payload.system !== void 0;
|
|
492
|
+
if (exists && carriesOpenCodePrompt(payload.system)) return false;
|
|
493
|
+
if (!exists) {
|
|
494
|
+
payload.system = prompt;
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
if (Array.isArray(payload.system)) {
|
|
498
|
+
payload.system = [{ type: "text", text: prompt }, ...payload.system];
|
|
499
|
+
} else if (typeof payload.system === "string" && payload.system.trim() !== "") {
|
|
500
|
+
payload.system = `${prompt}
|
|
501
|
+
|
|
502
|
+
${payload.system}`;
|
|
503
|
+
} else {
|
|
504
|
+
payload.system = prompt;
|
|
505
|
+
}
|
|
506
|
+
return true;
|
|
507
|
+
}
|
|
508
|
+
function injectResponsesPrompt(payload, prompt) {
|
|
509
|
+
const exists = payload.instructions !== void 0;
|
|
510
|
+
if (exists && carriesOpenCodePrompt(payload.instructions)) return false;
|
|
511
|
+
if (exists && typeof payload.instructions === "string" && payload.instructions.trim() !== "") {
|
|
512
|
+
payload.instructions = `${prompt}
|
|
513
|
+
|
|
514
|
+
${payload.instructions}`;
|
|
515
|
+
return true;
|
|
516
|
+
}
|
|
517
|
+
payload.instructions = prompt;
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/o2a/gateway-body.js
|
|
522
|
+
function ensureChatUsage(payload, shape) {
|
|
523
|
+
if (shape !== "chat") return false;
|
|
524
|
+
if (payload.stream !== true) return false;
|
|
525
|
+
if (payload.stream_options && typeof payload.stream_options === "object" && !Array.isArray(payload.stream_options)) {
|
|
526
|
+
if (payload.stream_options.include_usage === true) return false;
|
|
527
|
+
payload.stream_options = { ...payload.stream_options, include_usage: true };
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
payload.stream_options = { include_usage: true };
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
533
|
+
function ensureV2CompletionLimit(payload, shape) {
|
|
534
|
+
if (shape !== "chat") return false;
|
|
535
|
+
if (payload.max_completion_tokens !== void 0) return false;
|
|
536
|
+
if (payload.max_tokens !== void 0) return false;
|
|
537
|
+
payload.max_completion_tokens = V2_COMPLETION_LIMIT;
|
|
538
|
+
return true;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// src/o2a/headers.js
|
|
542
|
+
import crypto3 from "node:crypto";
|
|
543
|
+
var GIT_COMMIT_RE = /^[0-9a-f]{40}$/;
|
|
544
|
+
function projectID(project) {
|
|
545
|
+
if (project === "global" || GIT_COMMIT_RE.test(String(project))) return project;
|
|
546
|
+
const sum = crypto3.createHash("sha256").update(`opencode-project\0${project}`).digest();
|
|
547
|
+
return sum.subarray(0, 20).toString("hex");
|
|
548
|
+
}
|
|
549
|
+
function stableProjectID(session) {
|
|
550
|
+
return projectID(String(session ?? ""));
|
|
551
|
+
}
|
|
552
|
+
function traceHeaders() {
|
|
553
|
+
const traceId = crypto3.randomBytes(16).toString("hex");
|
|
554
|
+
const spanId = crypto3.randomBytes(8).toString("hex");
|
|
555
|
+
const parentId = crypto3.randomBytes(8).toString("hex");
|
|
556
|
+
return {
|
|
557
|
+
"traceparent": `00-${traceId}-${spanId}-01`,
|
|
558
|
+
"b3": `${traceId}-${spanId}-1-${parentId}`
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function gatewayHeaders({ session, childSession, project, stream, accept, anthropic, userAgent } = {}) {
|
|
562
|
+
const headers = {
|
|
563
|
+
"content-type": "application/json",
|
|
564
|
+
"authorization": `Bearer ${ANONYMOUS_CREDENTIAL}`,
|
|
565
|
+
"user-agent": userAgent ?? CLIENT_UA,
|
|
566
|
+
"x-opencode-client": "cli",
|
|
567
|
+
"x-session-affinity": session,
|
|
568
|
+
"x-session-id": session,
|
|
569
|
+
"x-opencode-session": session,
|
|
570
|
+
"x-opencode-session-id": childSession ?? session,
|
|
571
|
+
"x-opencode-project": projectID(project ?? stableProjectID(session)),
|
|
572
|
+
...traceHeaders()
|
|
573
|
+
};
|
|
574
|
+
if (accept !== null) headers["accept"] = accept ?? "*/*";
|
|
575
|
+
if (anthropic) headers["anthropic-beta"] = "interleaved-thinking-2025-05-14";
|
|
576
|
+
return headers;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// src/o2a/toolshape.js
|
|
580
|
+
function renameAndCompleteTools(payload, shape, { keep } = {}) {
|
|
581
|
+
if (shape !== "chat" && shape !== "flat" && shape !== "claude") {
|
|
582
|
+
return { renames: new ToolRenameMap(), injected: [], known: /* @__PURE__ */ new Set() };
|
|
583
|
+
}
|
|
584
|
+
const renames = new ToolRenameMap();
|
|
585
|
+
const used = /* @__PURE__ */ new Set();
|
|
586
|
+
const keepNames = keep instanceof Set ? keep : /* @__PURE__ */ new Set();
|
|
587
|
+
const officialUsed = new Set(keepNames);
|
|
588
|
+
let items = Array.isArray(payload.tools) ? [...payload.tools] : [];
|
|
589
|
+
if ((shape === "chat" || shape === "flat") && Array.isArray(payload.functions)) {
|
|
590
|
+
items = items.concat(convertLegacyFunctions(payload.functions, shape, renames, used));
|
|
591
|
+
delete payload.functions;
|
|
592
|
+
}
|
|
593
|
+
const declaredClientTools = items.filter((item) => renamableToolName(shape, item) !== null).length;
|
|
594
|
+
const shaped = [];
|
|
595
|
+
for (const item of items) {
|
|
596
|
+
const name = renamableToolName(shape, item);
|
|
597
|
+
if (name === null) {
|
|
598
|
+
shaped.push(item);
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
if (keepNames.has(name) || name.startsWith(CLIENT_TOOL_PREFIX)) {
|
|
602
|
+
shaped.push(item);
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
const official = officialForClientName(name);
|
|
606
|
+
if (official !== null && officialUsed.has(official) === false && officialToolDefinition(official) !== void 0) {
|
|
607
|
+
officialUsed.add(official);
|
|
608
|
+
renames.add(name, official);
|
|
609
|
+
shaped.push(withToolName(shape, item, official));
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
let wireName = renames.wireOf(name);
|
|
613
|
+
if (wireName === void 0) {
|
|
614
|
+
wireName = clientToolWireName(name, used);
|
|
615
|
+
renames.add(name, wireName);
|
|
616
|
+
}
|
|
617
|
+
shaped.push(wireName !== name ? withToolName(shape, item, wireName) : item);
|
|
618
|
+
}
|
|
619
|
+
const injected = [];
|
|
620
|
+
for (const name of V2_OFFICIAL_TOOL_ORDER) {
|
|
621
|
+
if (officialUsed.has(name)) continue;
|
|
622
|
+
shaped.push(officialTool(shape, name));
|
|
623
|
+
injected.push(name);
|
|
624
|
+
officialUsed.add(name);
|
|
625
|
+
}
|
|
626
|
+
payload.tools = shaped;
|
|
627
|
+
rewriteToolReferences(payload, shape, renames);
|
|
628
|
+
const known = new Set(officialUsed);
|
|
629
|
+
for (const wire of renames.keys()) known.add(wire);
|
|
630
|
+
if (declaredClientTools > 0) sanitizeToolHistory(payload, shape, known);
|
|
631
|
+
dropDanglingToolChoice(payload, shape, known);
|
|
632
|
+
return { renames, injected, known };
|
|
633
|
+
}
|
|
634
|
+
function sanitizeToolHistory(payload, shape, known) {
|
|
635
|
+
const fateOf = (name) => {
|
|
636
|
+
if (typeof name !== "string" || name === "") return { verdict: "drop" };
|
|
637
|
+
if (known.has(name)) return { verdict: "keep" };
|
|
638
|
+
const official = officialForClientName(name);
|
|
639
|
+
if (official !== null && known.has(official)) return { verdict: "rename", official };
|
|
640
|
+
return { verdict: "drop" };
|
|
641
|
+
};
|
|
642
|
+
if (shape === "chat") {
|
|
643
|
+
const messages2 = Array.isArray(payload.messages) ? payload.messages : null;
|
|
644
|
+
if (messages2 === null) return;
|
|
645
|
+
const droppedCalls2 = /* @__PURE__ */ new Set();
|
|
646
|
+
const droppedFunctions = /* @__PURE__ */ new Set();
|
|
647
|
+
let changed2 = false;
|
|
648
|
+
for (let i = 0; i < messages2.length; i += 1) {
|
|
649
|
+
const message = messages2[i];
|
|
650
|
+
if (!message || typeof message !== "object") continue;
|
|
651
|
+
if (Array.isArray(message.tool_calls)) {
|
|
652
|
+
const kept = [];
|
|
653
|
+
let touched = false;
|
|
654
|
+
for (const call of message.tool_calls) {
|
|
655
|
+
const fate = fateOf(call?.function?.name);
|
|
656
|
+
if (fate.verdict === "keep") {
|
|
657
|
+
kept.push(call);
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
if (fate.verdict === "rename") {
|
|
661
|
+
const fn = call.function ?? {};
|
|
662
|
+
kept.push({ ...call, function: { ...fn, name: fate.official } });
|
|
663
|
+
touched = true;
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
if (typeof call?.id === "string" && call.id !== "") droppedCalls2.add(call.id);
|
|
667
|
+
touched = true;
|
|
668
|
+
}
|
|
669
|
+
if (touched) {
|
|
670
|
+
messages2[i] = { ...message, tool_calls: kept };
|
|
671
|
+
changed2 = true;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
if (message.function_call && typeof message.function_call === "object") {
|
|
675
|
+
const fate = fateOf(message.function_call.name);
|
|
676
|
+
if (fate.verdict === "rename") {
|
|
677
|
+
messages2[i] = { ...message, function_call: { ...message.function_call, name: fate.official } };
|
|
678
|
+
changed2 = true;
|
|
679
|
+
} else if (fate.verdict === "drop") {
|
|
680
|
+
if (typeof message.function_call.name === "string") droppedFunctions.add(message.function_call.name);
|
|
681
|
+
const { function_call: _dropped, ...rest } = messages2[i];
|
|
682
|
+
messages2[i] = rest;
|
|
683
|
+
changed2 = true;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (message.role === "tool" && typeof message.tool_call_id === "string" && droppedCalls2.has(message.tool_call_id)) {
|
|
687
|
+
messages2.splice(i, 1);
|
|
688
|
+
i -= 1;
|
|
689
|
+
changed2 = true;
|
|
690
|
+
}
|
|
691
|
+
if (message.role === "function" && typeof message.name === "string" && droppedFunctions.has(message.name)) {
|
|
692
|
+
messages2.splice(i, 1);
|
|
693
|
+
i -= 1;
|
|
694
|
+
changed2 = true;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if (changed2 && messages2.length === 0) payload.messages = [];
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (shape === "flat") {
|
|
701
|
+
const input = Array.isArray(payload.input) ? payload.input : null;
|
|
702
|
+
if (input === null) return;
|
|
703
|
+
const droppedCalls2 = /* @__PURE__ */ new Set();
|
|
704
|
+
let changed2 = false;
|
|
705
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
706
|
+
const item = input[i];
|
|
707
|
+
const type = typeof item?.type === "string" ? item.type : "";
|
|
708
|
+
if (type === "function_call") {
|
|
709
|
+
const fate = fateOf(item.name);
|
|
710
|
+
if (fate.verdict === "rename") {
|
|
711
|
+
input[i] = { ...item, name: fate.official };
|
|
712
|
+
changed2 = true;
|
|
713
|
+
} else if (fate.verdict === "drop") {
|
|
714
|
+
if (typeof item.call_id === "string" && item.call_id !== "") droppedCalls2.add(item.call_id);
|
|
715
|
+
input.splice(i, 1);
|
|
716
|
+
i -= 1;
|
|
717
|
+
changed2 = true;
|
|
718
|
+
}
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
if (type === "function_call_output" && typeof item.call_id === "string" && droppedCalls2.has(item.call_id)) {
|
|
722
|
+
input.splice(i, 1);
|
|
723
|
+
i -= 1;
|
|
724
|
+
changed2 = true;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (changed2 && input.length === 0) payload.input = [];
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
const messages = Array.isArray(payload.messages) ? payload.messages : null;
|
|
731
|
+
if (messages === null) return;
|
|
732
|
+
const droppedCalls = /* @__PURE__ */ new Set();
|
|
733
|
+
let changed = false;
|
|
734
|
+
for (let i = 0; i < messages.length; i += 1) {
|
|
735
|
+
const message = messages[i];
|
|
736
|
+
if (!message || typeof message !== "object" || !Array.isArray(message.content)) continue;
|
|
737
|
+
const kept = [];
|
|
738
|
+
let touched = false;
|
|
739
|
+
for (const block of message.content) {
|
|
740
|
+
if (block?.type === "tool_use") {
|
|
741
|
+
const fate = fateOf(block.name);
|
|
742
|
+
if (fate.verdict === "keep") {
|
|
743
|
+
kept.push(block);
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
if (fate.verdict === "rename") {
|
|
747
|
+
kept.push({ ...block, name: fate.official });
|
|
748
|
+
touched = true;
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
if (typeof block.id === "string" && block.id !== "") droppedCalls.add(block.id);
|
|
752
|
+
touched = true;
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
if (block?.type === "tool_result" && typeof block.tool_use_id === "string" && droppedCalls.has(block.tool_use_id)) {
|
|
756
|
+
touched = true;
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
kept.push(block);
|
|
760
|
+
}
|
|
761
|
+
if (touched) {
|
|
762
|
+
messages[i] = kept.length === 0 ? null : { ...message, content: kept };
|
|
763
|
+
changed = true;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (changed) payload.messages = messages.filter((message) => message !== null);
|
|
767
|
+
}
|
|
768
|
+
function dropDanglingToolChoice(payload, shape, known) {
|
|
769
|
+
const choice = payload?.tool_choice;
|
|
770
|
+
if (choice === null || typeof choice !== "object") return;
|
|
771
|
+
const name = shape === "chat" && choice.function && typeof choice.function === "object" ? choice.function.name : choice.name;
|
|
772
|
+
if (typeof name === "string" && name !== "" && !known.has(name)) delete payload.tool_choice;
|
|
773
|
+
}
|
|
774
|
+
function withToolName(shape, entry, name) {
|
|
775
|
+
if (shape === "chat") {
|
|
776
|
+
return { ...entry, function: { ...entry.function, name } };
|
|
777
|
+
}
|
|
778
|
+
return { ...entry, name };
|
|
779
|
+
}
|
|
780
|
+
function convertLegacyFunctions(entries, shape, renames, used) {
|
|
781
|
+
const converted = [];
|
|
782
|
+
for (const raw of entries) {
|
|
783
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
784
|
+
const name = typeof raw.name === "string" ? raw.name : "";
|
|
785
|
+
if (name === "") continue;
|
|
786
|
+
let wireName = renames.wireOf(name);
|
|
787
|
+
if (wireName === void 0) {
|
|
788
|
+
wireName = clientToolWireName(name, used);
|
|
789
|
+
renames.add(name, wireName);
|
|
790
|
+
}
|
|
791
|
+
const description = typeof raw.description === "string" ? raw.description : void 0;
|
|
792
|
+
const parameters = raw.parameters ?? { type: "object", properties: {} };
|
|
793
|
+
if (shape === "chat") {
|
|
794
|
+
const fn = { name: wireName, description, parameters };
|
|
795
|
+
if (raw.strict !== void 0) fn.strict = raw.strict;
|
|
796
|
+
converted.push({ type: "function", function: fn });
|
|
797
|
+
} else {
|
|
798
|
+
const tool = { type: "function", name: wireName, description, parameters };
|
|
799
|
+
if (raw.strict !== void 0) tool.strict = raw.strict;
|
|
800
|
+
converted.push(tool);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return converted;
|
|
804
|
+
}
|
|
805
|
+
function rewriteToolReferences(payload, shape, renames) {
|
|
806
|
+
if (renames.size === 0) return;
|
|
807
|
+
const renamed = (container) => {
|
|
808
|
+
if (!container || typeof container !== "object") return null;
|
|
809
|
+
const name = container.name;
|
|
810
|
+
if (typeof name !== "string") return null;
|
|
811
|
+
const wireName = renames.wireOf(name);
|
|
812
|
+
return wireName !== void 0 && wireName !== name ? wireName : null;
|
|
813
|
+
};
|
|
814
|
+
if (shape === "chat") {
|
|
815
|
+
const choice2 = objectOf(payload.tool_choice);
|
|
816
|
+
if (choice2) {
|
|
817
|
+
if (choice2.function && typeof choice2.function === "object") {
|
|
818
|
+
const wire = renamed(choice2.function);
|
|
819
|
+
if (wire !== null) payload.tool_choice = { ...choice2, function: { ...choice2.function, name: wire } };
|
|
820
|
+
} else {
|
|
821
|
+
const wire = renamed(choice2);
|
|
822
|
+
if (wire !== null) payload.tool_choice = { ...choice2, name: wire };
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
const call = objectOf(payload.function_call);
|
|
826
|
+
if (call) {
|
|
827
|
+
const wire = renamed(call);
|
|
828
|
+
if (wire !== null) payload.function_call = { ...call, name: wire };
|
|
829
|
+
}
|
|
830
|
+
const messages2 = Array.isArray(payload.messages) ? payload.messages : [];
|
|
831
|
+
for (let i = 0; i < messages2.length; i += 1) {
|
|
832
|
+
const message = messages2[i];
|
|
833
|
+
if (!message || typeof message !== "object") continue;
|
|
834
|
+
let next = null;
|
|
835
|
+
const fnCall = objectOf(message.function_call);
|
|
836
|
+
if (fnCall) {
|
|
837
|
+
const wire = renamed(fnCall);
|
|
838
|
+
if (wire !== null) next = { ...message, function_call: { ...fnCall, name: wire } };
|
|
839
|
+
}
|
|
840
|
+
if (Array.isArray(message.tool_calls)) {
|
|
841
|
+
const calls = message.tool_calls.map((entry) => {
|
|
842
|
+
const fn = objectOf(entry?.function);
|
|
843
|
+
if (!fn) return entry;
|
|
844
|
+
const wire = renamed(fn);
|
|
845
|
+
return wire !== null ? { ...entry, function: { ...fn, name: wire } } : entry;
|
|
846
|
+
});
|
|
847
|
+
if (calls.some((entry, j) => entry !== message.tool_calls[j])) {
|
|
848
|
+
next = { ...next ?? message, tool_calls: calls };
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
if (next !== null) messages2[i] = next;
|
|
852
|
+
}
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
if (shape === "flat") {
|
|
856
|
+
const choice2 = objectOf(payload.tool_choice);
|
|
857
|
+
if (choice2) {
|
|
858
|
+
let next = null;
|
|
859
|
+
const wire = renamed(choice2);
|
|
860
|
+
if (wire !== null) next = { ...choice2, name: wire };
|
|
861
|
+
if (Array.isArray(choice2.allowed_tools)) {
|
|
862
|
+
const allowed = choice2.allowed_tools.map((entry) => {
|
|
863
|
+
if (!entry || typeof entry !== "object") return entry;
|
|
864
|
+
const entryWire = renamed(entry);
|
|
865
|
+
return entryWire !== null ? { ...entry, name: entryWire } : entry;
|
|
866
|
+
});
|
|
867
|
+
if (allowed.some((entry, j) => entry !== choice2.allowed_tools[j])) {
|
|
868
|
+
next = { ...next ?? choice2, allowed_tools: allowed };
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (next !== null) payload.tool_choice = next;
|
|
872
|
+
}
|
|
873
|
+
const input = Array.isArray(payload.input) ? payload.input : [];
|
|
874
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
875
|
+
const item = input[i];
|
|
876
|
+
const type = typeof item?.type === "string" ? item.type : "";
|
|
877
|
+
if (type !== "function_call" && type !== "custom_tool_call") continue;
|
|
878
|
+
const wire = renamed(item);
|
|
879
|
+
if (wire !== null) input[i] = { ...item, name: wire };
|
|
880
|
+
}
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const choice = objectOf(payload.tool_choice);
|
|
884
|
+
if (choice) {
|
|
885
|
+
const wire = renamed(choice);
|
|
886
|
+
if (wire !== null) payload.tool_choice = { ...choice, name: wire };
|
|
887
|
+
}
|
|
888
|
+
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
|
889
|
+
for (let i = 0; i < messages.length; i += 1) {
|
|
890
|
+
const message = messages[i];
|
|
891
|
+
if (!message || typeof message !== "object" || !Array.isArray(message.content)) continue;
|
|
892
|
+
const content = message.content.map((block) => {
|
|
893
|
+
if (block?.type !== "tool_use") return block;
|
|
894
|
+
const wire = renamed(block);
|
|
895
|
+
return wire !== null ? { ...block, name: wire } : block;
|
|
896
|
+
});
|
|
897
|
+
if (content.some((block, j) => block !== message.content[j])) {
|
|
898
|
+
messages[i] = { ...message, content };
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
function objectOf(value) {
|
|
903
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
904
|
+
}
|
|
905
|
+
export {
|
|
906
|
+
ANONYMOUS_CREDENTIAL,
|
|
907
|
+
BASE62_ALPHABET,
|
|
908
|
+
CANONICAL_SESSION_RE,
|
|
909
|
+
CLIENT_TOOL_DIGEST_LENGTH,
|
|
910
|
+
CLIENT_TOOL_PREFIX,
|
|
911
|
+
CLIENT_UA,
|
|
912
|
+
DESCENDING_TIME_RE,
|
|
913
|
+
O2A_COMMIT,
|
|
914
|
+
O2A_REFERENCE,
|
|
915
|
+
O2A_VERSION,
|
|
916
|
+
OFFICIAL_PROMPT_MARKER,
|
|
917
|
+
OFFICIAL_SKILLS,
|
|
918
|
+
OFFICIAL_TOOL_ALIASES,
|
|
919
|
+
SESSION_PHASE_MS,
|
|
920
|
+
SESSION_TIMESTAMP_WINDOW_MS,
|
|
921
|
+
TOOL_WIRE_NAME_LIMIT,
|
|
922
|
+
ToolRenameMap,
|
|
923
|
+
V2_CLIENT_VERSION,
|
|
924
|
+
V2_COMPLETION_LIMIT,
|
|
925
|
+
V2_OFFICIAL_TOOL_ORDER,
|
|
926
|
+
base62Fixed,
|
|
927
|
+
canonicalSessionID,
|
|
928
|
+
carriesOpenCodePrompt,
|
|
929
|
+
clientToolWireName,
|
|
930
|
+
currentTimePhaseMs,
|
|
931
|
+
descendingTimePart,
|
|
932
|
+
ensureChatUsage,
|
|
933
|
+
ensureV2CompletionLimit,
|
|
934
|
+
foldToolName,
|
|
935
|
+
gatewayHeaders,
|
|
936
|
+
injectOfficialPrompt,
|
|
937
|
+
isOfficialSessionID,
|
|
938
|
+
officialEnvironment,
|
|
939
|
+
officialForClientName,
|
|
940
|
+
officialPrompt,
|
|
941
|
+
officialSystemPrompt,
|
|
942
|
+
officialTool,
|
|
943
|
+
officialToolDefinition,
|
|
944
|
+
officialToolset,
|
|
945
|
+
phaseAgeMs,
|
|
946
|
+
projectID,
|
|
947
|
+
randomID,
|
|
948
|
+
randomProjectID,
|
|
949
|
+
renamableToolName,
|
|
950
|
+
renameAndCompleteTools,
|
|
951
|
+
sessionPhaseMs,
|
|
952
|
+
stableProjectID,
|
|
953
|
+
textFragments,
|
|
954
|
+
traceHeaders,
|
|
955
|
+
v2ShellDescription
|
|
956
|
+
};
|