@kevin5251984/guild 0.2.19 → 0.2.20
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/package.json +2 -2
- package/src/cli.ts +2 -5
- package/src/db.ts +39 -6
- package/src/generate.ts +3 -3
- package/src/handlers.ts +29 -45
- package/src/harness.ts +20 -4
- package/src/host-browse.ts +149 -4
- package/src/llm.ts +37 -15
- package/src/mcp.ts +32 -5
- package/src/mention.ts +80 -12
- package/src/oauth.ts +2 -2
- package/src/opencode-free.ts +3 -2
- package/src/public/chat.html +142 -26
- package/src/public/i18n.js +3 -0
- package/src/public/mobile.css +120 -14
- package/src/public/mobile.html +186 -16
- package/src/public/settings.html +51 -8
- package/src/reasoning-catalog.ts +346 -0
- package/src/router.ts +108 -8
- package/src/store.ts +15 -6
- package/src/subagent.ts +10 -5
- package/src/tools.ts +8 -3
- package/src/version.ts +25 -0
- package/vendor/protocol/src/index.ts +13 -1
package/src/llm.ts
CHANGED
|
@@ -39,6 +39,16 @@ import {
|
|
|
39
39
|
fromOpenAiUsage,
|
|
40
40
|
withDuration,
|
|
41
41
|
} from "./usage.ts";
|
|
42
|
+
import {
|
|
43
|
+
attachReasoning,
|
|
44
|
+
clampEffort,
|
|
45
|
+
reasoningFor,
|
|
46
|
+
reasoningPayload,
|
|
47
|
+
refreshReasoningCatalog,
|
|
48
|
+
sanitizeEffort,
|
|
49
|
+
} from "./reasoning-catalog.ts";
|
|
50
|
+
|
|
51
|
+
export { refreshReasoningCatalog };
|
|
42
52
|
import {
|
|
43
53
|
OPENCODE_FREE_DEFAULT_MODEL,
|
|
44
54
|
OPENCODE_FREE_PROVIDER_ID,
|
|
@@ -291,6 +301,7 @@ export function publicModels(dataDir: string, env: NodeJS.ProcessEnv = process.e
|
|
|
291
301
|
return {
|
|
292
302
|
id,
|
|
293
303
|
...provider,
|
|
304
|
+
models: attachReasoning(id, provider.models),
|
|
294
305
|
apiKey: stored === "literal" ? "" : key,
|
|
295
306
|
apiKeyPreview: key ? maskApiKey(key) : "",
|
|
296
307
|
stored,
|
|
@@ -307,19 +318,19 @@ export function publicModels(dataDir: string, env: NodeJS.ProcessEnv = process.e
|
|
|
307
318
|
ready:
|
|
308
319
|
isKeylessProvider(p.id) ||
|
|
309
320
|
Boolean(resolveApiKey(p.apiKey, env) || p.stored === "literal"),
|
|
310
|
-
models: p.models,
|
|
321
|
+
models: attachReasoning(p.id, p.models),
|
|
311
322
|
})),
|
|
312
323
|
...subscriptions.map((s) => ({
|
|
313
324
|
id: s.pickerId,
|
|
314
325
|
name: s.name,
|
|
315
326
|
kind: "oauth" as const,
|
|
316
327
|
ready: s.ready,
|
|
317
|
-
models: s.models ?? [],
|
|
328
|
+
models: attachReasoning(s.pickerId, s.models ?? []),
|
|
318
329
|
})),
|
|
319
330
|
];
|
|
320
331
|
return {
|
|
321
332
|
default: file.default ?? null,
|
|
322
|
-
reasoning: file.reasoning ?? "
|
|
333
|
+
reasoning: file.reasoning ?? "",
|
|
323
334
|
fast: Boolean(file.fast),
|
|
324
335
|
aux: file.aux ?? {},
|
|
325
336
|
auxRoles: AUX_ROLES,
|
|
@@ -483,9 +494,14 @@ export async function llmComplete(input: {
|
|
|
483
494
|
spawnDepth: 0,
|
|
484
495
|
allowWrite: true,
|
|
485
496
|
};
|
|
497
|
+
const file = readModelsFile(input.dataDir);
|
|
498
|
+
const effort = clampEffort(
|
|
499
|
+
file.fast ? "low" : file.reasoning,
|
|
500
|
+
reasoningFor(target.providerId, target.model),
|
|
501
|
+
Boolean(file.fast),
|
|
502
|
+
);
|
|
486
503
|
if (OAUTH_PICKER_IDS.has(target.providerId)) {
|
|
487
504
|
try {
|
|
488
|
-
const file = readModelsFile(input.dataDir);
|
|
489
505
|
return await completeOAuth({
|
|
490
506
|
dataDir: input.dataDir,
|
|
491
507
|
pickerId: target.providerId,
|
|
@@ -493,7 +509,7 @@ export async function llmComplete(input: {
|
|
|
493
509
|
system: input.system,
|
|
494
510
|
messages: input.messages,
|
|
495
511
|
temperature: input.temperature ?? 0.4,
|
|
496
|
-
reasoning:
|
|
512
|
+
reasoning: effort,
|
|
497
513
|
tools: useTools,
|
|
498
514
|
skills: input.skills,
|
|
499
515
|
toolCtx,
|
|
@@ -521,6 +537,7 @@ export async function llmComplete(input: {
|
|
|
521
537
|
input.temperature ?? 0.4,
|
|
522
538
|
useTools,
|
|
523
539
|
toolCtx,
|
|
540
|
+
effort,
|
|
524
541
|
);
|
|
525
542
|
if (!done) return null;
|
|
526
543
|
return {
|
|
@@ -554,11 +571,12 @@ async function dispatchComplete(
|
|
|
554
571
|
temperature: number,
|
|
555
572
|
tools: boolean,
|
|
556
573
|
ctx: ToolContext,
|
|
574
|
+
effort?: string,
|
|
557
575
|
): Promise<DispatchResult | null> {
|
|
558
576
|
if (isKeylessProvider(target.providerId) && usesZenResponses(target.model)) {
|
|
559
577
|
return tools
|
|
560
|
-
? completeZenResponsesTools(target, system, messages, ctx)
|
|
561
|
-
: wrapText(await completeZenResponses(target, system, messages));
|
|
578
|
+
? completeZenResponsesTools(target, system, messages, ctx, effort)
|
|
579
|
+
: wrapText(await completeZenResponses(target, system, messages, effort));
|
|
562
580
|
}
|
|
563
581
|
if (target.api === "openai-responses") {
|
|
564
582
|
const text = await completeCodex(target, system, messages);
|
|
@@ -570,8 +588,8 @@ async function dispatchComplete(
|
|
|
570
588
|
: wrapText(await completeAnthropic(target, system, messages));
|
|
571
589
|
}
|
|
572
590
|
return tools
|
|
573
|
-
? completeOpenAiTools(target, system, messages, temperature, ctx)
|
|
574
|
-
: wrapText(await completeOpenAi(target, system, messages, temperature));
|
|
591
|
+
? completeOpenAiTools(target, system, messages, temperature, ctx, effort)
|
|
592
|
+
: wrapText(await completeOpenAi(target, system, messages, temperature, effort));
|
|
575
593
|
}
|
|
576
594
|
|
|
577
595
|
function wrapText(text: string | null): DispatchResult | null {
|
|
@@ -584,6 +602,7 @@ async function completeOpenAiTools(
|
|
|
584
602
|
messages: { role: "user" | "assistant"; content: string }[],
|
|
585
603
|
temperature: number,
|
|
586
604
|
ctx: ToolContext,
|
|
605
|
+
effort?: string,
|
|
587
606
|
): Promise<DispatchResult | null> {
|
|
588
607
|
type ChatMsg = {
|
|
589
608
|
role: string;
|
|
@@ -635,6 +654,7 @@ async function completeOpenAiTools(
|
|
|
635
654
|
messages: msgs,
|
|
636
655
|
tools: catalog,
|
|
637
656
|
tool_choice: "auto",
|
|
657
|
+
...reasoningPayload(target.providerId, target.baseUrl, effort),
|
|
638
658
|
}),
|
|
639
659
|
signal: roundSignal(ctx),
|
|
640
660
|
});
|
|
@@ -901,6 +921,7 @@ async function completeZenResponses(
|
|
|
901
921
|
target: LlmTarget,
|
|
902
922
|
system: string,
|
|
903
923
|
messages: { role: "user" | "assistant"; content: string }[],
|
|
924
|
+
effort?: string,
|
|
904
925
|
): Promise<string | null> {
|
|
905
926
|
const response = await postZenResponses(
|
|
906
927
|
target,
|
|
@@ -908,6 +929,7 @@ async function completeZenResponses(
|
|
|
908
929
|
model: target.model,
|
|
909
930
|
instructions: system,
|
|
910
931
|
input: messages,
|
|
932
|
+
...reasoningPayload(target.providerId, target.baseUrl, effort),
|
|
911
933
|
},
|
|
912
934
|
AbortSignal.timeout(STREAM_IDLE_TIMEOUT_MS),
|
|
913
935
|
);
|
|
@@ -921,6 +943,7 @@ async function completeZenResponsesTools(
|
|
|
921
943
|
system: string,
|
|
922
944
|
messages: { role: "user" | "assistant"; content: string }[],
|
|
923
945
|
ctx: ToolContext,
|
|
946
|
+
effort?: string,
|
|
924
947
|
): Promise<DispatchResult | null> {
|
|
925
948
|
const input: ZenInput[] = messages.map((item) => ({
|
|
926
949
|
role: item.role,
|
|
@@ -960,6 +983,7 @@ async function completeZenResponsesTools(
|
|
|
960
983
|
input,
|
|
961
984
|
tools,
|
|
962
985
|
tool_choice: "auto",
|
|
986
|
+
...reasoningPayload(target.providerId, target.baseUrl, effort),
|
|
963
987
|
},
|
|
964
988
|
roundSignal(ctx),
|
|
965
989
|
);
|
|
@@ -1070,6 +1094,7 @@ async function completeOpenAi(
|
|
|
1070
1094
|
system: string,
|
|
1071
1095
|
messages: { role: "user" | "assistant"; content: string }[],
|
|
1072
1096
|
temperature: number,
|
|
1097
|
+
effort?: string,
|
|
1073
1098
|
): Promise<string | null> {
|
|
1074
1099
|
const url = `${target.baseUrl}/chat/completions`;
|
|
1075
1100
|
const response = await fetch(url, {
|
|
@@ -1079,6 +1104,7 @@ async function completeOpenAi(
|
|
|
1079
1104
|
model: target.model,
|
|
1080
1105
|
temperature,
|
|
1081
1106
|
messages: [{ role: "system", content: system }, ...messages],
|
|
1107
|
+
...reasoningPayload(target.providerId, target.baseUrl, effort),
|
|
1082
1108
|
}),
|
|
1083
1109
|
signal: AbortSignal.timeout(STREAM_IDLE_TIMEOUT_MS),
|
|
1084
1110
|
});
|
|
@@ -1278,6 +1304,7 @@ function sanitizeModels(file: ModelsFile): ModelsFile {
|
|
|
1278
1304
|
.map((model) => ({
|
|
1279
1305
|
id: model.id.trim(),
|
|
1280
1306
|
name: model.name?.trim() || undefined,
|
|
1307
|
+
...(model.reasoning ? { reasoning: model.reasoning } : {}),
|
|
1281
1308
|
}));
|
|
1282
1309
|
if (models.length === 0) {
|
|
1283
1310
|
throw new StoreError(400, `provider ${id} needs at least one model`);
|
|
@@ -1307,12 +1334,7 @@ function sanitizeModels(file: ModelsFile): ModelsFile {
|
|
|
1307
1334
|
for (const [role, ref] of Object.entries(file.aux ?? {})) {
|
|
1308
1335
|
aux[role as AuxRole] = validRef(ref as ModelRef | null);
|
|
1309
1336
|
}
|
|
1310
|
-
const reasoning =
|
|
1311
|
-
file.reasoning === "minimal" ||
|
|
1312
|
-
file.reasoning === "low" ||
|
|
1313
|
-
file.reasoning === "high"
|
|
1314
|
-
? file.reasoning
|
|
1315
|
-
: "medium";
|
|
1337
|
+
const reasoning = sanitizeEffort(file.reasoning);
|
|
1316
1338
|
const recent = (file.recent ?? [])
|
|
1317
1339
|
.map((ref) => validRef(ref))
|
|
1318
1340
|
.filter((ref): ref is ModelRef => Boolean(ref))
|
package/src/mcp.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
chmodSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
2
8
|
import { homedir } from "node:os";
|
|
3
9
|
import { join } from "node:path";
|
|
4
10
|
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
|
|
@@ -215,6 +221,8 @@ class McpSession {
|
|
|
215
221
|
|
|
216
222
|
const sessions = new Map<string, McpSession>();
|
|
217
223
|
|
|
224
|
+
const REDACTED_ENV_VALUE = "***";
|
|
225
|
+
|
|
218
226
|
export function mcpPath(dataDir: string): string {
|
|
219
227
|
return join(dataDir, "mcp.json");
|
|
220
228
|
}
|
|
@@ -237,10 +245,29 @@ export function writeMcpFile(
|
|
|
237
245
|
servers: Record<string, McpLaunch>,
|
|
238
246
|
): void {
|
|
239
247
|
mkdirSync(dataDir, { recursive: true });
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
);
|
|
248
|
+
const file = mcpPath(dataDir);
|
|
249
|
+
writeFileSync(file, `${JSON.stringify({ mcpServers: servers }, null, 2)}\n`, {
|
|
250
|
+
mode: 0o600,
|
|
251
|
+
});
|
|
252
|
+
chmodSync(file, 0o600);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* The wire shape for HTTP. `launch.env` holds API keys, so every value becomes
|
|
257
|
+
* a placeholder; the keys stay so the UI can still show that env is set.
|
|
258
|
+
* Spawning keeps using the full launch from `listGuildMcp` / `listActiveMcp`.
|
|
259
|
+
*/
|
|
260
|
+
export function publicMcpServer(server: McpServer): McpServer {
|
|
261
|
+
const env = server.launch?.env;
|
|
262
|
+
const launch: McpLaunch = { ...server.launch };
|
|
263
|
+
if (env && Object.keys(env).length) {
|
|
264
|
+
launch.env = Object.fromEntries(
|
|
265
|
+
Object.keys(env).map((key) => [key, REDACTED_ENV_VALUE]),
|
|
266
|
+
);
|
|
267
|
+
} else {
|
|
268
|
+
delete launch.env;
|
|
269
|
+
}
|
|
270
|
+
return { ...server, launch };
|
|
244
271
|
}
|
|
245
272
|
|
|
246
273
|
export function listGuildMcp(dataDir: string): McpServer[] {
|
package/src/mention.ts
CHANGED
|
@@ -8,6 +8,15 @@ const LIST_MARK = /^[ \t]*(?:\d+[.)、]\s*|[-*•]\s+)/;
|
|
|
8
8
|
/** Assignee right after the marker, or after 通過後/最後/再叫… — not the first @ anywhere on the line. */
|
|
9
9
|
const LIST_ASSIGN =
|
|
10
10
|
/^(?:`?@([A-Za-z0-9_-]+)`?|(?:通過後|最後|再叫|交給|交棒(?:給)?|指派|(?:call|ask)\s+)\s*`?@([A-Za-z0-9_-]+)`?)/i;
|
|
11
|
+
/** Numbered/bullet item that names a later wave — do not start them this turn. */
|
|
12
|
+
const LIST_LATER =
|
|
13
|
+
/^(?:通過後|最後|然後|之後|完成後)\s*`?@([A-Za-z0-9_-]+)`?/i;
|
|
14
|
+
/** Prose: 四席完成後由 @infra / 才交 @infra. 再叫 is a handoff, not a defer. */
|
|
15
|
+
const DEFER_AT =
|
|
16
|
+
/(?:完成後|通過後|最後(?:才|由)?|才需要|才叫|才交)[^@\n]{0,24}@([A-Za-z0-9_-]+)/gi;
|
|
17
|
+
/** Bare handle after the same cues: 才需要call infra. */
|
|
18
|
+
const DEFER_BARE =
|
|
19
|
+
/(?:完成後|才需要|才叫)\s*(?:call|ask|由)\s*`?@?([A-Za-z0-9_-]+)`?/gi;
|
|
11
20
|
|
|
12
21
|
/** Drop fenced / inline code so `@pm` in a snippet does not dispatch. */
|
|
13
22
|
export function stripMentionNoise(text: string): string {
|
|
@@ -40,6 +49,8 @@ export type MentionBlock = {
|
|
|
40
49
|
kind: "lead" | "list";
|
|
41
50
|
};
|
|
42
51
|
|
|
52
|
+
export type MentionBot = { id: string; handle: string };
|
|
53
|
+
|
|
43
54
|
function knownHandlesIn(chunk: string, known: Set<string>): string[] {
|
|
44
55
|
const names: string[] = [];
|
|
45
56
|
for (const row of chunk.matchAll(HANDLE)) {
|
|
@@ -97,17 +108,75 @@ export function lineStartMentions(text: string, handles: string[]): MentionBlock
|
|
|
97
108
|
}));
|
|
98
109
|
}
|
|
99
110
|
|
|
111
|
+
/** Fenced samples only. Keep inline `code` so 「再叫 `@infra`」 still parses. */
|
|
112
|
+
function stripFences(text: string): string {
|
|
113
|
+
return String(text ?? "").replace(/```[\s\S]*?```/g, " ");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Seats the same message says to start after someone else finishes. */
|
|
117
|
+
export function deferredHandles(text: string, handles: string[]): string[] {
|
|
118
|
+
const raw = String(text ?? "");
|
|
119
|
+
const known = new Set(handles.map((handle) => handle.toLowerCase()));
|
|
120
|
+
const out: string[] = [];
|
|
121
|
+
const add = (name: string) => {
|
|
122
|
+
const key = name.toLowerCase();
|
|
123
|
+
if (!known.has(key) || out.includes(key)) return;
|
|
124
|
+
out.push(key);
|
|
125
|
+
};
|
|
126
|
+
const scan = stripFences(raw);
|
|
127
|
+
for (const row of scan.matchAll(new RegExp(DEFER_AT.source, "gi"))) {
|
|
128
|
+
if (row[1]) add(row[1]);
|
|
129
|
+
}
|
|
130
|
+
for (const row of scan.matchAll(new RegExp(DEFER_BARE.source, "gi"))) {
|
|
131
|
+
if (row[1]) add(row[1]);
|
|
132
|
+
}
|
|
133
|
+
let fence = false;
|
|
134
|
+
for (const part of raw.split(/(\r?\n)/)) {
|
|
135
|
+
if (part === "\n" || part === "\r\n") continue;
|
|
136
|
+
const trimmed = part.trim();
|
|
137
|
+
if (trimmed.startsWith("```")) {
|
|
138
|
+
fence = !fence;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (fence || !LIST_MARK.test(part)) continue;
|
|
142
|
+
const hit = part.replace(LIST_MARK, "").match(LIST_LATER);
|
|
143
|
+
if (hit?.[1]) add(hit[1]);
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function withoutDeferredIds(
|
|
149
|
+
ids: string[],
|
|
150
|
+
text: string,
|
|
151
|
+
bots: MentionBot[],
|
|
152
|
+
): string[] {
|
|
153
|
+
if (!ids.length) return ids;
|
|
154
|
+
const deferred = new Set(
|
|
155
|
+
deferredHandles(
|
|
156
|
+
text,
|
|
157
|
+
bots.map((bot) => bot.handle),
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
if (!deferred.size) return ids;
|
|
161
|
+
return ids.filter((id) => {
|
|
162
|
+
const bot = bots.find((row) => row.id === id);
|
|
163
|
+
return !bot || !deferred.has(bot.handle.toLowerCase());
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
100
167
|
/**
|
|
101
168
|
* Fallback when the client did not pick an assignee.
|
|
102
169
|
* Every line-start @handle group, otherwise the first @handle in prose.
|
|
170
|
+
* Seats named as a later wave (完成後 / 最後 / 才叫) stay quiet this turn.
|
|
103
171
|
*/
|
|
104
172
|
export function summonedHandles(text: string, handles: string[]): string[] {
|
|
105
173
|
const known = new Set(handles.map((handle) => handle.toLowerCase()));
|
|
174
|
+
const deferred = new Set(deferredHandles(text, handles));
|
|
106
175
|
const take = (names: string[]): string[] => {
|
|
107
176
|
const out: string[] = [];
|
|
108
177
|
for (const name of names) {
|
|
109
178
|
const key = name.toLowerCase();
|
|
110
|
-
if (!known.has(key) || out.includes(key)) continue;
|
|
179
|
+
if (!known.has(key) || deferred.has(key) || out.includes(key)) continue;
|
|
111
180
|
out.push(key);
|
|
112
181
|
}
|
|
113
182
|
return out;
|
|
@@ -117,7 +186,8 @@ export function summonedHandles(text: string, handles: string[]): string[] {
|
|
|
117
186
|
const out: string[] = [];
|
|
118
187
|
for (const block of blocks) {
|
|
119
188
|
for (const handle of block.handles) {
|
|
120
|
-
if (
|
|
189
|
+
if (deferred.has(handle) || out.includes(handle)) continue;
|
|
190
|
+
out.push(handle);
|
|
121
191
|
}
|
|
122
192
|
}
|
|
123
193
|
return out;
|
|
@@ -128,11 +198,6 @@ export function summonedHandles(text: string, handles: string[]): string[] {
|
|
|
128
198
|
return [];
|
|
129
199
|
}
|
|
130
200
|
|
|
131
|
-
/** Fenced samples only. Keep inline `code` so 「再叫 `@infra`」 still parses. */
|
|
132
|
-
function stripFences(text: string): string {
|
|
133
|
-
return String(text ?? "").replace(/```[\s\S]*?```/g, " ");
|
|
134
|
-
}
|
|
135
|
-
|
|
136
201
|
/**
|
|
137
202
|
* Bot replies: line-start specs, else first prose @handle, plus assignment verbs
|
|
138
203
|
* even when the handle is wrapped in backticks (models quote @infra a lot).
|
|
@@ -143,18 +208,17 @@ const HANDOFF_ASK =
|
|
|
143
208
|
|
|
144
209
|
export function handoffHandles(text: string, handles: string[]): string[] {
|
|
145
210
|
const known = new Set(handles.map((handle) => handle.toLowerCase()));
|
|
211
|
+
const deferred = new Set(deferredHandles(text, handles));
|
|
146
212
|
const out = summonedHandles(text, handles);
|
|
147
213
|
const ask = new RegExp(HANDOFF_ASK.source, "gi");
|
|
148
214
|
for (const row of stripFences(text).matchAll(ask)) {
|
|
149
215
|
const key = row[1].toLowerCase();
|
|
150
|
-
if (!known.has(key) || out.includes(key)) continue;
|
|
216
|
+
if (!known.has(key) || deferred.has(key) || out.includes(key)) continue;
|
|
151
217
|
out.push(key);
|
|
152
218
|
}
|
|
153
219
|
return out;
|
|
154
220
|
}
|
|
155
221
|
|
|
156
|
-
export type MentionBot = { id: string; handle: string };
|
|
157
|
-
|
|
158
222
|
export function sanitizeMentionIds(
|
|
159
223
|
raw: unknown,
|
|
160
224
|
bots: MentionBot[],
|
|
@@ -188,7 +252,7 @@ export function parseMentionIds(
|
|
|
188
252
|
if (!bot || out.includes(bot.id)) continue;
|
|
189
253
|
out.push(bot.id);
|
|
190
254
|
}
|
|
191
|
-
return out;
|
|
255
|
+
return withoutDeferredIds(out, text, bots);
|
|
192
256
|
}
|
|
193
257
|
|
|
194
258
|
/**
|
|
@@ -206,7 +270,11 @@ export function messageMentionIds(
|
|
|
206
270
|
bots,
|
|
207
271
|
message.author === "you" ? "user" : "bot",
|
|
208
272
|
);
|
|
209
|
-
return
|
|
273
|
+
return withoutDeferredIds(
|
|
274
|
+
ids.filter((id) => id !== message.author),
|
|
275
|
+
message.body,
|
|
276
|
+
bots,
|
|
277
|
+
);
|
|
210
278
|
}
|
|
211
279
|
|
|
212
280
|
/**
|
package/src/oauth.ts
CHANGED
|
@@ -1242,7 +1242,7 @@ export async function completeOAuth(input: {
|
|
|
1242
1242
|
system: string;
|
|
1243
1243
|
messages: { role: "user" | "assistant"; content: string }[];
|
|
1244
1244
|
temperature?: number;
|
|
1245
|
-
reasoning?:
|
|
1245
|
+
reasoning?: string;
|
|
1246
1246
|
tools?: boolean;
|
|
1247
1247
|
skills?: SkillRef[];
|
|
1248
1248
|
toolCtx?: ToolContext;
|
|
@@ -1301,7 +1301,7 @@ export async function completeOAuth(input: {
|
|
|
1301
1301
|
});
|
|
1302
1302
|
const options: {
|
|
1303
1303
|
temperature?: number;
|
|
1304
|
-
reasoning?:
|
|
1304
|
+
reasoning?: string;
|
|
1305
1305
|
signal?: AbortSignal;
|
|
1306
1306
|
transformHeaders?: (
|
|
1307
1307
|
headers: Record<string, string | null>,
|
package/src/opencode-free.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ModelEntry, ProviderEntry } from "@guild/protocol";
|
|
2
|
+
import { guildUserAgent } from "./version.ts";
|
|
2
3
|
|
|
3
4
|
/** Built-in alias `free` → this provider, matching Hermes Agent (2026-08). */
|
|
4
5
|
export const OPENCODE_FREE_PROVIDER_ID = "opencode-free";
|
|
@@ -94,7 +95,7 @@ export function llmRequestHeaders(target: {
|
|
|
94
95
|
"content-type": "application/json",
|
|
95
96
|
"http-referer": "https://github.com/Jakevin/guild",
|
|
96
97
|
"x-title": "Guild",
|
|
97
|
-
"user-agent":
|
|
98
|
+
"user-agent": guildUserAgent(),
|
|
98
99
|
...extra,
|
|
99
100
|
};
|
|
100
101
|
delete headers.authorization;
|
|
@@ -126,7 +127,7 @@ export async function fetchOpenCodeFreeModels(
|
|
|
126
127
|
const res = await fetch(`${OPENCODE_FREE_BASE_URL}/models`, {
|
|
127
128
|
headers: {
|
|
128
129
|
accept: "application/json",
|
|
129
|
-
"user-agent":
|
|
130
|
+
"user-agent": guildUserAgent(),
|
|
130
131
|
"http-referer": "https://github.com/Jakevin/guild",
|
|
131
132
|
"x-title": "Guild",
|
|
132
133
|
},
|