freecodego 0.1.6-alpha.2.4 → 0.1.7-alpha.2.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/bootstrap.js CHANGED
@@ -1,17 +1,18 @@
1
1
  // packages/freecodego/harness-plugin/lib/index.js
2
2
  import { createRequire as createRequire2 } from "node:module";
3
3
  import path, { basename as basename2, dirname as dirname2, extname, isAbsolute as isAbsolute2, join as join4, matchesGlob, normalize as normalize2, relative as relative2, resolve as resolve2, sep } from "node:path";
4
+ import { credentialRef as credentialRef2 } from "@deepseek-ai/dsh-credentials";
4
5
  import { constants, createCipheriv, createHash as createHash3, createHmac, createPublicKey, publicEncrypt, randomBytes, randomUUID as randomUUID3, timingSafeEqual } from "node:crypto";
5
6
  import fs, { appendFile, copyFile as copyFile2, mkdir as mkdir4, mkdtemp, open as open2, readFile as readFile3, readdir as readdir2, readlink, realpath, rename as rename3, rm as rm2, stat as stat2, symlink, unlink, writeFile as writeFile3 } from "node:fs/promises";
6
7
  import { availableParallelism, homedir, platform as platform2, release, tmpdir } from "node:os";
7
8
  import { deflateRawSync, gunzipSync, inflateRawSync, inflateSync } from "node:zlib";
8
- import z2 from "@deepseek-ai/schemastery";
9
9
  import { appendFileSync, chmodSync, constants as constants$1, createWriteStream, existsSync as existsSync2, lstatSync, mkdirSync, readFileSync as readFileSync2, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
10
10
  import { writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
11
11
  import { expandHomePath, resolveDshHome } from "@deepseek-ai/dsh-home-paths";
12
12
  import { execFile, spawn as spawn2 } from "node:child_process";
13
- import { BlockAssembler, EMPTY_RESPONSE_CODE, LlmAdapter, LlmError, ReasoningEffortId, ToolCallId, attributionHeaders, createAssistantMessage as createAssistantMessage2, createMessage, createToolResultMessage as createToolResultMessage2, createUserMessage } from "@deepseek-ai/dsh-llm";
13
+ import { BlockAssembler, EMPTY_RESPONSE_CODE, LlmAdapter, LlmError, ReasoningEffortId, ToolCallId, attributionHeaders, createAssistantMessage as createAssistantMessage2, createToolResultMessage as createToolResultMessage2, createUserMessage } from "@deepseek-ai/dsh-llm";
14
14
  import { promisify } from "node:util";
15
+ import z2 from "@deepseek-ai/schemastery";
15
16
  import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
16
17
 
17
18
  // packages/freecodego/native-runtime-host/lib/index.js
@@ -1296,8 +1297,7 @@ import { createServer } from "node:http";
1296
1297
  import "@deepseek-ai/dsh-token-meter/src/estimate.ts";
1297
1298
  import { apply } from "@deepseek-ai/dsh-mcp-client";
1298
1299
  import { apply as apply$1 } from "@deepseek-ai/dsh-skill-filesystem";
1299
- import { load } from "js-yaml";
1300
- import { credentialRef as credentialRef2 } from "@deepseek-ai/dsh-credentials";
1300
+ import yaml, { load } from "js-yaml";
1301
1301
  import { KNOWN_SESSION_EVENT_TYPES, SessionId } from "@deepseek-ai/dsh-session";
1302
1302
  import { SANDBOX_MODES, setSandboxMode } from "@deepseek-ai/dsh-sandbox-policy";
1303
1303
  import { setApprovalPolicy } from "@deepseek-ai/dsh-user-approval";
@@ -1356,6 +1356,38 @@ var FreeCodeGoMobileAuthClient = class {
1356
1356
  async sendVerifyCode(email, signal) {
1357
1357
  return { countdown: finiteNumber$1((await this.request("/send-verify-code", { email }, signal)).countdown, "countdown") };
1358
1358
  }
1359
+ /**
1360
+ * Ask the gateway to mail a password-reset code to one address.
1361
+ *
1362
+ * `method: 'code'` is what makes this the desktop flow: without it the gateway
1363
+ * mails a browser link built from its own frontend URL instead, which a
1364
+ * webview has nowhere to land. The gateway answers the same way whether or not
1365
+ * the address is registered — that is deliberate anti-enumeration behaviour on
1366
+ * its side, not an answer about this account.
1367
+ * @param email - the address to send the reset code to.
1368
+ * @param signal - aborts the request when the caller cancels.
1369
+ */
1370
+ async requestPasswordResetCode(email, signal) {
1371
+ await this.request("/forgot-password", {
1372
+ email,
1373
+ method: "code"
1374
+ }, signal);
1375
+ }
1376
+ /**
1377
+ * Replace the account's password using the emailed reset code.
1378
+ *
1379
+ * Deliberately returns no tokens: the gateway does not issue a session here, so
1380
+ * signing in stays a separate, explicit step with the new password.
1381
+ * @param input - the address, the code, and the new password.
1382
+ * @param signal - aborts the request when the caller cancels.
1383
+ */
1384
+ async resetPassword(input, signal) {
1385
+ await this.request("/reset-password", {
1386
+ email: input.email,
1387
+ verify_code: input.verifyCode,
1388
+ new_password: input.newPassword
1389
+ }, signal);
1390
+ }
1359
1391
  /** Register and return an authenticated token pair to the host caller.
1360
1392
  * @param signal - aborts the request when the caller cancels.
1361
1393
  * @param input - the address, password, and remember-me intent.
@@ -1463,12 +1495,18 @@ function tokens(payload) {
1463
1495
  tokenType: "Bearer"
1464
1496
  };
1465
1497
  }
1498
+ var addressLocalPart = (email) => {
1499
+ const at2 = email.indexOf("@");
1500
+ return (at2 <= 0 ? email : email.slice(0, at2)).trim();
1501
+ };
1466
1502
  function user(payload) {
1467
1503
  const avatarUrl = typeof payload.avatar_url === "string" && payload.avatar_url.trim() !== "" ? payload.avatar_url.trim() : typeof payload.avatarUrl === "string" && payload.avatarUrl.trim() !== "" ? payload.avatarUrl.trim() : void 0;
1504
+ const email = requiredString(payload.email, "user.email");
1505
+ const username = typeof payload.username === "string" && payload.username.trim() !== "" ? payload.username.trim() : addressLocalPart(email);
1468
1506
  return {
1469
1507
  id: finiteNumber$1(payload.id, "user.id"),
1470
- username: requiredString(payload.username, "user.username"),
1471
- email: requiredString(payload.email, "user.email"),
1508
+ username,
1509
+ email,
1472
1510
  ...avatarUrl === void 0 ? {} : { avatarUrl },
1473
1511
  role: requiredString(payload.role, "user.role"),
1474
1512
  balance: finiteNumber$1(payload.balance, "user.balance"),
@@ -1559,9 +1597,9 @@ function asAttemptResult(value) {
1559
1597
  };
1560
1598
  }
1561
1599
  async function runGuardedRefresh(input) {
1562
- const classify = input.classify ?? classifyRefreshFailure;
1600
+ const classify2 = input.classify ?? classifyRefreshFailure;
1563
1601
  const controller = new AbortController();
1564
- const first = await attemptOnce(input.attempt, controller.signal, false, classify);
1602
+ const first = await attemptOnce(input.attempt, controller.signal, false, classify2);
1565
1603
  if (first.ok) return {
1566
1604
  ok: true,
1567
1605
  value: first.value,
@@ -1583,7 +1621,7 @@ async function runGuardedRefresh(input) {
1583
1621
  ...passthrough(first)
1584
1622
  };
1585
1623
  await (input.wait ?? defaultWait)(input.retryDelayMs ?? 1e3);
1586
- const second = await attemptOnce(input.attempt, controller.signal, true, classify);
1624
+ const second = await attemptOnce(input.attempt, controller.signal, true, classify2);
1587
1625
  if (second.ok) return {
1588
1626
  ok: true,
1589
1627
  value: second.value,
@@ -1602,7 +1640,7 @@ async function runGuardedRefresh(input) {
1602
1640
  function passthrough(result) {
1603
1641
  return result.ok || result.message === void 0 ? {} : { message: result.message };
1604
1642
  }
1605
- async function attemptOnce(attempt, signal, retry, classify) {
1643
+ async function attemptOnce(attempt, signal, retry, classify2) {
1606
1644
  try {
1607
1645
  const outcome = await attempt({
1608
1646
  signal,
@@ -1615,7 +1653,7 @@ async function attemptOnce(attempt, signal, retry, classify) {
1615
1653
  };
1616
1654
  return reported;
1617
1655
  } catch (error) {
1618
- const classified = classify(error);
1656
+ const classified = classify2(error);
1619
1657
  return {
1620
1658
  ok: false,
1621
1659
  kind: classified.kind,
@@ -1810,6 +1848,31 @@ var FreeCodeGoAccountCoordinator = class {
1810
1848
  return this.auth.sendVerifyCode(email, signal);
1811
1849
  }
1812
1850
  /**
1851
+ * Mail a password-reset code for one address.
1852
+ *
1853
+ * Straight through to the gateway: recovery is the one account operation that
1854
+ * needs no session, no vault write and no identity, so there is nothing here
1855
+ * for the coordinator to keep.
1856
+ * @param email - the address the reset code is sent to.
1857
+ * @param signal - aborts the request when the caller cancels.
1858
+ */
1859
+ async requestPasswordResetCode(email, signal) {
1860
+ return this.auth.requestPasswordResetCode(email, signal);
1861
+ }
1862
+ /**
1863
+ * Replace the password using the emailed reset code.
1864
+ *
1865
+ * It deliberately does not sign in afterwards — the gateway issues no session
1866
+ * here — so the caller keeps the login form as the next step. A reset also
1867
+ * invalidates nothing locally: this machine's stored password, if the user
1868
+ * asked for one to be kept, is the caller's business to rewrite.
1869
+ * @param input - the address, the code, and the new password.
1870
+ * @param signal - aborts the request when the caller cancels.
1871
+ */
1872
+ async resetPassword(input, signal) {
1873
+ return this.auth.resetPassword(input, signal);
1874
+ }
1875
+ /**
1813
1876
  * Authenticate and store a successful token pair in the host vault.
1814
1877
  *
1815
1878
  * The intent of this attempt is recorded here and committed in
@@ -3255,6 +3318,7 @@ function array(value, label) {
3255
3318
  }
3256
3319
 
3257
3320
  // packages/freecodego/harness-plugin/lib/index.js
3321
+ import { isVolatile } from "@deepseek-ai/cosmokit";
3258
3322
  import { Buffer as Buffer$1 } from "node:buffer";
3259
3323
  import { deriveTurnTokenUsage } from "@deepseek-ai/dsh-token-meter/client";
3260
3324
 
@@ -7632,20 +7696,6 @@ function walk$1(value, ancestors, onCycle) {
7632
7696
  ancestors.delete(value);
7633
7697
  }
7634
7698
  }
7635
- var FreeCodeGoGuardSettingsSchema = z2.object({
7636
- envReadGuardEnabled: z2.boolean().default(true),
7637
- doomLoopGuardEnabled: z2.boolean().default(true),
7638
- lspEnabled: z2.boolean().default(true),
7639
- commandPolicyEnabled: z2.boolean().default(true),
7640
- planModeEnabled: z2.boolean().default(true),
7641
- contextBudgetEnabled: z2.boolean().default(true),
7642
- cacheColdClearEnabled: z2.boolean().default(true),
7643
- cacheBreakAttributionEnabled: z2.boolean().default(true),
7644
- assistantLoopGuardEnabled: z2.boolean().default(true),
7645
- spillRecallEnabled: z2.boolean().default(true),
7646
- compactionFidelityEnabled: z2.boolean().default(true),
7647
- promptCompositionEnabled: z2.boolean().default(true)
7648
- });
7649
7699
  var SECRET_BASENAME = /^(?:\.env(?!\.(?:example|sample|template|dist)$)(?:\..+)?|\.credentials\.ya?ml(?!\.(?:example|sample|template|dist)$)(?:\..+)?|\.envrc|\.npmrc|\.netrc|\.pypirc|\.git-credentials|\.htpasswd|id_(?:rsa|dsa|ecdsa|ed25519)(?:_sk)?(?:\.pub)?|.+_rsa|.+_ed25519|.+\.pem|.+\.pfx|.+\.p12|.+\.keystore|secrets?\.json|secrets?\.ya?ml|credentials\.json)$/;
7650
7700
  var SECRET_DIRECTORIES = /* @__PURE__ */ new Set([".ssh", ".gnupg"]);
7651
7701
  var SECRET_FILE_IN_DIRECTORIES = [
@@ -10547,10 +10597,7 @@ function hookSignalOf(value) {
10547
10597
  }
10548
10598
  function hookContextMessage(text2) {
10549
10599
  return createUserMessage({
10550
- source: {
10551
- kind: "plugin",
10552
- plugin: "freecodego-hooks"
10553
- },
10600
+ source: { kind: "freecodego-hooks" },
10554
10601
  content: [{
10555
10602
  type: "text",
10556
10603
  text: text2
@@ -12267,6 +12314,9 @@ function isCancellationWording(message2) {
12267
12314
  }
12268
12315
  var ROUTE_TTL_MS = 1440 * 60 * 1e3;
12269
12316
  var DEFAULT_STREAM_IDLE_MS = 12e4;
12317
+ function bridgeRouteIdOf(baseURL) {
12318
+ return /\/anthropic\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:\/v1)?\/?$/iu.exec(baseURL.trim())?.[1];
12319
+ }
12270
12320
  function callId$1(value) {
12271
12321
  return value;
12272
12322
  }
@@ -12356,6 +12406,22 @@ var ClaudeProtocolBridge = class {
12356
12406
  apiKey: this.secret
12357
12407
  };
12358
12408
  }
12409
+ /**
12410
+ * Whether this process still serves a route id.
12411
+ *
12412
+ * A route belongs to the listener that minted it: `dispose()` clears the map and
12413
+ * a new process mints new ids and a new port, so a saved endpoint whose id is
12414
+ * absent here answers nothing. That is the question a binding written before a
12415
+ * restart has to ask before it can be kept — see `web-search-binding.ts` — and
12416
+ * it is asked of the bridge rather than of a copy of the map, because only the
12417
+ * bridge knows when a route expired.
12418
+ * @param id - route id parsed off a saved endpoint ({@link bridgeRouteIdOf}).
12419
+ * @returns whether this bridge would answer for it.
12420
+ */
12421
+ hasRoute(id) {
12422
+ this.pruneRoutes();
12423
+ return this.routes.has(id);
12424
+ }
12359
12425
  /** Drop routes older than the TTL so a long-lived bridge cannot leak them. */
12360
12426
  pruneRoutes() {
12361
12427
  const cutoff = Date.now() - ROUTE_TTL_MS;
@@ -12917,8 +12983,7 @@ function estimateAnthropicInputTokens(body) {
12917
12983
  }
12918
12984
  function toResponsesGenerateOptions(route, body) {
12919
12985
  const messages = [];
12920
- if (typeof body.instructions === "string" && body.instructions.trim() !== "") messages.push(createMessage({
12921
- role: "user",
12986
+ if (typeof body.instructions === "string" && body.instructions.trim() !== "") messages.push(createUserMessage({
12922
12987
  content: [{
12923
12988
  type: "text",
12924
12989
  text: body.instructions
@@ -12928,8 +12993,7 @@ function toResponsesGenerateOptions(route, body) {
12928
12993
  if (Array.isArray(body.input)) for (const value of body.input) {
12929
12994
  const item = value;
12930
12995
  if (item.type === "function_call") {
12931
- messages.push(createMessage({
12932
- role: "assistant",
12996
+ messages.push(createAssistantMessage2({
12933
12997
  content: [{
12934
12998
  type: "tool-call",
12935
12999
  id: callId$1(String(item.call_id ?? item.id ?? "tool")),
@@ -12937,7 +13001,6 @@ function toResponsesGenerateOptions(route, body) {
12937
13001
  arguments: String(item.arguments ?? "")
12938
13002
  }],
12939
13003
  source: {
12940
- kind: "model",
12941
13004
  provider: "bridge",
12942
13005
  model: "bridge"
12943
13006
  }
@@ -12946,20 +13009,13 @@ function toResponsesGenerateOptions(route, body) {
12946
13009
  }
12947
13010
  if (item.type === "function_call_output") {
12948
13011
  const call = String(item.call_id ?? "tool");
12949
- messages.push(createMessage({
12950
- role: "user",
13012
+ messages.push(createToolResultMessage2({
13013
+ callId: callId$1(call),
12951
13014
  content: [{
12952
- type: "tool-result",
12953
- toolCallId: callId$1(call),
12954
- content: [{
12955
- type: "text",
12956
- text: typeof item.output === "string" ? item.output : JSON.stringify(item.output ?? "")
12957
- }]
13015
+ type: "text",
13016
+ text: typeof item.output === "string" ? item.output : JSON.stringify(item.output ?? "")
12958
13017
  }],
12959
- source: {
12960
- kind: "tool",
12961
- callId: callId$1(call)
12962
- }
13018
+ isError: item.is_error === true
12963
13019
  }));
12964
13020
  continue;
12965
13021
  }
@@ -12975,14 +13031,15 @@ function toResponsesGenerateOptions(route, body) {
12975
13031
  text: text2
12976
13032
  }] : [];
12977
13033
  });
12978
- if (blocks.length > 0) messages.push(createMessage({
12979
- role,
13034
+ if (blocks.length > 0) messages.push(role === "assistant" ? createAssistantMessage2({
12980
13035
  content: blocks,
12981
- source: role === "assistant" ? {
12982
- kind: "model",
13036
+ source: {
12983
13037
  provider: "bridge",
12984
13038
  model: "bridge"
12985
- } : { kind: "user" }
13039
+ }
13040
+ }) : createUserMessage({
13041
+ content: blocks,
13042
+ source: { kind: "user" }
12986
13043
  }));
12987
13044
  }
12988
13045
  const tools = Array.isArray(body.tools) ? body.tools.flatMap((value) => {
@@ -13007,45 +13064,65 @@ var BRIDGE_IMAGE_PLACEHOLDER = "[image attachment: not forwarded by the FreeCode
13007
13064
  function anthropicMessages(value) {
13008
13065
  const item = value;
13009
13066
  const role = item.role === "assistant" ? "assistant" : "user";
13010
- const blocks = (Array.isArray(item.content) ? item.content : [{
13067
+ const content = Array.isArray(item.content) ? item.content : [{
13011
13068
  type: "text",
13012
13069
  text: String(item.content ?? "")
13013
- }]).flatMap((block) => {
13070
+ }];
13071
+ const messages = [];
13072
+ const blocks = [];
13073
+ const flushTurn = () => {
13074
+ if (blocks.length === 0) return;
13075
+ messages.push(role === "assistant" ? createAssistantMessage2({
13076
+ content: blocks.splice(0),
13077
+ source: {
13078
+ provider: "bridge",
13079
+ model: "bridge"
13080
+ }
13081
+ }) : createUserMessage({
13082
+ content: blocks.splice(0),
13083
+ source: { kind: "user" }
13084
+ }));
13085
+ };
13086
+ for (const block of content) {
13014
13087
  const part = block;
13015
- if (part.type === "text") return [{
13016
- type: "text",
13017
- text: String(part.text ?? "")
13018
- }];
13019
- if (part.type === "tool_use") return [{
13020
- type: "tool-call",
13021
- id: callId$1(String(part.id ?? randomUUID3())),
13022
- name: String(part.name ?? "tool"),
13023
- arguments: JSON.stringify(part.input ?? {})
13024
- }];
13025
- if (part.type === "tool_result") return [{
13026
- type: "tool-result",
13027
- toolCallId: callId$1(String(part.tool_use_id ?? "tool")),
13028
- content: [{
13088
+ if (part.type === "tool_result") {
13089
+ flushTurn();
13090
+ messages.push(createToolResultMessage2({
13091
+ callId: callId$1(String(part.tool_use_id ?? "tool")),
13092
+ content: [{
13093
+ type: "text",
13094
+ text: toolResultText(part.content)
13095
+ }],
13096
+ isError: part.is_error === true
13097
+ }));
13098
+ continue;
13099
+ }
13100
+ if (part.type === "text") {
13101
+ blocks.push({
13029
13102
  type: "text",
13030
- text: toolResultText(part.content)
13031
- }]
13032
- }];
13033
- if (part.type === "image") return [{
13034
- type: "text",
13035
- text: BRIDGE_IMAGE_PLACEHOLDER
13036
- }];
13037
- return [];
13038
- });
13039
- if (blocks.length === 0) return [];
13040
- return [createMessage({
13041
- role,
13042
- content: blocks,
13043
- source: role === "assistant" ? {
13044
- kind: "model",
13045
- provider: "bridge",
13046
- model: "bridge"
13047
- } : { kind: "user" }
13048
- })];
13103
+ text: String(part.text ?? "")
13104
+ });
13105
+ continue;
13106
+ }
13107
+ if (part.type === "tool_use") {
13108
+ blocks.push({
13109
+ type: "tool-call",
13110
+ id: callId$1(String(part.id ?? randomUUID3())),
13111
+ name: String(part.name ?? "tool"),
13112
+ arguments: JSON.stringify(part.input ?? {})
13113
+ });
13114
+ continue;
13115
+ }
13116
+ if (part.type === "image") {
13117
+ blocks.push({
13118
+ type: "text",
13119
+ text: BRIDGE_IMAGE_PLACEHOLDER
13120
+ });
13121
+ continue;
13122
+ }
13123
+ }
13124
+ flushTurn();
13125
+ return messages;
13049
13126
  }
13050
13127
  function toolResultText(content) {
13051
13128
  if (typeof content === "string") return content;
@@ -13360,40 +13437,6 @@ async function inlineImageAttachmentBlocks(content, read) {
13360
13437
  function callId2(value) {
13361
13438
  return value;
13362
13439
  }
13363
- var FreeCodeGoCapabilitySettingsSchema = z2.object({
13364
- mcpEnabled: z2.boolean().default(false),
13365
- skillEnabled: z2.boolean().default(false),
13366
- voiceInputEnabled: z2.boolean().default(true),
13367
- sessionDeleteEnabled: z2.boolean().default(true),
13368
- modelCategories: z2.dict(z2.union([
13369
- z2.const("text"),
13370
- z2.const("image"),
13371
- z2.const("video"),
13372
- z2.const("audio")
13373
- ])).default({}),
13374
- mcpServers: z2.array(z2.object({
13375
- id: z2.string().min(1).max(80),
13376
- enabled: z2.boolean().default(true),
13377
- transport: z2.union([z2.const("stdio"), z2.const("streamable-http")]),
13378
- serverName: z2.string().pattern(SERVER_NAME$1),
13379
- command: z2.string().default(""),
13380
- args: z2.array(z2.string().max(4096)).default([]),
13381
- env: z2.dict(z2.string()).default({}),
13382
- cwd: z2.string().default(""),
13383
- url: z2.string().default(""),
13384
- headers: z2.dict(z2.string()).default({})
13385
- })).default([]),
13386
- skillRoots: z2.array(z2.object({
13387
- id: z2.string().min(1).max(80),
13388
- enabled: z2.boolean().default(true),
13389
- path: z2.string().min(1).max(4096)
13390
- })).default([]),
13391
- skillInvocationOverrides: z2.dict(z2.boolean()).default({}),
13392
- preferredSkillPlacement: z2.union([z2.object({
13393
- agent: z2.union([z2.const("harness"), z2.const("agents")]),
13394
- scope: z2.union([z2.const("project"), z2.const("user")])
13395
- }), z2.const(null)])
13396
- });
13397
13440
  var NO_PROJECT_ENTRIES = {
13398
13441
  mcpServers: [],
13399
13442
  skillRoots: []
@@ -14240,6 +14283,8 @@ function toMcpClientConfig(server) {
14240
14283
  var MAX_SOURCE_BYTES = 2e6;
14241
14284
  var MAX_SOURCE_FILES = 50;
14242
14285
  var RESOURCE_NAME = /^[A-Za-z0-9_.:/@-]{1,256}$/;
14286
+ var CONFLICT_PROTECTION_KEY = "pluginConflictProtectionEnabled";
14287
+ var NO_SETTINGS_PORT_CONFLICT_PROTECTION = false;
14243
14288
  var OWN_STAND_IN_MODULES = /* @__PURE__ */ new Set(["freecodego/agent-team", "freecodego/tool-agent-team"]);
14244
14289
  var OWN_STAND_IN_ENTRY_IDS = /* @__PURE__ */ new Set(["freecodego-agent-team", "freecodego-tool-agent-team"]);
14245
14290
  function isOfficialModuleName(moduleName) {
@@ -14250,27 +14295,6 @@ function isOwnStandIn(moduleName, entryId) {
14250
14295
  const tail = entryId.slice(entryId.lastIndexOf(":") + 1);
14251
14296
  return OWN_STAND_IN_ENTRY_IDS.has(tail);
14252
14297
  }
14253
- var FreeCodeGoPluginConflictSettingsSchema = z2.object({
14254
- pluginConflictProtectionEnabled: z2.boolean().default(false),
14255
- pluginConflictRecords: z2.array(z2.object({
14256
- id: z2.string().min(1).max(256),
14257
- detectedAt: z2.number(),
14258
- resource: z2.union([
14259
- z2.const("tool"),
14260
- z2.const("command"),
14261
- z2.const("settings"),
14262
- z2.const("route"),
14263
- z2.const("provider"),
14264
- z2.const("slot")
14265
- ]),
14266
- resourceName: z2.string().min(1).max(256),
14267
- disabledEntryId: z2.string().min(1).max(512),
14268
- disabledModuleName: z2.string().min(1).max(512),
14269
- keptEntryId: z2.string().min(1).max(512),
14270
- keptModuleName: z2.string().min(1).max(512),
14271
- yieldedToOfficial: z2.boolean()
14272
- })).default([])
14273
- });
14274
14298
  function scanPluginResourceClaims(source) {
14275
14299
  const claims = /* @__PURE__ */ new Map();
14276
14300
  const add = (resource, resourceName) => {
@@ -14316,7 +14340,7 @@ var FreeCodeGoPluginConflictGuard = class {
14316
14340
  this.ctx = ctx;
14317
14341
  this.settings = settings;
14318
14342
  this.records = settings?.get()?.pluginConflictRecords ?? [];
14319
- this.enabled = settings?.get()?.pluginConflictProtectionEnabled ?? bootstrapConflictProtectionEnabled();
14343
+ this.enabled = settings?.get()?.[CONFLICT_PROTECTION_KEY] ?? NO_SETTINGS_PORT_CONFLICT_PROTECTION;
14320
14344
  }
14321
14345
  /**
14322
14346
  * Install plugin-owned Loader lifecycle wrappers and seed active entry claims.
@@ -14351,13 +14375,17 @@ var FreeCodeGoPluginConflictGuard = class {
14351
14375
  };
14352
14376
  }, "freecodego: plugin conflict lifecycle guard");
14353
14377
  }
14354
- /** Attach settings after a pre-Loader bootstrap installation.
14378
+ /** Attach the settings port this guard reads the switch and its records through.
14379
+ *
14380
+ * The guard is a per-root singleton, so a later mount that carries a port re-points the
14381
+ * existing instance at the live document instead of leaving it on the answer it was
14382
+ * constructed with.
14355
14383
  * @param settings - the plugin settings port, when one is available.
14356
14384
  */
14357
14385
  configure(settings) {
14358
14386
  if (settings === void 0 || settings === this.settings) return;
14359
14387
  this.settings = settings;
14360
- this.enabled = settings.get()?.pluginConflictProtectionEnabled ?? bootstrapConflictProtectionEnabled();
14388
+ this.enabled = settings.get()?.[CONFLICT_PROTECTION_KEY] ?? NO_SETTINGS_PORT_CONFLICT_PROTECTION;
14361
14389
  if (this.started) this.installInterception();
14362
14390
  const persisted = settings.get()?.pluginConflictRecords ?? [];
14363
14391
  const merged = [...persisted, ...this.records.filter((record3) => !persisted.some((item) => sameConflict(item, record3)))].slice(-100);
@@ -14404,13 +14432,26 @@ var FreeCodeGoPluginConflictGuard = class {
14404
14432
  protectionEnabled() {
14405
14433
  return this.settings?.get()?.pluginConflictProtectionEnabled ?? this.enabled;
14406
14434
  }
14407
- /** Persist the global automatic-repair switch.
14435
+ /**
14436
+ * Persist the global automatic-repair switch, and start or stop acting on it.
14437
+ *
14438
+ * Recording the value is not enough in the on direction. A guard built while the switch was
14439
+ * off never installed its wrappers — that is deliberate, since leaving them in place would
14440
+ * keep this plugin reading every entry's package while the deployment had said not to — so a
14441
+ * guard that only wrote the new value would report protection as on while the running Loader
14442
+ * went on starting every duplicate, and would silently stay inert until the next boot. The
14443
+ * install has to come after the write resolves, because the live document is what
14444
+ * {@link protectionEnabled} reads back.
14445
+ *
14446
+ * The off direction needs nothing: the wrappers ask `protectionEnabled()` per entry, so a
14447
+ * switch turned off stops the answering without being uninstalled.
14408
14448
  * @param enabled - whether this capability is switched on.
14409
14449
  * @returns the plugin Conflict Status.
14410
14450
  */
14411
14451
  async setEnabled(enabled) {
14412
14452
  this.enabled = enabled;
14413
14453
  await this.settings?.update({ pluginConflictProtectionEnabled: enabled });
14454
+ if (enabled) this.installInterception();
14414
14455
  return this.snapshot();
14415
14456
  }
14416
14457
  async seed() {
@@ -14654,14 +14695,6 @@ function relativeModuleSpecifiers(source) {
14654
14695
  add(/\brequire\s*\(\s*(['"])(\.{1,2}\/[^'"]+)\1\s*\)/g);
14655
14696
  return [...specifiers];
14656
14697
  }
14657
- function bootstrapConflictProtectionEnabled() {
14658
- const settingsPath = join4(harnessHomeDirectory(), "settings.yaml");
14659
- try {
14660
- return readFileSync2(settingsPath, "utf8").match(/^freecodego-harness:\s*\r?\n(?:[ \t]+[^\r\n]*\r?\n)*?[ \t]+pluginConflictProtectionEnabled:\s*(true|false)\s*(?:#.*)?$/m)?.[1] === "true";
14661
- } catch {
14662
- return false;
14663
- }
14664
- }
14665
14698
  function sameConflict(left, right) {
14666
14699
  return left.resource === right.resource && left.resourceName === right.resourceName && left.disabledEntryId === right.disabledEntryId && left.disabledModuleName === right.disabledModuleName && left.keptEntryId === right.keptEntryId && left.keptModuleName === right.keptModuleName;
14667
14700
  }
@@ -14687,7 +14720,6 @@ var MAX_UNCONFIRMED_STARTUPS = 2;
14687
14720
  var DSH_INSTALL_TIMEOUT_MS = 15 * 6e4;
14688
14721
  var NPM_PACKAGE_RE$2 = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/i;
14689
14722
  var RELEASE_TAG_RE = /^(?:freecodego-)?v(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/u;
14690
- var FreeCodeGoPluginUpdateSettingsSchema = z2.object({ pluginUpdateChecksEnabled: z2.boolean().default(true) });
14691
14723
  var FreeCodeGoPluginUpdateService = class {
14692
14724
  options;
14693
14725
  packageName;
@@ -16973,10 +17005,7 @@ var FreeCodeGoAdvisorRuntime = class {
16973
17005
  const evidence = new AdvisorEvidenceCache((call, signal) => executeReviewTool(this.ctx, agent, call, signal));
16974
17006
  const findings = await Promise.all(Object.entries(COUNCIL_ROLES).map(async ([role, instruction]) => {
16975
17007
  const messages = [createUserMessage({
16976
- source: {
16977
- kind: "plugin",
16978
- plugin: "freecodego-advisor-council"
16979
- },
17008
+ source: { kind: "freecodego-advisor-council" },
16980
17009
  content: [{
16981
17010
  type: "text",
16982
17011
  text: `${reviewPrompt$2(delta, watchdog.instructions)}
@@ -17093,10 +17122,7 @@ Council perspective: ${role}. ${instruction}`
17093
17122
  const watchdog = await discoverWatchdog(agent.session.header.cwd);
17094
17123
  this.watchdogFilesBySession.set(String(agent.id), watchdog.files);
17095
17124
  const messages = [createUserMessage({
17096
- source: {
17097
- kind: "plugin",
17098
- plugin: "freecodego-advisor"
17099
- },
17125
+ source: { kind: "freecodego-advisor" },
17100
17126
  content: [{
17101
17127
  type: "text",
17102
17128
  text: reviewPrompt$2(delta, watchdog.instructions)
@@ -17235,10 +17261,7 @@ Perspective: ${perspective}`}`,
17235
17261
  cooldownUntilTurn: state.cooldownUntilTurn
17236
17262
  });
17237
17263
  const message2 = createUserMessage({
17238
- source: {
17239
- kind: "plugin",
17240
- plugin: "freecodego-advisor"
17241
- },
17264
+ source: { kind: "freecodego-advisor" },
17242
17265
  content: [{
17243
17266
  type: "text",
17244
17267
  text: `<advisory severity="${advice.severity}" guidance="weigh, do not blindly obey">
@@ -17351,7 +17374,6 @@ function messageText$2(message2) {
17351
17374
  return message2.content.map((block) => {
17352
17375
  if (block.type === "text" || block.type === "reasoning") return block.text;
17353
17376
  if (block.type === "tool-call") return `${block.name}(${block.arguments})`;
17354
- if (block.type === "tool-result") return block.content.map((item) => item.type === "text" ? item.text : `[${item.type}]`).join("\n");
17355
17377
  return `[${block.type}]`;
17356
17378
  }).join("\n");
17357
17379
  }
@@ -20943,10 +20965,7 @@ var FreeCodeGoEngineCouncil = class {
20943
20965
  try {
20944
20966
  signal.throwIfAborted();
20945
20967
  agent.followup(createUserMessage({
20946
- source: {
20947
- kind: "plugin",
20948
- plugin: "freecodego-engine-council"
20949
- },
20968
+ source: { kind: "freecodego-engine-council" },
20950
20969
  content: [{
20951
20970
  type: "text",
20952
20971
  text: prompt
@@ -21264,10 +21283,7 @@ function terminalReport(task, request, runtimes, state, quorum, error) {
21264
21283
  function injectCouncilHandoff(parent, report) {
21265
21284
  try {
21266
21285
  parent.inject(createUserMessage({
21267
- source: {
21268
- kind: "plugin",
21269
- plugin: "freecodego-engine-council"
21270
- },
21286
+ source: { kind: "freecodego-engine-council" },
21271
21287
  content: [{
21272
21288
  type: "text",
21273
21289
  text: "Engineering council " + report.id + " finished with state " + report.state + ". Read the durable report before proceeding. " + report.finalRecommendation
@@ -21337,8 +21353,9 @@ function sameRoutes(left, right) {
21337
21353
  return left.length === right.length && left.every((route, index) => route.provider === right[index]?.provider && route.model === right[index]?.model);
21338
21354
  }
21339
21355
  async function synchronizeSubagentModelRoutes(settings, llm) {
21340
- const current = settings.get(SUBAGENT_MODEL_SELECTION_NAMESPACE);
21341
- if (current === void 0) return [];
21356
+ const descriptor = settings.describe().find((row) => row.ns === SUBAGENT_MODEL_SELECTION_NAMESPACE);
21357
+ if (descriptor === void 0) return [];
21358
+ const current = descriptor.value;
21342
21359
  const previous = routesOf(current);
21343
21360
  const previousByProvider = /* @__PURE__ */ new Map();
21344
21361
  for (const route of previous) {
@@ -21381,7 +21398,7 @@ async function synchronizeSubagentModelRoutes(settings, llm) {
21381
21398
  allowedModels: filtered
21382
21399
  };
21383
21400
  if (currentRecord.enabled === void 0) patch.enabled = true;
21384
- if (!sameRoutes(previous, filtered) || currentRecord.enabled === void 0) await settings.update(SUBAGENT_MODEL_SELECTION_NAMESPACE, patch);
21401
+ if (!sameRoutes(previous, filtered) || currentRecord.enabled === void 0) await settings.update(SUBAGENT_MODEL_SELECTION_NAMESPACE, patch, descriptor.revision);
21385
21402
  return filtered;
21386
21403
  }
21387
21404
  var FreeCodeGoSubagentModelRouting = class {
@@ -21416,9 +21433,17 @@ var FreeCodeGoSubagentModelRouting = class {
21416
21433
  const llm = this.ctx.get("llm");
21417
21434
  if (settings === void 0 || llm === void 0) return;
21418
21435
  const operation = (async () => {
21436
+ let retries = 0;
21419
21437
  do {
21420
21438
  this.rerun = false;
21421
- await synchronizeSubagentModelRoutes(settings, llm);
21439
+ try {
21440
+ await synchronizeSubagentModelRoutes(settings, llm);
21441
+ retries = 0;
21442
+ } catch {
21443
+ if (retries >= 1 || this.disposed) break;
21444
+ retries += 1;
21445
+ this.rerun = true;
21446
+ }
21422
21447
  } while (this.rerun && !this.disposed);
21423
21448
  })().catch(() => void 0).finally(() => {
21424
21449
  if (this.pending === operation) this.pending = void 0;
@@ -26869,97 +26894,6 @@ var FreeCodeGoVerifyOnStop = class {
26869
26894
  }
26870
26895
  }
26871
26896
  };
26872
- var FreeCodeGoEngineeringSettingsSchema = z2.object({
26873
- engineeringEnabled: z2.boolean().default(false),
26874
- /**
26875
- * The rest of the bundled engineering library is opt-in: 23 audited Skills
26876
- * (the remainder of the authored set plus the vendored `mattpocock/skills`
26877
- * entries that are not in the default-on starter root) stay unmounted until
26878
- * the user turns this on.
26879
- */
26880
- engineeringSkillsEnabled: z2.boolean().default(false),
26881
- /**
26882
- * The starter set is on by default: ten Skills whose value does not depend on
26883
- * adopting a whole methodology. Four are disciplines the model applies on its
26884
- * own (evidence before completion claims, navigation before broad reading,
26885
- * planning multi-file work, root-cause debugging); the rest are `/name`
26886
- * entries a user reaches for on demand and that cost nothing until typed
26887
- * (a prompt-technique reference, the grilling interview and its primitive, a
26888
- * re-pitch escape hatch, a handoff document, and a questionnaire for
26889
- * decisions the agent cannot settle). They live in their own asset root so
26890
- * "all engineering Skills" stays the union of the roots — no skill file is
26891
- * duplicated, and no audit can disagree with a mount.
26892
- */
26893
- engineeringStarterSkillsEnabled: z2.boolean().default(true),
26894
- /**
26895
- * The vendored superpowers workflow pack is a separate opt-in: it is the
26896
- * auto-triggering, plan-then-dispatch methodology, so it must be a deliberate
26897
- * choice rather than a side effect of enabling the engineering disciplines.
26898
- */
26899
- engineeringSuperpowersSkillsEnabled: z2.boolean().default(false),
26900
- /**
26901
- * Injects a bounded capability map of the mounted Skills at session start.
26902
- * A default-off Skill library is invisible otherwise: the model never lists
26903
- * it, and a user who turned a pack on has no way to see what arrived.
26904
- */
26905
- engineeringSkillMapEnabled: z2.boolean().default(true),
26906
- /** Enables bounded project-declared verification after an approved council plan. */
26907
- engineeringQualityEnabled: z2.boolean().default(true),
26908
- engineeringMemoryEnabled: z2.boolean().default(true),
26909
- engineeringCouncilEnabled: z2.boolean().default(true),
26910
- engineeringCouncilDeepseekEnabled: z2.boolean().default(true),
26911
- engineeringCouncilCodexEnabled: z2.boolean().default(true),
26912
- engineeringCouncilClaudeEnabled: z2.boolean().default(true),
26913
- engineeringMemoryContextTokenBudget: z2.number().step(1).min(0).max(4e3).default(1200),
26914
- /**
26915
- * Default off, deliberately: the lexical rerank is free, deterministic, and
26916
- * already ranks by term coverage. Flipping this on spends one small model
26917
- * request per memory search to catch the memories that share no wording with
26918
- * the query — worth it for a user who searches by concept, wasted for one who
26919
- * greps for identifiers.
26920
- */
26921
- engineeringMemorySelectorEnabled: z2.boolean().default(false),
26922
- /**
26923
- * Default off, deliberately: this lets a model approve a pending action without
26924
- * asking the user. Off means every approval keeps prompting. On means an action
26925
- * whose arguments the reviewer can actually read may be cleared automatically —
26926
- * anything it refuses, cannot read, or declines to judge still goes to the user.
26927
- */
26928
- engineeringActionReviewEnabled: z2.boolean().default(false),
26929
- engineeringCodeGraphEnabled: z2.boolean().default(true),
26930
- engineeringCodeGraphAutoUpdate: z2.boolean().default(true),
26931
- engineeringGraphEngine: z2.union([
26932
- z2.const("auto"),
26933
- z2.const("graphify"),
26934
- z2.const("codegraph")
26935
- ]).default("auto"),
26936
- engineeringCouncilMaxRounds: z2.number().step(1).min(1).max(3).default(2),
26937
- engineeringCouncilTimeoutMs: z2.number().step(1).min(1e4).max(3e5).default(12e4),
26938
- engineeringCouncilQuorum: z2.number().step(1).min(1).max(3).default(2),
26939
- engineeringCouncilAutoRun: z2.boolean().default(false),
26940
- /**
26941
- * The three phases of the autonomous engineering loop. Each is a separate
26942
- * switch because they carry different risk: creating a goal only commits the
26943
- * user to finishing, while enabling the driver lets work continue with no
26944
- * human turn in between.
26945
- *
26946
- * - `engineeringLoopCapturePlan` — when the user approves a council decision,
26947
- * persist the approved plan as a durable Harness goal.
26948
- * - `engineeringLoopVerifyOnComplete` — when the active goal's driver reports
26949
- * it finished, run the declared verification stages and record the result.
26950
- * - `engineeringLoopAutoContinue` — let the Harness goal-round driver start
26951
- * the next round without a user turn. Off by default: a goal that continues
26952
- * unattended is the one behaviour a user must opt into explicitly.
26953
- */
26954
- engineeringLoopCapturePlan: z2.boolean().default(true),
26955
- engineeringLoopVerifyOnComplete: z2.boolean().default(true),
26956
- engineeringLoopAutoContinue: z2.boolean().default(false),
26957
- /** Round cap for a captured plan goal; bounds unattended continuation. */
26958
- engineeringLoopMaxGoalRounds: z2.number().step(1).min(1).max(256).default(24),
26959
- engineeringCouncilMaxTokens: z2.number().step(100).min(1200).max(2e4).default(3600),
26960
- engineeringCouncilMaxConcurrent: z2.number().step(1).min(1).max(8).default(2),
26961
- engineeringCouncilDecisionTtlMs: z2.number().step(6e4).min(6e4).max(10080 * 6e4).default(30 * 6e4)
26962
- });
26963
26897
  var GOAL_OBJECTIVE_MAX_CHARS = 8e3;
26964
26898
  var GOAL_EXCERPT_MAX_CHARS = 160;
26965
26899
  function goalPhaseOf(value) {
@@ -28486,10 +28420,7 @@ var FreeCodeGoEngineeringRegistry = class {
28486
28420
  fence.close
28487
28421
  ].join("\n");
28488
28422
  agent.inject(createUserMessage({
28489
- source: {
28490
- kind: "plugin",
28491
- plugin: "freecodego-engineering-memory"
28492
- },
28423
+ source: { kind: "freecodego-engineering-memory" },
28493
28424
  content: [{
28494
28425
  type: "text",
28495
28426
  text: text2
@@ -28526,10 +28457,7 @@ var FreeCodeGoEngineeringRegistry = class {
28526
28457
  if (built === void 0) return;
28527
28458
  this.skillMapBudget = built.metrics;
28528
28459
  agent.inject(createUserMessage({
28529
- source: {
28530
- kind: "plugin",
28531
- plugin: "freecodego-engineering-skills"
28532
- },
28460
+ source: { kind: "freecodego-engineering-skills" },
28533
28461
  content: [{
28534
28462
  type: "text",
28535
28463
  text: built.text
@@ -29899,7 +29827,7 @@ function selectGraphEngine(preference, graphifyInstalled, codeGraphInstalled) {
29899
29827
  }
29900
29828
  function normalizeSettings(value) {
29901
29829
  return {
29902
- engineeringEnabled: value?.engineeringEnabled === true,
29830
+ engineeringEnabled: value?.engineeringEnabled !== false,
29903
29831
  engineeringSkillsEnabled: value?.engineeringSkillsEnabled === true,
29904
29832
  engineeringStarterSkillsEnabled: value?.engineeringStarterSkillsEnabled !== false,
29905
29833
  engineeringSuperpowersSkillsEnabled: value?.engineeringSuperpowersSkillsEnabled === true,
@@ -29929,12 +29857,12 @@ function normalizeSettings(value) {
29929
29857
  engineeringCouncilDecisionTtlMs: boundedInteger$1(value?.engineeringCouncilDecisionTtlMs, 30 * 6e4, 6e4, 10080 * 6e4)
29930
29858
  };
29931
29859
  }
29932
- function pendingRecall(agent, plugin) {
29860
+ function pendingRecall(agent, producer) {
29933
29861
  try {
29934
29862
  const inbox = agent.inbox;
29935
29863
  if (inbox === void 0) return false;
29936
- const fromPlugin = (message2) => isRecord2(message2.source) && message2.source.kind === "plugin" && message2.source.plugin === plugin;
29937
- return (inbox.nextStep ?? []).some(fromPlugin) || (inbox.nextTurn ?? []).some(fromPlugin);
29864
+ const fromProducer = (message2) => isRecord2(message2.source) && message2.source.kind === producer;
29865
+ return (inbox.nextStep ?? []).some(fromProducer) || (inbox.nextTurn ?? []).some(fromProducer);
29938
29866
  } catch {
29939
29867
  return false;
29940
29868
  }
@@ -30955,14 +30883,6 @@ function describeCronExpression(expression) {
30955
30883
  }
30956
30884
  return `cron ${expression.source}`;
30957
30885
  }
30958
- var FreeCodeGoAutomationSettingsSchema = z2.object({
30959
- /** Master switch for declarative failure recovery. */
30960
- hookChainsEnabled: z2.boolean().default(true),
30961
- hookChainsMaxDepth: z2.number().step(1).min(0).max(10).default(2),
30962
- hookChainsCooldownMs: z2.number().step(1e3).min(0).max(1440 * 60 * 1e3).default(3e4),
30963
- /** Master switch for the calendar planner. */
30964
- scheduledTasksEnabled: z2.boolean().default(true)
30965
- });
30966
30886
  var HOOK_CHAINS_RELATIVE_PATH = join4(".freecodego", "hook-chains.json");
30967
30887
  function outcomeOfTurnEnd(reason) {
30968
30888
  switch (typeof reason === "object" && reason !== null ? reason.kind : void 0) {
@@ -31458,7 +31378,21 @@ function setEngineAvailability(agentEngines, id, availability) {
31458
31378
  } catch {
31459
31379
  }
31460
31380
  }
31381
+ var MODELS_SETTINGS_ENTRY = "llm-pi-ai";
31382
+ function peerSettings(ctx, entryId) {
31383
+ const settings = ctx.get("settings");
31384
+ if (typeof settings?.describe !== "function") return void 0;
31385
+ try {
31386
+ const descriptor = settings.describe().find((row) => row.ns === entryId);
31387
+ return descriptor === void 0 ? void 0 : maybeRecord(descriptor.value);
31388
+ } catch {
31389
+ return;
31390
+ }
31391
+ }
31461
31392
  var DONE = "[DONE]";
31393
+ function unsupported(what) {
31394
+ throw new LlmError(`${what} cannot be serialized on this wire`, "UNSUPPORTED_CONTENT");
31395
+ }
31462
31396
  async function* parseSse(stream) {
31463
31397
  const events = stream.pipeThrough(new TextDecoderStream()).pipeThrough(new EventSourceParserStream());
31464
31398
  for await (const event of events) {
@@ -31561,8 +31495,8 @@ function serializeRequest(options, defaults = {}) {
31561
31495
  content: options.system
31562
31496
  });
31563
31497
  for (const message2 of options.messages) {
31564
- message2.content;
31565
31498
  if (message2.role === "system") {
31499
+ message2.content;
31566
31500
  messages.push({
31567
31501
  role: "system",
31568
31502
  content: text(message2.content)
@@ -31573,16 +31507,19 @@ function serializeRequest(options, defaults = {}) {
31573
31507
  messages.push(serializeAssistant(message2));
31574
31508
  continue;
31575
31509
  }
31576
- const results = message2.content.filter((block) => block.type === "tool-result");
31577
- const content = text(message2.content.filter((block) => block.type !== "tool-result"));
31578
- for (const result of results) messages.push({
31579
- role: "tool",
31580
- tool_call_id: result.toolCallId,
31581
- content: text(result.content) || "(no output)"
31582
- });
31583
- if (content !== "" || results.length === 0) messages.push({
31510
+ if (message2.role === "tool") {
31511
+ messages.push({
31512
+ role: "tool",
31513
+ tool_call_id: message2.toolCallId,
31514
+ content: text(message2.content) || "(no output)"
31515
+ });
31516
+ continue;
31517
+ }
31518
+ if (message2.role === "developer") unsupported("developer message");
31519
+ message2.content;
31520
+ messages.push({
31584
31521
  role: "user",
31585
- content
31522
+ content: text(message2.content)
31586
31523
  });
31587
31524
  }
31588
31525
  return requestWithMessages(options, messages, defaults);
@@ -31607,14 +31544,17 @@ async function serializeRequestWithInlineImages(options, images, defaults = {})
31607
31544
  messages.push(serializeAssistant(message2));
31608
31545
  continue;
31609
31546
  }
31610
- const results = message2.content.filter((block) => block.type === "tool-result");
31611
- const content = await inlineUserContent(message2.content.filter((block) => block.type !== "tool-result"), images);
31612
- for (const result of results) messages.push({
31613
- role: "tool",
31614
- tool_call_id: result.toolCallId,
31615
- content: text(result.content) || "(no output)"
31616
- });
31617
- if (content.length > 0 || results.length === 0) messages.push({
31547
+ if (message2.role === "tool") {
31548
+ messages.push({
31549
+ role: "tool",
31550
+ tool_call_id: message2.toolCallId,
31551
+ content: text(message2.content) || "(no output)"
31552
+ });
31553
+ continue;
31554
+ }
31555
+ if (message2.role === "developer") unsupported("developer message");
31556
+ const content = await inlineUserContent(message2.content, images);
31557
+ messages.push({
31618
31558
  role: "user",
31619
31559
  content: inlineUserWireContent(content)
31620
31560
  });
@@ -31870,13 +31810,6 @@ async function anthropicContent(message2, images) {
31870
31810
  case "tool-call":
31871
31811
  if (message2.role === "assistant") blocks.push(anthropicToolUse(block));
31872
31812
  break;
31873
- case "tool-result":
31874
- blocks.push({
31875
- type: "tool_result",
31876
- tool_use_id: block.toolCallId,
31877
- content: textOf$2(block.content) || "(no output)"
31878
- });
31879
- break;
31880
31813
  default:
31881
31814
  break;
31882
31815
  }
@@ -31886,19 +31819,38 @@ async function anthropicTurns(options, images) {
31886
31819
  const systems = [];
31887
31820
  if (options.system !== void 0 && options.system !== "") systems.push(options.system);
31888
31821
  const messages = [];
31822
+ const results = [];
31823
+ const flushResults = () => {
31824
+ if (results.length === 0) return;
31825
+ messages.push({
31826
+ role: "user",
31827
+ content: results.splice(0)
31828
+ });
31829
+ };
31889
31830
  for (const message2 of options.messages) {
31890
31831
  if (message2.role === "system") {
31891
31832
  const text2 = textOf$2(message2.content);
31892
31833
  if (text2 !== "") systems.push(text2);
31893
31834
  continue;
31894
31835
  }
31836
+ if (message2.role === "developer") unsupported("developer message");
31837
+ if (message2.role === "tool") {
31838
+ results.push({
31839
+ type: "tool_result",
31840
+ tool_use_id: message2.toolCallId,
31841
+ content: textOf$2(message2.content) || "(no output)"
31842
+ });
31843
+ continue;
31844
+ }
31895
31845
  const content = await anthropicContent(message2, images);
31896
31846
  if (content.length === 0) continue;
31847
+ flushResults();
31897
31848
  messages.push({
31898
31849
  role: message2.role === "assistant" ? "assistant" : "user",
31899
31850
  content
31900
31851
  });
31901
31852
  }
31853
+ flushResults();
31902
31854
  return {
31903
31855
  system: systems.length === 0 ? void 0 : systems.join("\n\n"),
31904
31856
  messages
@@ -32234,7 +32186,7 @@ var OpenAiCompatibleAdapter = class extends LlmAdapter {
32234
32186
  const detail = redactProviderDetail(await response.text().catch(() => ""));
32235
32187
  if (response.status === 400) onBadRequest?.(detail);
32236
32188
  if (response.status === 429) this.config.onRateLimited?.(this.config.providerName, response.status);
32237
- const rateHint = response.status === 429 ? this.config.rateLimitedHint?.(this.config.providerName) : void 0;
32189
+ const rateHint = this.config.rateLimitedHint?.(this.config.providerName, response.status, detail);
32238
32190
  throw new LlmError(`${this.config.providerName} provider request failed (HTTP ${response.status})${detail === "" ? "" : `: ${detail}`}${rateHint === void 0 ? "" : `
32239
32191
  ${rateHint}`}`, llmCodeForUpstreamStatus(response.status), { status: response.status });
32240
32192
  }
@@ -33347,21 +33299,29 @@ function audioMimeType(file) {
33347
33299
  const extension = path.extname(file).toLowerCase();
33348
33300
  return extension === ".webm" ? "audio/webm" : extension === ".ogg" || extension === ".opus" ? "audio/ogg" : extension === ".wav" ? "audio/wav" : extension === ".m4a" || extension === ".mp4" ? "audio/mp4" : extension === ".flac" ? "audio/flac" : "audio/mpeg";
33349
33301
  }
33350
- function registerMediaTools(host) {
33302
+ var MEDIA_GENERATION_TOOL_NAMES = {
33303
+ image: "freecodego_generate_image",
33304
+ video: "freecodego_generate_video"
33305
+ };
33306
+ var NO_MEDIA_TOOLS = {
33307
+ names: [],
33308
+ dispose: () => void 0
33309
+ };
33310
+ var GENERIC_MEDIA_OUTPUT = {
33311
+ schema: {
33312
+ type: "object",
33313
+ additionalProperties: true
33314
+ },
33315
+ render: (_args, value) => [{
33316
+ type: "text",
33317
+ text: JSON.stringify(value)
33318
+ }]
33319
+ };
33320
+ function registerGenerationTools(host) {
33351
33321
  const tools = host.ctx.get("tools");
33352
- if (tools === void 0) return;
33353
- const genericOutput = {
33354
- schema: {
33355
- type: "object",
33356
- additionalProperties: true
33357
- },
33358
- render: (_args, value) => [{
33359
- type: "text",
33360
- text: JSON.stringify(value)
33361
- }]
33362
- };
33363
- const disposeImage = tools.register(toolDefinition({
33364
- name: "freecodego_generate_image",
33322
+ if (tools === void 0) return NO_MEDIA_TOOLS;
33323
+ const imageTool = toolDefinition({
33324
+ name: MEDIA_GENERATION_TOOL_NAMES.image,
33365
33325
  description: "Generate an image with the user-selected default media model. Do not pass a model id. Pass images to edit or fuse existing pictures on the providers that accept sources. If that provider is unavailable, the Host safely tries another configured image model.",
33366
33326
  parameters: {
33367
33327
  type: "object",
@@ -33389,7 +33349,7 @@ function registerMediaTools(host) {
33389
33349
  additionalProperties: false
33390
33350
  },
33391
33351
  output: {
33392
- schema: genericOutput.schema,
33352
+ schema: GENERIC_MEDIA_OUTPUT.schema,
33393
33353
  render: (_args, value) => renderGeneratedImages(value)
33394
33354
  },
33395
33355
  execute: async (args, exec) => {
@@ -33399,9 +33359,9 @@ function registerMediaTools(host) {
33399
33359
  card: "generic",
33400
33360
  title: `Generate image: ${args.prompt}`
33401
33361
  })
33402
- }));
33403
- const disposeVideo = tools.register(toolDefinition({
33404
- name: "freecodego_generate_video",
33362
+ });
33363
+ const videoTool = toolDefinition({
33364
+ name: MEDIA_GENERATION_TOOL_NAMES.video,
33405
33365
  description: "Generate a video with the user-selected default media model. Do not pass a model id. If that provider is unavailable before task creation, the Host safely tries another configured video model.",
33406
33366
  parameters: {
33407
33367
  type: "object",
@@ -33440,7 +33400,7 @@ function registerMediaTools(host) {
33440
33400
  required: ["prompt"],
33441
33401
  additionalProperties: false
33442
33402
  },
33443
- output: genericOutput,
33403
+ output: GENERIC_MEDIA_OUTPUT,
33444
33404
  execute: async (args, exec) => {
33445
33405
  return host.generateVideoWithFallback(args, exec.signal);
33446
33406
  },
@@ -33448,8 +33408,21 @@ function registerMediaTools(host) {
33448
33408
  card: "generic",
33449
33409
  title: `Generate video: ${args.prompt}`
33450
33410
  })
33451
- }));
33452
- const disposeAudio = tools.register(toolDefinition({
33411
+ });
33412
+ const disposeImage = tools.register(imageTool);
33413
+ const disposeVideo = tools.register(videoTool);
33414
+ return {
33415
+ names: [imageTool.name, videoTool.name],
33416
+ dispose: () => {
33417
+ disposeImage();
33418
+ disposeVideo();
33419
+ }
33420
+ };
33421
+ }
33422
+ function registerAudioTools(host) {
33423
+ const tools = host.ctx.get("tools");
33424
+ if (tools === void 0) return NO_MEDIA_TOOLS;
33425
+ const audioTool = toolDefinition({
33453
33426
  name: "freecodego_generate_audio",
33454
33427
  description: "Generate speech/audio with the user-selected default media model. Do not pass a model id. The Host can fall back to another configured audio model when the selected provider is unavailable.",
33455
33428
  parameters: {
@@ -33474,7 +33447,7 @@ function registerMediaTools(host) {
33474
33447
  required: ["input"],
33475
33448
  additionalProperties: false
33476
33449
  },
33477
- output: genericOutput,
33450
+ output: GENERIC_MEDIA_OUTPUT,
33478
33451
  execute: async (args, exec) => {
33479
33452
  return host.generateAudioWithFallback(args, exec.agent?.session.header.cwd, exec.signal);
33480
33453
  },
@@ -33482,8 +33455,8 @@ function registerMediaTools(host) {
33482
33455
  card: "generic",
33483
33456
  title: "Generate audio with FreeCodeGo"
33484
33457
  })
33485
- }));
33486
- const disposeTranscribe = tools.register(toolDefinition({
33458
+ });
33459
+ const transcribeTool = toolDefinition({
33487
33460
  name: "freecodego_transcribe_audio",
33488
33461
  description: "Transcribe an audio file with the fixed free Groq whisper-large-v3-turbo model. The file must be inside the active workspace.",
33489
33462
  parameters: {
@@ -33499,7 +33472,7 @@ function registerMediaTools(host) {
33499
33472
  required: ["path"],
33500
33473
  additionalProperties: false
33501
33474
  },
33502
- output: genericOutput,
33475
+ output: GENERIC_MEDIA_OUTPUT,
33503
33476
  execute: async (args, exec) => {
33504
33477
  const cwd = exec.agent?.session.header.cwd;
33505
33478
  if (cwd === void 0) throw new Error("Audio transcription requires an active workspace");
@@ -33531,13 +33504,16 @@ function registerMediaTools(host) {
33531
33504
  card: "generic",
33532
33505
  title: `Transcribe audio: ${args.path}`
33533
33506
  })
33534
- }));
33535
- host.ctx.effect(() => () => {
33536
- disposeImage();
33537
- disposeVideo();
33538
- disposeAudio();
33539
- disposeTranscribe();
33540
- }, "freecodego: default media tools");
33507
+ });
33508
+ const disposeAudio = tools.register(audioTool);
33509
+ const disposeTranscribe = tools.register(transcribeTool);
33510
+ return {
33511
+ names: [audioTool.name, transcribeTool.name],
33512
+ dispose: () => {
33513
+ disposeAudio();
33514
+ disposeTranscribe();
33515
+ }
33516
+ };
33541
33517
  }
33542
33518
  async function generateImageWithFallback(host, args, signal) {
33543
33519
  return withMediaFallback(host, "image", signal, async (route) => {
@@ -33890,7 +33866,7 @@ function mediaRoute(host, selection) {
33890
33866
  };
33891
33867
  }
33892
33868
  async function configuredMediaConnection(host, route) {
33893
- const profile = asRecord(asRecord(asRecord(host.ctx.get("settings")?.get("llm-pi-ai")).providers)[route.provider]);
33869
+ const profile = asRecord(asRecord(asRecord(peerSettings(host.ctx, MODELS_SETTINGS_ENTRY)).providers)[route.provider]);
33894
33870
  const baseURL = asString(profile.baseURL) ?? defaultMediaBaseURL(route.provider);
33895
33871
  if (baseURL === void 0) throw new Error(`Media provider "${route.provider}" needs a Base URL in the Harness Models settings`);
33896
33872
  const reference = asString(profile.apiKeyEnv) ?? defaultMediaCredentialRef(route.provider);
@@ -34464,12 +34440,19 @@ function registerAdvisorTools(deps) {
34464
34440
  disposeTeamImplementation();
34465
34441
  }, "freecodego: Advisor and engineering council tools");
34466
34442
  }
34467
- function registerAgnesTools(deps) {
34443
+ var AGNES_MEDIA_TOOL_NAMES = {
34444
+ image: "agnes_generate_image",
34445
+ video: "agnes_generate_video"
34446
+ };
34447
+ function registerAgnesMediaTools(deps) {
34468
34448
  const client = deps.agnes;
34469
34449
  const tools = deps.ctx.get("tools");
34470
- if (client === void 0 || tools === void 0) return;
34450
+ if (client === void 0 || tools === void 0) return {
34451
+ names: [],
34452
+ dispose: () => void 0
34453
+ };
34471
34454
  const disposeImage = tools.register(toolDefinition({
34472
- name: "agnes_generate_image",
34455
+ name: AGNES_MEDIA_TOOL_NAMES.image,
34473
34456
  description: "Legacy image-generation tool name. Generate an image with the FreeCodeGo Settings image default; do not assume Agnes is selected. The Host routes this request through the configured default image model and falls back safely when unavailable.",
34474
34457
  parameters: {
34475
34458
  type: "object",
@@ -34505,7 +34488,7 @@ function registerAgnesTools(deps) {
34505
34488
  })
34506
34489
  }));
34507
34490
  const disposeVideo = tools.register(toolDefinition({
34508
- name: "agnes_generate_video",
34491
+ name: AGNES_MEDIA_TOOL_NAMES.video,
34509
34492
  description: "Legacy video-generation tool name. Generate a video with the FreeCodeGo Settings video default; do not assume Agnes is selected. The Host routes this request through the configured default video model and falls back safely when unavailable.",
34510
34493
  parameters: {
34511
34494
  type: "object",
@@ -34556,10 +34539,13 @@ function registerAgnesTools(deps) {
34556
34539
  title: `Generate video: ${args.prompt}`
34557
34540
  })
34558
34541
  }));
34559
- deps.ctx.effect(() => () => {
34560
- disposeImage();
34561
- disposeVideo();
34562
- }, "freecodego: Agnes media tools");
34542
+ return {
34543
+ names: [AGNES_MEDIA_TOOL_NAMES.image, AGNES_MEDIA_TOOL_NAMES.video],
34544
+ dispose: () => {
34545
+ disposeImage();
34546
+ disposeVideo();
34547
+ }
34548
+ };
34563
34549
  }
34564
34550
  var COMMUNITY_ASSET_HOSTS = /* @__PURE__ */ new Set([
34565
34551
  "raw.githubusercontent.com",
@@ -36721,6 +36707,102 @@ async function ensureFreeCodeGoAgentPreset() {
36721
36707
  return false;
36722
36708
  }
36723
36709
  }
36710
+ async function loaderSettled(ctx) {
36711
+ const loader = ctx?.root?.loader;
36712
+ if (typeof loader?.await !== "function") return;
36713
+ try {
36714
+ await loader.await.call(loader);
36715
+ } catch {
36716
+ }
36717
+ }
36718
+ function findUpstreamAgentPresets(ctx) {
36719
+ const get = ctx?.get;
36720
+ if (typeof get !== "function") return void 0;
36721
+ let candidate;
36722
+ try {
36723
+ candidate = get.call(ctx, "agentPresets");
36724
+ } catch {
36725
+ return;
36726
+ }
36727
+ const view = candidate;
36728
+ return typeof view?.list === "function" && typeof view?.register === "function" ? view : void 0;
36729
+ }
36730
+ var JS_EXPRESSION_TAG = "tag:yaml.org,2002:js";
36731
+ var PLATFORM_EXPRESSION = /^process\.platform\s*(===|!==)\s*'([^']+)'$/;
36732
+ var presetSchema = yaml.DEFAULT_SCHEMA.extend([new yaml.Type(JS_EXPRESSION_TAG, {
36733
+ kind: "scalar",
36734
+ construct: (source) => {
36735
+ const match = PLATFORM_EXPRESSION.exec(source.trim());
36736
+ if (match === null) throw new Error(`unsupported !!js expression in a bundled preset: ${source.trim()}`);
36737
+ return match[1] === "===" ? process.platform === match[2] : process.platform !== match[2];
36738
+ }
36739
+ })]);
36740
+ async function readBundledAgentPreset(presetId) {
36741
+ const source = presetSourceDirectory(presetId);
36742
+ if (source === void 0) return void 0;
36743
+ const metadata = maybeRecord(yaml.load(await readFile3(join4(source, "preset.yml"), "utf8"), { schema: presetSchema }));
36744
+ const loaded2 = yaml.load(await readFile3(join4(source, "agent.cordis.yml"), "utf8"), { schema: presetSchema });
36745
+ if (!Array.isArray(loaded2)) return void 0;
36746
+ const plugins = [];
36747
+ for (const row of loaded2) {
36748
+ const record3 = maybeRecord(row);
36749
+ if (record3 === void 0) return void 0;
36750
+ plugins.push(record3);
36751
+ }
36752
+ const name2 = metadata?.name;
36753
+ const description = metadata?.description;
36754
+ const order = metadata?.order;
36755
+ return {
36756
+ id: presetId,
36757
+ ...typeof name2 === "string" && name2 !== "" ? { name: name2 } : {},
36758
+ ...typeof description === "string" && description !== "" ? { description } : {},
36759
+ ...typeof order === "number" && Number.isFinite(order) ? { order } : {},
36760
+ plugins
36761
+ };
36762
+ }
36763
+ function installFreeCodeGoAgentPresets(ctx) {
36764
+ const disposers = [];
36765
+ let released = false;
36766
+ const keep = (unregister) => {
36767
+ if (released) {
36768
+ Promise.resolve(unregister()).catch(() => {
36769
+ });
36770
+ return;
36771
+ }
36772
+ disposers.push(unregister);
36773
+ };
36774
+ return {
36775
+ done: (async () => {
36776
+ await loaderSettled(ctx);
36777
+ const registry = findUpstreamAgentPresets(ctx);
36778
+ if (registry === void 0) {
36779
+ await ensureFreeCodeGoAgentPreset();
36780
+ return;
36781
+ }
36782
+ let declared;
36783
+ try {
36784
+ declared = new Set((await registry.list()).map((row) => row.id));
36785
+ } catch {
36786
+ declared = /* @__PURE__ */ new Set();
36787
+ }
36788
+ for (const presetId of BUNDLED_PRESET_IDS) {
36789
+ if (declared.has(presetId)) continue;
36790
+ const definition = await readBundledAgentPreset(presetId);
36791
+ if (definition === void 0) continue;
36792
+ try {
36793
+ keep(await registry.register(definition));
36794
+ } catch {
36795
+ }
36796
+ }
36797
+ })().catch(() => {
36798
+ }),
36799
+ release: () => {
36800
+ released = true;
36801
+ for (const unregister of disposers.splice(0)) Promise.resolve(unregister()).catch(() => {
36802
+ });
36803
+ }
36804
+ };
36805
+ }
36724
36806
  var DEFAULT_CAPACITY = 1e3;
36725
36807
  var DEFAULT_TTL_MS = 30 * 6e4;
36726
36808
  function computeKey(payload) {
@@ -42612,17 +42694,6 @@ Fix these and call the tool again; no child agent was started.`;
42612
42694
  var run$1 = promisify(execFile);
42613
42695
  var GIT_TIMEOUT_MS$1 = 1e4;
42614
42696
  var MAX_GIT_OUTPUT$1 = 4096;
42615
- var FreeCodeGoTrustSettingsSchema = z2.object({
42616
- /**
42617
- * Master switch for the whole gate.
42618
- *
42619
- * Off means every project-scoped surface is treated as trusted — the
42620
- * pre-gate behaviour, kept reachable so a deployment that already controls
42621
- * which repositories it opens is not forced to grant each one. It is
42622
- * deliberately not the default: the safe state must not be the opt-in one.
42623
- */
42624
- folderTrustEnabled: z2.boolean().default(true)
42625
- });
42626
42697
  var FOLDER_TRUST_ENV = "FREECODEGO_FOLDER_TRUST";
42627
42698
  function folderTrustEnabled(settings) {
42628
42699
  const raw = process.env[FOLDER_TRUST_ENV]?.trim().toLowerCase();
@@ -43359,10 +43430,7 @@ var AssistantLoopGuard = class {
43359
43430
  injectReminder(agent, finding) {
43360
43431
  try {
43361
43432
  agent.inject(createUserMessage({
43362
- source: {
43363
- kind: "plugin",
43364
- plugin: "freecodego-assistant-loop-guard"
43365
- },
43433
+ source: { kind: "freecodego-assistant-loop-guard" },
43366
43434
  content: [{
43367
43435
  type: "text",
43368
43436
  text: renderAssistantLoopReminder(finding)
@@ -44058,10 +44126,8 @@ function isHarnessPruned(message2) {
44058
44126
  return text2 !== void 0 && text2.includes("[... tool result middle pruned ...]");
44059
44127
  }
44060
44128
  function hasOnlyTextContent(message2) {
44061
- if (message2.role !== "user" || !Array.isArray(message2.content)) return false;
44062
- const outer = message2.content[0];
44063
- if (outer?.type !== "tool-result" || !Array.isArray(outer.content)) return false;
44064
- const blocks = outer.content;
44129
+ if (message2.role !== "tool" || !Array.isArray(message2.content)) return false;
44130
+ const blocks = message2.content;
44065
44131
  if (blocks.length === 0) return false;
44066
44132
  return blocks.every((block) => block?.type === "text");
44067
44133
  }
@@ -44072,17 +44138,13 @@ function isClearableResult(message2, tool2, clearable, clearedMarker) {
44072
44138
  return hasOnlyTextContent(message2);
44073
44139
  }
44074
44140
  function resultText(message2) {
44075
- if (message2.role !== "user" || !Array.isArray(message2.content)) return void 0;
44076
- const outer = message2.content[0];
44077
- if (outer?.type !== "tool-result" || !Array.isArray(outer.content)) return void 0;
44141
+ if (message2.role !== "tool" || !Array.isArray(message2.content)) return void 0;
44078
44142
  const texts = [];
44079
- for (const block of outer.content) if (block?.type === "text" && typeof block.text === "string") texts.push(block.text);
44143
+ for (const block of message2.content) if (block?.type === "text" && typeof block.text === "string") texts.push(block.text);
44080
44144
  return texts.join("\n");
44081
44145
  }
44082
44146
  function resultCallId(message2) {
44083
- if (message2.role !== "user" || !Array.isArray(message2.content)) return void 0;
44084
- const first = message2.content[0];
44085
- return first?.type === "tool-result" && typeof first.toolCallId === "string" ? first.toolCallId : void 0;
44147
+ return message2.role === "tool" && typeof message2.toolCallId === "string" ? message2.toolCallId : void 0;
44086
44148
  }
44087
44149
  function clearOldToolResults(messages, options = {}) {
44088
44150
  const clearable = options.clearableTools ?? CLEARABLE_TOOL_KINDS;
@@ -44133,12 +44195,8 @@ function clearOldToolResults(messages, options = {}) {
44133
44195
  return {
44134
44196
  ...message2,
44135
44197
  content: [{
44136
- type: "tool-result",
44137
- toolCallId: callId3,
44138
- content: [{
44139
- type: "text",
44140
- text: replacement
44141
- }]
44198
+ type: "text",
44199
+ text: replacement
44142
44200
  }]
44143
44201
  };
44144
44202
  }),
@@ -45093,7 +45151,7 @@ function messageText$1(content, depth = 4) {
45093
45151
  }
45094
45152
  function isSummaryEvent(data) {
45095
45153
  const message2 = data.message;
45096
- if (message2?.source?.kind === "compaction") return true;
45154
+ if (message2?.source?.kind === "compact-checkpoint") return true;
45097
45155
  const text2 = messageText$1(message2?.content);
45098
45156
  return text2.startsWith("<summary>") || text2.includes("<compacted-conversation");
45099
45157
  }
@@ -45416,10 +45474,7 @@ function installRehydration(ctx, deps) {
45416
45474
  const agent = ctx.agents?.get?.(String(session.id));
45417
45475
  if (agent === void 0 || typeof agent.inject !== "function") return;
45418
45476
  agent.inject(createUserMessage({
45419
- source: {
45420
- kind: "plugin",
45421
- plugin: "freecodego-rehydration"
45422
- },
45477
+ source: { kind: "freecodego-rehydration" },
45423
45478
  content: [{
45424
45479
  type: "text",
45425
45480
  text: body
@@ -45446,10 +45501,7 @@ function createMemorySelector(options) {
45446
45501
  provider: options.route.provider,
45447
45502
  model: options.route.model,
45448
45503
  messages: [createUserMessage({
45449
- source: {
45450
- kind: "plugin",
45451
- plugin: "freecodego-memory-recall"
45452
- },
45504
+ source: { kind: "freecodego-memory-recall" },
45453
45505
  content: [{
45454
45506
  type: "text",
45455
45507
  text: selectorPrompt(query2, excerpts, limit)
@@ -45823,36 +45875,40 @@ function sortEntries(entries) {
45823
45875
  return [...entries].sort((left, right) => left.name.localeCompare(right.name));
45824
45876
  }
45825
45877
  var FreeCodeGoPolicy = class {
45826
- settings;
45878
+ config;
45879
+ writer;
45827
45880
  /**
45828
- * @param settings - the registered namespace scope, or `undefined` when the
45829
- * composition has no settings service (headless SDK trees, tests). The
45830
- * policy is always constructible so consumers never branch on its presence;
45831
- * they branch on {@link configured} instead.
45881
+ * @param config - the plugin's resolved configuration, or `undefined` when the
45882
+ * composition supplies none (tests, a bare context). The policy is always
45883
+ * constructible so consumers never branch on its presence; they branch on
45884
+ * {@link configured} instead.
45885
+ * @param writer - the persistence step, absent when no settings service is mounted.
45832
45886
  */
45833
- constructor(settings) {
45834
- this.settings = settings;
45887
+ constructor(config, writer = void 0) {
45888
+ this.config = config;
45889
+ this.writer = writer;
45835
45890
  }
45836
- /** Whether a settings service was available when the plugin was built. */
45891
+ /** Whether a configuration was available when the plugin was built. */
45837
45892
  get configured() {
45838
- return this.settings !== void 0;
45893
+ return this.config !== void 0;
45839
45894
  }
45840
45895
  /**
45841
45896
  * The settings a behaviour read must use.
45842
45897
  *
45843
- * The only read of the registered scope in this package, which is what makes it
45844
- * the place a second configuration layer would have to be applied — see the
45845
- * module doc.
45846
- * @returns the resolved document, or `undefined` when no settings service exists.
45898
+ * The only read of the Config in this package, which is what makes it the place a
45899
+ * second configuration layer would have to be applied — see the module doc.
45900
+ * @returns the resolved document, or `undefined` when no configuration exists.
45847
45901
  */
45848
45902
  get() {
45849
- return this.settings?.get();
45903
+ return this.config === void 0 ? void 0 : resolveSettings(this.config);
45850
45904
  }
45851
45905
  /**
45852
45906
  * Apply a user-originated change.
45853
45907
  *
45854
- * Writes go to the registered scope, which is the only layer there is: nothing
45855
- * a user gesture can reach edits a document other than their own.
45908
+ * The write goes to this plugin's own profile entry, through the settings service that
45909
+ * owns validation, revisions, and the document write. A composition without that
45910
+ * service drops the patch rather than pretending to have stored it: the caller is a
45911
+ * user gesture whose failure the Web client reports from the RPC that carried it.
45856
45912
  * @param patch - the fields to merge into the settings document. Typed as the
45857
45913
  * harness types it (`object`) rather than as a `Partial` of the document,
45858
45914
  * because a patch is a *merge*: the caller that owns one settings domain
@@ -45860,9 +45916,17 @@ var FreeCodeGoPolicy = class {
45860
45916
  * force every caller to widen a patch it knows is already valid.
45861
45917
  */
45862
45918
  async update(patch) {
45863
- await this.settings?.update(patch);
45919
+ await this.writer?.update(patch);
45864
45920
  }
45865
45921
  };
45922
+ function resolveSettings(config) {
45923
+ const document = {};
45924
+ for (const [key, entry] of Object.entries(config)) {
45925
+ if (!isVolatile(entry)) continue;
45926
+ document[key] = entry.get();
45927
+ }
45928
+ return document;
45929
+ }
45866
45930
  var ROLLOUT_STAGES = [
45867
45931
  "off",
45868
45932
  "record_only",
@@ -46446,10 +46510,7 @@ function createMemoryDreamPlanner(options) {
46446
46510
  provider: options.route.provider,
46447
46511
  model: options.route.model,
46448
46512
  messages: [createUserMessage({
46449
- source: {
46450
- kind: "plugin",
46451
- plugin: "freecodego-memory-dream"
46452
- },
46513
+ source: { kind: "freecodego-memory-dream" },
46453
46514
  content: [{
46454
46515
  type: "text",
46455
46516
  text: dreamPrompt(request)
@@ -46706,10 +46767,7 @@ function createActionReviewer(input) {
46706
46767
  provider: input.route.provider,
46707
46768
  model: input.route.model,
46708
46769
  messages: [createUserMessage({
46709
- source: {
46710
- kind: "plugin",
46711
- plugin: "freecodego-action-review"
46712
- },
46770
+ source: { kind: "freecodego-action-review" },
46713
46771
  content: [{
46714
46772
  type: "text",
46715
46773
  text: reviewPrompt(composed, action)
@@ -46802,10 +46860,6 @@ function messageText(message2) {
46802
46860
  const block = item;
46803
46861
  if (block.type === "text" || block.type === "reasoning") return block.text ?? "";
46804
46862
  if (block.type === "tool-call") return `${block.name ?? ""}(${block.arguments ?? ""})`;
46805
- if (block.type === "tool-result") return (block.content ?? []).map((part) => {
46806
- const item2 = part;
46807
- return item2.type === "text" ? item2.text ?? "" : `[${item2.type ?? "unknown"}]`;
46808
- }).join("\n");
46809
46863
  return block.type === void 0 ? "" : `[${block.type}]`;
46810
46864
  }).join("\n");
46811
46865
  }
@@ -52093,7 +52147,12 @@ function imageInputModalities(id, name2) {
52093
52147
  return /(?:\bgpt[\s_.-]?(?:4o|4\.1|4\.5|5)\b|\bgemini\b|\bclaude\b|\bgrok\b|(?:[-_.]|\b)(?:vision|vl|4v)(?:[-_.]|\b))/iu.test(value) ? ["text", "image"] : ["text"];
52094
52148
  }
52095
52149
  var RATE_LIMIT_PROXY_HINT = "\u5F53\u524D IP \u53EF\u80FD\u88AB\u4E0A\u6E38\u9650\u6D41\uFF1A\u8BF7\u5C1D\u8BD5\u5173\u95ED\u672C\u5730/\u5168\u5C40\u4EE3\u7406\u540E\u91CD\u8BD5\u3002\nYour current IP may be rate-limited by the upstream: try disabling your local/global proxy and retry.";
52096
- var KILO_RATE_LIMIT_HINT = "Kilo \u514D\u8D39\u901A\u9053\u6309\u51FA\u53E3 IP \u9650\u6D41\uFF08\u5B98\u65B9\u914D\u989D\uFF1A\u6BCF\u5C0F\u65F6 200 \u6B21\uFF0C\u767B\u5F55\u4E0E\u5426\u90FD\u4E00\u6837\uFF09\uFF0C\u5F53\u524D\u7F51\u7EDC\u53EF\u80FD\u5DF2\u7528\u5B8C\uFF1A\u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u66F4\u6362\u7F51\u7EDC\u51FA\u53E3\u3002\nKilo meters its free routes per egress IP (200 requests/hour, signed in or not) and this network may be out of budget: retry later, or use a different network egress.";
52150
+ var KILO_RATE_LIMIT_HINT = "Kilo \u514D\u8D39\u901A\u9053\u6309\u51FA\u53E3 IP \u9650\u6D41\uFF08\u5B98\u65B9\u914D\u989D\uFF1A\u6BCF\u5C0F\u65F6 200 \u6B21\uFF0C\u767B\u5F55\u4E0E\u5426\u90FD\u4E00\u6837\uFF09\uFF0C\u5F53\u524D\u7F51\u7EDC\u53EF\u80FD\u5DF2\u7528\u5B8C\uFF1A\u8BF7\u66F4\u6362\u8282\u70B9 IP \u540E\u91CD\u8BD5\uFF0C\u6216\u5173\u95ED\u4EE3\u7406\u3001\u7528\u771F\u5B9E IP \u8BBF\u95EE\u3002\nKilo meters its free routes per egress IP (200 requests/hour, signed in or not) and this network may be out of budget: switch to a different node IP, or disable your proxy and retry from your real IP.";
52151
+ var OPENCODE_FREE_TIER_HINT = 'OpenCode \u514D\u8D39\u989D\u5EA6\u6309\u51FA\u53E3 IP \u5224\u5B9A\uFF1A\u5F53\u524D\u8282\u70B9 IP \u88AB\u4E0A\u6E38\u5224\u4E3A\u975E\u5B98\u65B9\u5BA2\u6237\u7AEF\uFF08\u4E0A\u6E38\u539F\u6587\uFF1Afree tier can only be used from within OpenCode\uFF09\uFF0C\u4E0D\u662F\u6A21\u578B\u6216\u5BC6\u94A5\u7684\u95EE\u9898\u3002\u8BF7\u66F4\u6362\u8282\u70B9 IP \u540E\u91CD\u8BD5\uFF0C\u6216\u5173\u95ED\u4EE3\u7406\u3001\u7528\u771F\u5B9E IP \u8BBF\u95EE\u3002\nOpenCode decides its free tier by egress IP: this node address is not accepted as its own client (upstream: "free tier can only be used from within OpenCode"), so neither the model nor the key is the problem. Switch to a different node IP, or disable your proxy and retry from your real IP.';
52152
+ function isOpenCodeFreeTierRefusal(status, detail) {
52153
+ if (status !== 403) return false;
52154
+ return /FreeTierError|free tier can only be used/iu.test(detail);
52155
+ }
52097
52156
  var FREE_UPSTREAM_MODELS = [
52098
52157
  {
52099
52158
  id: "mimo-v2.5",
@@ -52497,7 +52556,10 @@ var FreeCodeGoManagedCatalogs = class {
52497
52556
  normalizeReasoningEffort: (effort) => isDirectReasoningEffort(effort) ? effort : void 0,
52498
52557
  defaultReasoningEffort: "off",
52499
52558
  reasoningEffortsForModel: () => DIRECT_REASONING_EFFORTS,
52500
- rateLimitedHint: () => RATE_LIMIT_PROXY_HINT,
52559
+ rateLimitedHint: (_provider, status, detail) => {
52560
+ if (isOpenCodeFreeTierRefusal(status, detail)) return OPENCODE_FREE_TIER_HINT;
52561
+ return status === 429 ? RATE_LIMIT_PROXY_HINT : void 0;
52562
+ },
52501
52563
  omitDefaultMaxTokens: true,
52502
52564
  omitMaxTokens: true,
52503
52565
  defaultContextWindow: 1e6,
@@ -52523,7 +52585,7 @@ var FreeCodeGoManagedCatalogs = class {
52523
52585
  headers: { "user-agent": "freecodego/kilo" }
52524
52586
  };
52525
52587
  },
52526
- rateLimitedHint: () => KILO_RATE_LIMIT_HINT,
52588
+ rateLimitedHint: (_provider, status) => status === 429 ? KILO_RATE_LIMIT_HINT : void 0,
52527
52589
  reasoningWire: "standard",
52528
52590
  normalizeReasoningEffort: (effort) => isDirectReasoningEffort(effort) ? effort : void 0,
52529
52591
  defaultReasoningEffort: "off",
@@ -52863,8 +52925,8 @@ var FreeCodeGoManagedCatalogs = class {
52863
52925
  /** Refresh user-visible gateway monitor facts without delaying model picker paint. */
52864
52926
  refreshGatewayHealthInBackground() {
52865
52927
  const endpoint = this.deps.gatewayBaseUrl();
52866
- const unsupported = this.gatewayHealthUnsupported;
52867
- if (unsupported !== void 0 && unsupported.endpoint === endpoint && Date.now() < unsupported.expiresAt || this.deps.api() === void 0 || this.deps.account() === void 0 || this.deps.account().snapshot().status !== "authenticated") return;
52928
+ const unsupported2 = this.gatewayHealthUnsupported;
52929
+ if (unsupported2 !== void 0 && unsupported2.endpoint === endpoint && Date.now() < unsupported2.expiresAt || this.deps.api() === void 0 || this.deps.account() === void 0 || this.deps.account().snapshot().status !== "authenticated") return;
52868
52930
  const now = Date.now();
52869
52931
  if (this.gatewayHealthPromise !== void 0 || (this.gatewayHealthCache?.expiresAt ?? 0) > now || this.gatewayHealthRefreshAfter > now) return;
52870
52932
  this.gatewayHealthRefreshAfter = now + GATEWAY_HEALTH_CACHE_TTL_MS;
@@ -55156,10 +55218,7 @@ function renderFile(file) {
55156
55218
  lines.push("</file>");
55157
55219
  return lines.join("\n");
55158
55220
  }
55159
- var REVIEW_MESSAGE_SOURCE = {
55160
- kind: "plugin",
55161
- plugin: "freecodego/review"
55162
- };
55221
+ var REVIEW_MESSAGE_SOURCE = { kind: "freecodego/review" };
55163
55222
  function createReviewModelPort(llm, resolveRoute2, sessionId) {
55164
55223
  return { async generate(request) {
55165
55224
  const route = resolveRoute2();
@@ -58048,10 +58107,7 @@ function wrapChild(handle, maxOutputChars) {
58048
58107
  return {
58049
58108
  async run(prompt) {
58050
58109
  handle.agent.followup(createUserMessage({
58051
- source: {
58052
- kind: "plugin",
58053
- plugin: "freecodego-review"
58054
- },
58110
+ source: { kind: "freecodego-review" },
58055
58111
  content: [{
58056
58112
  type: "text",
58057
58113
  text: prompt
@@ -58363,6 +58419,14 @@ function narrowTurnScope(turnPaths, changedPaths) {
58363
58419
  const changed = new Set(changedPaths);
58364
58420
  return turnPaths.every((path3) => changed.has(path3)) ? turnPaths : changedPaths;
58365
58421
  }
58422
+ function isCallbackTarget(target) {
58423
+ if (target === void 0) return false;
58424
+ try {
58425
+ return new URL(target, "http://127.0.0.1").pathname === TRAE_CALLBACK_PATH;
58426
+ } catch {
58427
+ return false;
58428
+ }
58429
+ }
58366
58430
  function completionPage(language) {
58367
58431
  const title = language === "zh" ? "TRAE \u6388\u6743\u5DF2\u6536\u5230" : "TRAE authorization received";
58368
58432
  return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title></head><body style="font-family:system-ui;margin:48px;color:#222"><h1 style="font-size:20px">${title}</h1><p>${language === "zh" ? "\u8BF7\u56DE\u5230\u5E94\u7528\u67E5\u770B\u767B\u5F55\u7ED3\u679C\u3002" : "Return to the app to see the result."}</p></body></html>`;
@@ -58370,7 +58434,7 @@ function completionPage(language) {
58370
58434
  async function startTraeCallbackListener(onCallback, language = "zh") {
58371
58435
  const server = createServer((request, response) => {
58372
58436
  const url = request.url === void 0 ? "/" : request.url;
58373
- if (!url.startsWith("/authorize")) {
58437
+ if (!isCallbackTarget(url)) {
58374
58438
  response.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
58375
58439
  response.end("not found");
58376
58440
  return;
@@ -58684,6 +58748,16 @@ async function sendVerifyCode(host, email) {
58684
58748
  if (host.account === void 0) throw backendNotConfigured();
58685
58749
  return host.account.sendVerifyCode(email);
58686
58750
  }
58751
+ async function forgotPassword(host, email) {
58752
+ if (host.account === void 0) throw backendNotConfigured();
58753
+ await host.account.requestPasswordResetCode(email);
58754
+ return { sent: true };
58755
+ }
58756
+ async function resetPassword(host, input) {
58757
+ if (host.account === void 0) throw backendNotConfigured();
58758
+ await host.account.resetPassword(input);
58759
+ return { reset: true };
58760
+ }
58687
58761
  async function login(host, input) {
58688
58762
  if (host.account === void 0) throw backendNotConfigured();
58689
58763
  const result = await host.account.login({
@@ -61248,10 +61322,7 @@ async function engineeringTeamDecision(host, sessionId, request) {
61248
61322
  if (goalId !== void 0) host.engineering.armApprovedPlanGoal(agent);
61249
61323
  }
61250
61324
  agent.inject(createUserMessage({
61251
- source: {
61252
- kind: "plugin",
61253
- plugin: "freecodego-engine-council"
61254
- },
61325
+ source: { kind: "freecodego-engine-council" },
61255
61326
  content: [{
61256
61327
  type: "text",
61257
61328
  text: decision.state === "approved" ? goalId === void 0 ? "The user approved engineering council " + decision.id + ". You may implement the reviewed plan, then run council verification." : "The user approved engineering council " + decision.id + '. The approved plan is now goal "' + goalId + '", which the Harness continues automatically while it stays active. Work the plan to completion, then run council verification.' : "The user rejected engineering council " + decision.id + ". Do not implement this plan; revise it or start a new council."
@@ -61283,10 +61354,7 @@ async function engineeringTeamVerify(host, sessionId, request) {
61283
61354
  }
61284
61355
  await host.engineCouncil.recordVerification(agent, input.id, result);
61285
61356
  agent.inject(createUserMessage({
61286
- source: {
61287
- kind: "plugin",
61288
- plugin: "freecodego-engine-council"
61289
- },
61357
+ source: { kind: "freecodego-engine-council" },
61290
61358
  content: [{
61291
61359
  type: "text",
61292
61360
  text: "Engineering council " + input.id + " verification is complete. Read the durable verification result and report failed, skipped, unavailable, or cancelled stages accurately."
@@ -61302,10 +61370,7 @@ async function engineeringTeamImplementation(host, sessionId, request) {
61302
61370
  const implementation = await host.engineCouncil.recordImplementation(agent, request.id, request.summary);
61303
61371
  engineeringTeamVerify(host, sessionId, { id: request.id }).catch(() => void 0);
61304
61372
  agent.inject(createUserMessage({
61305
- source: {
61306
- kind: "plugin",
61307
- plugin: "freecodego-engine-council"
61308
- },
61373
+ source: { kind: "freecodego-engine-council" },
61309
61374
  content: [{
61310
61375
  type: "text",
61311
61376
  text: `Implementation for engineering council ${request.id} was marked complete. Executed verification (build/types/lint/tests) is starting automatically; read its durable result when reporting completion.`
@@ -61535,6 +61600,73 @@ var WorkBuddyPoolService = class {
61535
61600
  return results;
61536
61601
  }
61537
61602
  };
61603
+ var WEB_SEARCH_API_KEY_REF = credentialRef2("FREECODEGO_WEB_SEARCH_API_KEY");
61604
+ var WEB_SEARCH_SETTINGS_NAMESPACE = "web-search-deepseek";
61605
+ function messageOf(error) {
61606
+ return error instanceof Error ? error.message : String(error);
61607
+ }
61608
+ function classify(section, servesBridgeRoute) {
61609
+ if (section === void 0) return {
61610
+ state: "none",
61611
+ alive: true
61612
+ };
61613
+ if (asString(section.apiKeyEnv) !== WEB_SEARCH_API_KEY_REF) return {
61614
+ state: "other",
61615
+ alive: true
61616
+ };
61617
+ const saved = asString(section.baseURL);
61618
+ if (saved === void 0) return {
61619
+ state: "other",
61620
+ alive: true
61621
+ };
61622
+ const routeId = bridgeRouteIdOf(saved);
61623
+ return routeId === void 0 ? {
61624
+ state: "durable",
61625
+ alive: true
61626
+ } : {
61627
+ state: "bridge",
61628
+ alive: servesBridgeRoute(routeId)
61629
+ };
61630
+ }
61631
+ async function webSearchBindingStatus(deps) {
61632
+ const status = classify(await deps.readSection(), deps.servesBridgeRoute);
61633
+ return deps.remembered === void 0 ? status : {
61634
+ ...status,
61635
+ remembered: deps.remembered
61636
+ };
61637
+ }
61638
+ async function repairWebSearchBinding(deps) {
61639
+ let section;
61640
+ try {
61641
+ section = await deps.readSection();
61642
+ } catch (error) {
61643
+ return {
61644
+ outcome: "failed",
61645
+ detail: messageOf(error)
61646
+ };
61647
+ }
61648
+ const status = classify(section, deps.servesBridgeRoute);
61649
+ if (status.state === "none") return { outcome: "unavailable" };
61650
+ if (status.state === "other") return { outcome: "not-applicable" };
61651
+ if (status.alive) return { outcome: "live" };
61652
+ const remembered = deps.remembered;
61653
+ if (remembered === void 0) return { outcome: "no-memory" };
61654
+ try {
61655
+ const binding = await deps.bind(remembered);
61656
+ await deps.writeCredential(binding.apiKeyEnv, binding.apiKey);
61657
+ await deps.writeSection({
61658
+ model: binding.model,
61659
+ baseURL: binding.baseURL,
61660
+ apiKeyEnv: binding.apiKeyEnv
61661
+ });
61662
+ return { outcome: "rebuilt" };
61663
+ } catch (error) {
61664
+ return {
61665
+ outcome: "failed",
61666
+ detail: messageOf(error)
61667
+ };
61668
+ }
61669
+ }
61538
61670
  var FREECODEGO_CLOUD_ORIGIN = "https://freecodego.com";
61539
61671
  async function setDefaultEngine(host, engine) {
61540
61672
  if (engine !== "deepseek" && engine !== "codex" && engine !== "claude") throw new Error("agent engine must be deepseek, codex, or claude");
@@ -61587,9 +61719,9 @@ async function setDefaultModel(host, model) {
61587
61719
  return { model: persisted };
61588
61720
  }
61589
61721
  function defaultAgentOptions(host) {
61590
- const configuredEngine = host.policy.get()?.defaultEngine ?? host.config.defaultEngine ?? "deepseek";
61722
+ const configuredEngine = host.policy.get()?.defaultEngine ?? "deepseek";
61591
61723
  const engine = configuredEngine === "codex" || configuredEngine === "claude" ? configuredEngine : "deepseek";
61592
- const model = host.policy.get()?.defaultModel ?? host.config.defaultModel;
61724
+ const model = host.policy.get()?.defaultModel;
61593
61725
  const normalizedModel = model?.trim();
61594
61726
  if (normalizedModel !== void 0 && AGNES_TEXT_MODEL_IDS.has(normalizedModel)) return {
61595
61727
  engine,
@@ -61799,6 +61931,26 @@ async function sessionDelete(host, sessionId) {
61799
61931
  host.ctx.emit("api-session/removed", id);
61800
61932
  return { deleted: true };
61801
61933
  }
61934
+ async function webSearchBind(host, input) {
61935
+ if (input === null || typeof input !== "object") throw new Error("web search binding must be an object");
61936
+ const provider = typeof input.provider === "string" ? input.provider.trim().toLowerCase() : "";
61937
+ const model = typeof input.model === "string" ? input.model.trim() : "";
61938
+ if (provider === "" || model === "") throw new Error("a provider and a model are required");
61939
+ const route = await host.catalogs.claudeGatewayForRoute({
61940
+ provider,
61941
+ modelId: model
61942
+ });
61943
+ const base = route.baseURL.replace(/\/+$/u, "");
61944
+ if (!/^https?:\/\//iu.test(base)) throw new Error(`Provider "${provider}" did not resolve an HTTP endpoint`);
61945
+ return {
61946
+ provider,
61947
+ model: route.model ?? model,
61948
+ baseURL: /\/v1$/u.test(base) ? base : `${base}/v1`,
61949
+ apiKeyEnv: WEB_SEARCH_API_KEY_REF,
61950
+ apiKey: route.apiKey,
61951
+ durable: !/^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(?::\d+)?(?:\/|$)/iu.test(base)
61952
+ };
61953
+ }
61802
61954
  async function capabilitiesSnapshot(host) {
61803
61955
  return host.capabilities.snapshot();
61804
61956
  }
@@ -62157,9 +62309,7 @@ function configuredProviderRoute(ctx, model) {
62157
62309
  model: model.slice(provider.length + 1)
62158
62310
  };
62159
62311
  }
62160
- const settings = ctx.get("settings");
62161
- if (typeof settings?.get !== "function") return void 0;
62162
- const providers = asRecord(asRecord(settings.get("llm-pi-ai")).providers);
62312
+ const providers = asRecord(asRecord(peerSettings(ctx, MODELS_SETTINGS_ENTRY)).providers);
62163
62313
  for (const [provider, profile] of Object.entries(providers)) {
62164
62314
  const row = asRecord(profile);
62165
62315
  if ((Array.isArray(row.models) ? row.models : []).some((entry) => asRecord(entry).id === model || String(asRecord(entry).id ?? "").toLowerCase() === model.toLowerCase())) return {
@@ -62443,7 +62593,7 @@ var MEMORY_CASES = [
62443
62593
  suite: "memory",
62444
62594
  claim: "A shared file is preferred over a shared session, because it is the actionable link.",
62445
62595
  measure: () => {
62446
- const classify = (sharedFile, sameTurn, sameSession) => sharedFile !== void 0 ? "shared-file" : sameTurn ? "same-turn" : sameSession ? "same-session" : "none";
62596
+ const classify2 = (sharedFile, sameTurn, sameSession) => sharedFile !== void 0 ? "shared-file" : sameTurn ? "same-turn" : sameSession ? "same-session" : "none";
62447
62597
  const cases = [
62448
62598
  [
62449
62599
  "shared-file",
@@ -62470,7 +62620,7 @@ var MEMORY_CASES = [
62470
62620
  true
62471
62621
  ]
62472
62622
  ];
62473
- const correct = cases.filter(([expected, file, turn, session]) => classify(file, turn, session) === expected).length;
62623
+ const correct = cases.filter(([expected, file, turn, session]) => classify2(file, turn, session) === expected).length;
62474
62624
  return {
62475
62625
  observed: correct / cases.length,
62476
62626
  required: 1,
@@ -62793,28 +62943,43 @@ var WIRE_CASES = [
62793
62943
  {
62794
62944
  id: "wire.tool-results-stay-adjacent",
62795
62945
  suite: "wire",
62796
- claim: "Tool results immediately follow their assistant frame; trailing text becomes a later user turn.",
62946
+ claim: "A tool result is its own tool-role message and follows the assistant frame that asked for it, with later text as a user turn.",
62797
62947
  measure: () => {
62798
62948
  const roles = wireMessages(serializeRequest({
62799
62949
  provider: "logfare",
62800
62950
  model: "gpt-5.6-sol",
62801
- messages: [createUserMessage({
62802
- source: { kind: "user" },
62803
- content: [{
62804
- type: "text",
62805
- text: "Continue the discussion."
62806
- }, {
62807
- type: "tool-result",
62808
- toolCallId: ToolCallId("call-1"),
62951
+ messages: [
62952
+ createAssistantMessage2({
62953
+ source: {
62954
+ provider: "logfare",
62955
+ model: "gpt-5.6-sol"
62956
+ },
62957
+ content: [{
62958
+ type: "tool-call",
62959
+ id: ToolCallId("call-1"),
62960
+ name: "read",
62961
+ arguments: '{"path":"a"}'
62962
+ }]
62963
+ }),
62964
+ createToolResultMessage2({
62965
+ callId: ToolCallId("call-1"),
62809
62966
  content: [{
62810
62967
  type: "text",
62811
62968
  text: "ok"
62969
+ }],
62970
+ isError: false
62971
+ }),
62972
+ createUserMessage({
62973
+ source: { kind: "user" },
62974
+ content: [{
62975
+ type: "text",
62976
+ text: "Continue the discussion."
62812
62977
  }]
62813
- }]
62814
- })]
62978
+ })
62979
+ ]
62815
62980
  })).map((message2) => message2.role);
62816
62981
  return {
62817
- observed: roles.join(",") === "tool,user" ? 1 : 0,
62982
+ observed: roles.join(",") === "assistant,tool,user" ? 1 : 0,
62818
62983
  required: 1,
62819
62984
  detail: `roles=${roles.join(",")}`
62820
62985
  };
@@ -62835,16 +63000,13 @@ var WIRE_CASES = [
62835
63000
  const first = wireMessages(serializeRequest({
62836
63001
  provider: "logfare",
62837
63002
  model: "gpt-5.6-sol",
62838
- messages: [createUserMessage({
62839
- source: { kind: "user" },
63003
+ messages: [createToolResultMessage2({
63004
+ callId: ToolCallId("call-1"),
62840
63005
  content: [{
62841
- type: "tool-result",
62842
- toolCallId: ToolCallId("call-1"),
62843
- content: [{
62844
- type: "image",
62845
- attachment
62846
- }]
62847
- }]
63006
+ type: "image",
63007
+ attachment
63008
+ }],
63009
+ isError: false
62848
63010
  })]
62849
63011
  }))[0];
62850
63012
  return {
@@ -65897,23 +66059,42 @@ var ADAPTER_CASES = [
65897
66059
  }
65898
66060
  }
65899
66061
  ];
66062
+ function routingSettings(value, onWrite = () => void 0) {
66063
+ return {
66064
+ describe: () => [{
66065
+ ns: "subagent-model-selection",
66066
+ value,
66067
+ revision: 3
66068
+ }],
66069
+ update: async (_ns, patch) => {
66070
+ onWrite(patch);
66071
+ }
66072
+ };
66073
+ }
66074
+ var NO_ROUTING_SETTINGS = {
66075
+ describe: () => [],
66076
+ update: async () => void 0
66077
+ };
65900
66078
  var ROUTING_CASES = [
65901
66079
  {
65902
66080
  id: "routing.sync-needs-a-settings-namespace",
65903
66081
  suite: "routing",
65904
- claim: "A composition without the subagent settings namespace syncs nothing.",
66082
+ claim: "A composition that declares no Subagent model-selection entry syncs nothing and writes nothing.",
65905
66083
  measure: async () => {
66084
+ let writes = 0;
65906
66085
  const result = await synchronizeSubagentModelRoutes({
65907
- get: () => void 0,
65908
- update: async () => void 0
66086
+ ...NO_ROUTING_SETTINGS,
66087
+ update: async () => {
66088
+ writes += 1;
66089
+ }
65909
66090
  }, {
65910
66091
  listProviders: () => [{ id: "p" }],
65911
66092
  listModels: async () => [{ id: "m" }]
65912
66093
  });
65913
66094
  return {
65914
- observed: result.length === 0 ? 1 : 0,
66095
+ observed: result.length === 0 && writes === 0 ? 1 : 0,
65915
66096
  required: 1,
65916
- detail: `routes=${result.length}`
66097
+ detail: `routes=${result.length} writes=${writes}`
65917
66098
  };
65918
66099
  }
65919
66100
  },
@@ -65923,15 +66104,12 @@ var ROUTING_CASES = [
65923
66104
  claim: "Unavailable and non-text models are excluded, and duplicates are collapsed.",
65924
66105
  measure: async () => {
65925
66106
  const written = [];
65926
- const ids = (await synchronizeSubagentModelRoutes({
65927
- get: () => ({
65928
- allowedModels: [],
65929
- enabled: void 0
65930
- }),
65931
- update: async (_ns, value) => {
65932
- written.push(value);
65933
- }
65934
- }, {
66107
+ const ids = (await synchronizeSubagentModelRoutes(routingSettings({
66108
+ allowedModels: [],
66109
+ enabled: void 0
66110
+ }, (patch) => {
66111
+ written.push(patch);
66112
+ }), {
65935
66113
  listProviders: () => [{ id: "p" }],
65936
66114
  listModels: async () => [
65937
66115
  {
@@ -65972,27 +66150,23 @@ var ROUTING_CASES = [
65972
66150
  suite: "routing",
65973
66151
  claim: "A provider that answered with nothing loses its stale routes; one that failed keeps them.",
65974
66152
  measure: async () => {
65975
- const previous = [
65976
- {
65977
- provider: "answered",
65978
- model: "stale"
65979
- },
65980
- {
65981
- provider: "failed",
65982
- model: "keep"
65983
- },
65984
- {
65985
- provider: "removed",
65986
- model: "drop"
65987
- }
65988
- ];
65989
- const ids = (await synchronizeSubagentModelRoutes({
65990
- get: () => ({
65991
- allowedModels: previous,
65992
- enabled: true
65993
- }),
65994
- update: async () => void 0
65995
- }, {
66153
+ const ids = (await synchronizeSubagentModelRoutes(routingSettings({
66154
+ allowedModels: [
66155
+ {
66156
+ provider: "answered",
66157
+ model: "stale"
66158
+ },
66159
+ {
66160
+ provider: "failed",
66161
+ model: "keep"
66162
+ },
66163
+ {
66164
+ provider: "removed",
66165
+ model: "drop"
66166
+ }
66167
+ ],
66168
+ enabled: true
66169
+ }), {
65996
66170
  listProviders: () => [
65997
66171
  { id: "answered" },
65998
66172
  { id: "failed" },
@@ -66022,15 +66196,12 @@ var ROUTING_CASES = [
66022
66196
  measure: async () => {
66023
66197
  const writes = [];
66024
66198
  const run2 = async (enabled) => {
66025
- await synchronizeSubagentModelRoutes({
66026
- get: () => ({
66027
- allowedModels: [],
66028
- enabled
66029
- }),
66030
- update: async (_ns, value) => {
66031
- writes.push(value);
66032
- }
66033
- }, {
66199
+ await synchronizeSubagentModelRoutes(routingSettings({
66200
+ allowedModels: [],
66201
+ enabled
66202
+ }, (patch) => {
66203
+ writes.push(patch);
66204
+ }), {
66034
66205
  listProviders: () => [{ id: "p" }],
66035
66206
  listModels: async () => [{
66036
66207
  id: "m",
@@ -66056,18 +66227,15 @@ var ROUTING_CASES = [
66056
66227
  claim: "An unchanged catalog does not rewrite the settings document.",
66057
66228
  measure: async () => {
66058
66229
  let updates = 0;
66059
- await synchronizeSubagentModelRoutes({
66060
- get: () => ({
66061
- allowedModels: [{
66062
- provider: "p",
66063
- model: "m"
66064
- }],
66065
- enabled: true
66066
- }),
66067
- update: async () => {
66068
- updates += 1;
66069
- }
66070
- }, {
66230
+ await synchronizeSubagentModelRoutes(routingSettings({
66231
+ allowedModels: [{
66232
+ provider: "p",
66233
+ model: "m"
66234
+ }],
66235
+ enabled: true
66236
+ }, () => {
66237
+ updates += 1;
66238
+ }), {
66071
66239
  listProviders: () => [{ id: "p" }],
66072
66240
  listModels: async () => [{
66073
66241
  id: "m",
@@ -67745,10 +67913,6 @@ function reportCommandPolicyDiagnostics(ctx, policy) {
67745
67913
  commandPolicyDiagnosticsReported = true;
67746
67914
  ctx.logger?.warn?.(`freecodego: the built-in command policy compiled with ${policy.diagnostics.length} diagnostic(s); rules that failed their own examples were dropped and are no longer enforced \u2014 ${describePolicyDiagnostics(policy.diagnostics)}`);
67747
67915
  }
67748
- function bootstrapFreeCodeGoHarness(ctx) {
67749
- registerFreeCodeGoSessionEventTypes();
67750
- return installFreeCodeGoPluginConflictGuard(ctx);
67751
- }
67752
67916
  function memoryPipelineHome(cwd) {
67753
67917
  const identity = path.resolve(cwd).replaceAll("\\", "/").toLowerCase();
67754
67918
  const segment = createHash3("sha256").update(identity).digest("hex").slice(0, 24);
@@ -67837,6 +68001,8 @@ var FreeCodeGoHarnessPlugin = (() => {
67837
68001
  let _headroomStatus_decorators;
67838
68002
  let _deferredToolsStatus_decorators;
67839
68003
  let _deferredToolsSetEnabled_decorators;
68004
+ let _mediaGenerationStatus_decorators;
68005
+ let _mediaGenerationSetEnabled_decorators;
67840
68006
  let _lspMountStatus_decorators;
67841
68007
  let _sandboxModeStatus_decorators;
67842
68008
  let _sandboxModeSet_decorators;
@@ -67873,6 +68039,8 @@ var FreeCodeGoHarnessPlugin = (() => {
67873
68039
  let _engineeringCodeGraphCancel_decorators;
67874
68040
  let _engineeringCodeGraphClearProject_decorators;
67875
68041
  let _sessionDelete_decorators;
68042
+ let _webSearchBind_decorators;
68043
+ let _webSearchBindingStatus_decorators;
67876
68044
  let _capabilitiesSnapshot_decorators;
67877
68045
  let _capabilitiesSetEnabled_decorators;
67878
68046
  let _modelCategorySet_decorators;
@@ -67916,6 +68084,8 @@ var FreeCodeGoHarnessPlugin = (() => {
67916
68084
  let _accountDetail_decorators;
67917
68085
  let _register_decorators;
67918
68086
  let _sendVerifyCode_decorators;
68087
+ let _forgotPassword_decorators;
68088
+ let _resetPassword_decorators;
67919
68089
  let _login_decorators;
67920
68090
  let _rememberedPassword_decorators;
67921
68091
  let _oauthLogin_decorators;
@@ -68059,6 +68229,8 @@ var FreeCodeGoHarnessPlugin = (() => {
68059
68229
  _headroomStatus_decorators = [Remote("headroomStatus")];
68060
68230
  _deferredToolsStatus_decorators = [Remote("deferredToolsStatus")];
68061
68231
  _deferredToolsSetEnabled_decorators = [Remote("deferredToolsSetEnabled")];
68232
+ _mediaGenerationStatus_decorators = [Remote("mediaGenerationStatus")];
68233
+ _mediaGenerationSetEnabled_decorators = [Remote("mediaGenerationSetEnabled")];
68062
68234
  _lspMountStatus_decorators = [Remote("lspMountStatus")];
68063
68235
  _sandboxModeStatus_decorators = [Remote("sandboxModeStatus")];
68064
68236
  _sandboxModeSet_decorators = [Remote("sandboxModeSet")];
@@ -68095,6 +68267,8 @@ var FreeCodeGoHarnessPlugin = (() => {
68095
68267
  _engineeringCodeGraphCancel_decorators = [Remote("engineeringCodeGraphCancel")];
68096
68268
  _engineeringCodeGraphClearProject_decorators = [Remote("engineeringCodeGraphClearProject")];
68097
68269
  _sessionDelete_decorators = [Remote("sessionDelete")];
68270
+ _webSearchBind_decorators = [Remote("webSearchBind")];
68271
+ _webSearchBindingStatus_decorators = [Remote("webSearchBindingStatus")];
68098
68272
  _capabilitiesSnapshot_decorators = [Remote("capabilities")];
68099
68273
  _capabilitiesSetEnabled_decorators = [Remote("capabilitiesSetEnabled")];
68100
68274
  _modelCategorySet_decorators = [Remote("modelCategorySet")];
@@ -68138,6 +68312,8 @@ var FreeCodeGoHarnessPlugin = (() => {
68138
68312
  _accountDetail_decorators = [Remote("accountDetail")];
68139
68313
  _register_decorators = [Remote("accountRegister")];
68140
68314
  _sendVerifyCode_decorators = [Remote("accountSendVerifyCode")];
68315
+ _forgotPassword_decorators = [Remote("accountForgotPassword")];
68316
+ _resetPassword_decorators = [Remote("accountResetPassword")];
68141
68317
  _login_decorators = [Remote("accountLogin")];
68142
68318
  _rememberedPassword_decorators = [Remote("accountRememberedPassword")];
68143
68319
  _oauthLogin_decorators = [Remote("accountOAuthLogin")];
@@ -68788,6 +68964,28 @@ var FreeCodeGoHarnessPlugin = (() => {
68788
68964
  },
68789
68965
  metadata: _metadata
68790
68966
  }, null, _instanceExtraInitializers);
68967
+ __esDecorate(this, null, _mediaGenerationStatus_decorators, {
68968
+ kind: "method",
68969
+ name: "mediaGenerationStatus",
68970
+ static: false,
68971
+ private: false,
68972
+ access: {
68973
+ has: (obj) => "mediaGenerationStatus" in obj,
68974
+ get: (obj) => obj.mediaGenerationStatus
68975
+ },
68976
+ metadata: _metadata
68977
+ }, null, _instanceExtraInitializers);
68978
+ __esDecorate(this, null, _mediaGenerationSetEnabled_decorators, {
68979
+ kind: "method",
68980
+ name: "mediaGenerationSetEnabled",
68981
+ static: false,
68982
+ private: false,
68983
+ access: {
68984
+ has: (obj) => "mediaGenerationSetEnabled" in obj,
68985
+ get: (obj) => obj.mediaGenerationSetEnabled
68986
+ },
68987
+ metadata: _metadata
68988
+ }, null, _instanceExtraInitializers);
68791
68989
  __esDecorate(this, null, _lspMountStatus_decorators, {
68792
68990
  kind: "method",
68793
68991
  name: "lspMountStatus",
@@ -69184,6 +69382,28 @@ var FreeCodeGoHarnessPlugin = (() => {
69184
69382
  },
69185
69383
  metadata: _metadata
69186
69384
  }, null, _instanceExtraInitializers);
69385
+ __esDecorate(this, null, _webSearchBind_decorators, {
69386
+ kind: "method",
69387
+ name: "webSearchBind",
69388
+ static: false,
69389
+ private: false,
69390
+ access: {
69391
+ has: (obj) => "webSearchBind" in obj,
69392
+ get: (obj) => obj.webSearchBind
69393
+ },
69394
+ metadata: _metadata
69395
+ }, null, _instanceExtraInitializers);
69396
+ __esDecorate(this, null, _webSearchBindingStatus_decorators, {
69397
+ kind: "method",
69398
+ name: "webSearchBindingStatus",
69399
+ static: false,
69400
+ private: false,
69401
+ access: {
69402
+ has: (obj) => "webSearchBindingStatus" in obj,
69403
+ get: (obj) => obj.webSearchBindingStatus
69404
+ },
69405
+ metadata: _metadata
69406
+ }, null, _instanceExtraInitializers);
69187
69407
  __esDecorate(this, null, _capabilitiesSnapshot_decorators, {
69188
69408
  kind: "method",
69189
69409
  name: "capabilitiesSnapshot",
@@ -69657,6 +69877,28 @@ var FreeCodeGoHarnessPlugin = (() => {
69657
69877
  },
69658
69878
  metadata: _metadata
69659
69879
  }, null, _instanceExtraInitializers);
69880
+ __esDecorate(this, null, _forgotPassword_decorators, {
69881
+ kind: "method",
69882
+ name: "forgotPassword",
69883
+ static: false,
69884
+ private: false,
69885
+ access: {
69886
+ has: (obj) => "forgotPassword" in obj,
69887
+ get: (obj) => obj.forgotPassword
69888
+ },
69889
+ metadata: _metadata
69890
+ }, null, _instanceExtraInitializers);
69891
+ __esDecorate(this, null, _resetPassword_decorators, {
69892
+ kind: "method",
69893
+ name: "resetPassword",
69894
+ static: false,
69895
+ private: false,
69896
+ access: {
69897
+ has: (obj) => "resetPassword" in obj,
69898
+ get: (obj) => obj.resetPassword
69899
+ },
69900
+ metadata: _metadata
69901
+ }, null, _instanceExtraInitializers);
69660
69902
  __esDecorate(this, null, _login_decorators, {
69661
69903
  kind: "method",
69662
69904
  name: "login",
@@ -70654,40 +70896,336 @@ var FreeCodeGoHarnessPlugin = (() => {
70654
70896
  "sessionPersistence"
70655
70897
  ];
70656
70898
  /**
70657
- * Runtime schema for {@link Config}, declared inline so the generated config
70658
- * catalog can walk it: it is the only statically resolvable form a
70659
- * plugin-class schema may take. Schemastery has no first-class `.optional()`;
70660
- * the public `Config` type already types these fields as optional, so the
70661
- * schema carries the same contract at runtime (absent input passes through)
70662
- * without fighting the inferred builder types.
70899
+ * Runtime schema for {@link Config}.
70900
+ *
70901
+ * One flat `z.object({...})` literal, and that shape is load-bearing: the generated
70902
+ * config catalog walks this expression *statically* — it resolves a local `const` or
70903
+ * a workspace-package import and refuses anything else — and the readers gate in
70904
+ * `scripts/freecodego-config-readers.spec.ts` parses the same literal to check that
70905
+ * every field it declares is read somewhere. The document therefore cannot be
70906
+ * assembled from the group schemas' own modules, even though those modules own the
70907
+ * *types*: an `intersect` of relative imports is invisible to both walks, and a
70908
+ * schema nothing walks is a schema nothing checks.
70909
+ *
70910
+ * Which fields are which
70911
+ * ----------------------
70912
+ * The first block is deployment input: ordinary fields, no `volatile()`, and a change
70913
+ * to one of them is a composition change that remounts the plugin. Every field below
70914
+ * it is a setting — `volatile()` is what the loader reads as "commit this into the
70915
+ * running references instead of remounting", and it is also what puts the field in
70916
+ * the settings form. {@link FreeCodeGoPolicy} is the only reader of any of them; the
70917
+ * section comments name the module that owns each group's type.
70663
70918
  */
70664
70919
  static Config = z2.object({
70665
70920
  gateway: z2.object({ baseUrl: z2.string().default(FREECODEGO_CLOUD_ORIGIN) }),
70666
- defaultModel: z2.string(),
70667
- defaultEngine: z2.union([
70668
- z2.const("deepseek"),
70669
- z2.const("codex"),
70670
- z2.const("claude")
70671
- ]).default("deepseek"),
70672
70921
  codexRuntimeDirectory: z2.string().default(""),
70673
70922
  codexRuntimeSourceDirectory: z2.string().default(""),
70674
70923
  claudeRuntimeDirectory: z2.string().default(""),
70675
70924
  autoSubagentModelSelection: z2.boolean().default(true),
70676
- autoAdvisorEnabled: z2.boolean().default(true),
70677
70925
  updatePackageName: z2.string().default(""),
70678
70926
  updateReleaseRepository: z2.string().default(""),
70679
- updateReleaseTokenEnv: z2.string().default("")
70927
+ updateReleaseTokenEnv: z2.string().default(""),
70928
+ mcpEnabled: z2.boolean().default(true).volatile(),
70929
+ skillEnabled: z2.boolean().default(true).volatile(),
70930
+ voiceInputEnabled: z2.boolean().default(true).volatile(),
70931
+ sessionDeleteEnabled: z2.boolean().default(true).volatile(),
70932
+ modelCategories: z2.dict(z2.union([
70933
+ z2.const("text"),
70934
+ z2.const("image"),
70935
+ z2.const("video"),
70936
+ z2.const("audio")
70937
+ ])).default({}).volatile(),
70938
+ webSearchBindingProvider: z2.string().default("").volatile(),
70939
+ webSearchBindingModel: z2.string().default("").volatile(),
70940
+ mcpServers: z2.array(z2.object({
70941
+ id: z2.string().min(1).max(80),
70942
+ enabled: z2.boolean().default(true),
70943
+ transport: z2.union([z2.const("stdio"), z2.const("streamable-http")]),
70944
+ serverName: z2.string().pattern(SERVER_NAME$1),
70945
+ command: z2.string().default(""),
70946
+ args: z2.array(z2.string().max(4096)).default([]),
70947
+ env: z2.dict(z2.string()).default({}),
70948
+ cwd: z2.string().default(""),
70949
+ url: z2.string().default(""),
70950
+ headers: z2.dict(z2.string()).default({})
70951
+ })).default([]).volatile(),
70952
+ skillRoots: z2.array(z2.object({
70953
+ id: z2.string().min(1).max(80),
70954
+ enabled: z2.boolean().default(true),
70955
+ path: z2.string().min(1).max(4096)
70956
+ })).default([]).volatile(),
70957
+ skillInvocationOverrides: z2.dict(z2.boolean()).default({}).volatile(),
70958
+ preferredSkillPlacement: z2.union([z2.object({
70959
+ agent: z2.union([z2.const("harness"), z2.const("agents")]),
70960
+ scope: z2.union([z2.const("project"), z2.const("user")])
70961
+ }), z2.const(null)]).volatile(),
70962
+ envReadGuardEnabled: z2.boolean().default(true).volatile(),
70963
+ doomLoopGuardEnabled: z2.boolean().default(true).volatile(),
70964
+ lspEnabled: z2.boolean().default(true).volatile(),
70965
+ commandPolicyEnabled: z2.boolean().default(true).volatile(),
70966
+ planModeEnabled: z2.boolean().default(true).volatile(),
70967
+ contextBudgetEnabled: z2.boolean().default(true).volatile(),
70968
+ cacheColdClearEnabled: z2.boolean().default(true).volatile(),
70969
+ cacheBreakAttributionEnabled: z2.boolean().default(true).volatile(),
70970
+ assistantLoopGuardEnabled: z2.boolean().default(true).volatile(),
70971
+ spillRecallEnabled: z2.boolean().default(true).volatile(),
70972
+ compactionFidelityEnabled: z2.boolean().default(true).volatile(),
70973
+ promptCompositionEnabled: z2.boolean().default(true).volatile(),
70974
+ /**
70975
+ * Master switch for the whole gate.
70976
+ *
70977
+ * Off means every project-scoped surface is treated as trusted — the
70978
+ * pre-gate behaviour, kept reachable so a deployment that already controls
70979
+ * which repositories it opens is not forced to grant each one. It is
70980
+ * deliberately not the default: the safe state must not be the opt-in one.
70981
+ */
70982
+ folderTrustEnabled: z2.boolean().default(true).volatile(),
70983
+ pluginConflictProtectionEnabled: z2.boolean().default(true).volatile(),
70984
+ pluginConflictRecords: z2.array(z2.object({
70985
+ id: z2.string().min(1).max(256),
70986
+ detectedAt: z2.number(),
70987
+ resource: z2.union([
70988
+ z2.const("tool"),
70989
+ z2.const("command"),
70990
+ z2.const("settings"),
70991
+ z2.const("route"),
70992
+ z2.const("provider"),
70993
+ z2.const("slot")
70994
+ ]),
70995
+ resourceName: z2.string().min(1).max(256),
70996
+ disabledEntryId: z2.string().min(1).max(512),
70997
+ disabledModuleName: z2.string().min(1).max(512),
70998
+ keptEntryId: z2.string().min(1).max(512),
70999
+ keptModuleName: z2.string().min(1).max(512),
71000
+ yieldedToOfficial: z2.boolean()
71001
+ })).default([]).volatile(),
71002
+ pluginUpdateChecksEnabled: z2.boolean().default(true).volatile(),
71003
+ engineeringEnabled: z2.boolean().default(true).volatile(),
71004
+ /**
71005
+ * The rest of the bundled engineering library is opt-in: 23 audited Skills
71006
+ * (the remainder of the authored set plus the vendored `mattpocock/skills`
71007
+ * entries that are not in the default-on starter root) stay unmounted until
71008
+ * the user turns this on.
71009
+ */
71010
+ engineeringSkillsEnabled: z2.boolean().default(false).volatile(),
71011
+ /**
71012
+ * The starter set is on by default: ten Skills whose value does not depend on
71013
+ * adopting a whole methodology. Four are disciplines the model applies on its
71014
+ * own (evidence before completion claims, navigation before broad reading,
71015
+ * planning multi-file work, root-cause debugging); the rest are `/name`
71016
+ * entries a user reaches for on demand and that cost nothing until typed
71017
+ * (a prompt-technique reference, the grilling interview and its primitive, a
71018
+ * re-pitch escape hatch, a handoff document, and a questionnaire for
71019
+ * decisions the agent cannot settle). They live in their own asset root so
71020
+ * "all engineering Skills" stays the union of the roots — no skill file is
71021
+ * duplicated, and no audit can disagree with a mount.
71022
+ */
71023
+ engineeringStarterSkillsEnabled: z2.boolean().default(true).volatile(),
71024
+ /**
71025
+ * The vendored superpowers workflow pack is a separate opt-in: it is the
71026
+ * auto-triggering, plan-then-dispatch methodology, so it must be a deliberate
71027
+ * choice rather than a side effect of enabling the engineering disciplines.
71028
+ */
71029
+ engineeringSuperpowersSkillsEnabled: z2.boolean().default(false).volatile(),
71030
+ /**
71031
+ * Injects a bounded capability map of the mounted Skills at session start.
71032
+ * A default-off Skill library is invisible otherwise: the model never lists
71033
+ * it, and a user who turned a pack on has no way to see what arrived.
71034
+ */
71035
+ engineeringSkillMapEnabled: z2.boolean().default(true).volatile(),
71036
+ /** Enables bounded project-declared verification after an approved council plan. */
71037
+ engineeringQualityEnabled: z2.boolean().default(true).volatile(),
71038
+ engineeringMemoryEnabled: z2.boolean().default(true).volatile(),
71039
+ engineeringCouncilEnabled: z2.boolean().default(true).volatile(),
71040
+ engineeringCouncilDeepseekEnabled: z2.boolean().default(true).volatile(),
71041
+ engineeringCouncilCodexEnabled: z2.boolean().default(true).volatile(),
71042
+ engineeringCouncilClaudeEnabled: z2.boolean().default(true).volatile(),
71043
+ engineeringMemoryContextTokenBudget: z2.number().step(1).min(0).max(4e3).default(1200).volatile(),
71044
+ /**
71045
+ * Default off, deliberately: the lexical rerank is free, deterministic, and
71046
+ * already ranks by term coverage. Flipping this on spends one small model
71047
+ * request per memory search to catch the memories that share no wording with
71048
+ * the query — worth it for a user who searches by concept, wasted for one who
71049
+ * greps for identifiers.
71050
+ */
71051
+ engineeringMemorySelectorEnabled: z2.boolean().default(false).volatile(),
71052
+ /**
71053
+ * Default off, deliberately: this lets a model approve a pending action without
71054
+ * asking the user. Off means every approval keeps prompting. On means an action
71055
+ * whose arguments the reviewer can actually read may be cleared automatically —
71056
+ * anything it refuses, cannot read, or declines to judge still goes to the user.
71057
+ */
71058
+ engineeringActionReviewEnabled: z2.boolean().default(false).volatile(),
71059
+ engineeringCodeGraphEnabled: z2.boolean().default(true).volatile(),
71060
+ engineeringCodeGraphAutoUpdate: z2.boolean().default(true).volatile(),
71061
+ engineeringGraphEngine: z2.union([
71062
+ z2.const("auto"),
71063
+ z2.const("graphify"),
71064
+ z2.const("codegraph")
71065
+ ]).default("auto").volatile(),
71066
+ engineeringCouncilMaxRounds: z2.number().step(1).min(1).max(3).default(2).volatile(),
71067
+ engineeringCouncilTimeoutMs: z2.number().step(1).min(1e4).max(3e5).default(12e4).volatile(),
71068
+ engineeringCouncilQuorum: z2.number().step(1).min(1).max(3).default(2).volatile(),
71069
+ engineeringCouncilAutoRun: z2.boolean().default(false).volatile(),
71070
+ /**
71071
+ * The three phases of the autonomous engineering loop. Each is a separate
71072
+ * switch because they carry different risk: creating a goal only commits the
71073
+ * user to finishing, while enabling the driver lets work continue with no
71074
+ * human turn in between.
71075
+ *
71076
+ * - `engineeringLoopCapturePlan` — when the user approves a council decision,
71077
+ * persist the approved plan as a durable Harness goal.
71078
+ * - `engineeringLoopVerifyOnComplete` — when the active goal's driver reports
71079
+ * it finished, run the declared verification stages and record the result.
71080
+ * - `engineeringLoopAutoContinue` — let the Harness goal-round driver start
71081
+ * the next round without a user turn. Off by default: a goal that continues
71082
+ * unattended is the one behaviour a user must opt into explicitly.
71083
+ */
71084
+ engineeringLoopCapturePlan: z2.boolean().default(true).volatile(),
71085
+ engineeringLoopVerifyOnComplete: z2.boolean().default(true).volatile(),
71086
+ engineeringLoopAutoContinue: z2.boolean().default(false).volatile(),
71087
+ /** Round cap for a captured plan goal; bounds unattended continuation. */
71088
+ engineeringLoopMaxGoalRounds: z2.number().step(1).min(1).max(256).default(24).volatile(),
71089
+ engineeringCouncilMaxTokens: z2.number().step(100).min(1200).max(2e4).default(3600).volatile(),
71090
+ engineeringCouncilMaxConcurrent: z2.number().step(1).min(1).max(8).default(2).volatile(),
71091
+ engineeringCouncilDecisionTtlMs: z2.number().step(6e4).min(6e4).max(10080 * 6e4).default(30 * 6e4).volatile(),
71092
+ /** Master switch for declarative failure recovery. */
71093
+ hookChainsEnabled: z2.boolean().default(true).volatile(),
71094
+ hookChainsMaxDepth: z2.number().step(1).min(0).max(10).default(2).volatile(),
71095
+ hookChainsCooldownMs: z2.number().step(1e3).min(0).max(1440 * 60 * 1e3).default(3e4).volatile(),
71096
+ /** Master switch for the calendar planner. */
71097
+ scheduledTasksEnabled: z2.boolean().default(true).volatile(),
71098
+ defaultModel: z2.string().default("").volatile(),
71099
+ defaultEngine: z2.union([
71100
+ z2.const("deepseek"),
71101
+ z2.const("codex"),
71102
+ z2.const("claude")
71103
+ ]).default("deepseek").volatile(),
71104
+ mediaDefaults: z2.object({
71105
+ image: z2.string().default(""),
71106
+ video: z2.string().default(""),
71107
+ audio: z2.string().default("")
71108
+ }).default({
71109
+ image: "",
71110
+ video: "",
71111
+ audio: ""
71112
+ }).volatile(),
71113
+ /**
71114
+ * Master switch for the image and video generation tools.
71115
+ *
71116
+ * On by default: the tools spend nothing until a route is called, and they are the
71117
+ * only way the capability is reachable — a feature that has to be found before it
71118
+ * does anything is why the media surface looked absent. It governs the generic
71119
+ * `freecodego_generate_image` / `_video` pair and the legacy `agnes_*` aliases with
71120
+ * them; `freecodego_generate_audio` and `freecodego_transcribe_audio` are
71121
+ * deliberately outside it, because one writes into the active workspace and the
71122
+ * other reads out of it.
71123
+ */
71124
+ mediaGenerationEnabled: z2.boolean().default(true).volatile(),
71125
+ /**
71126
+ * Sandbox profile deny globs, enforced in the plugin policy layer.
71127
+ *
71128
+ * Empty by default: a deny list is containment the user asked for, and
71129
+ * inventing one would refuse paths nobody named.
71130
+ */
71131
+ sandboxDenyPatterns: z2.array(z2.string()).default([]).volatile(),
71132
+ rehydrationEnabled: z2.boolean().default(true).volatile(),
71133
+ /**
71134
+ * Conversation-arc rehydration (goals + decisions folded from the
71135
+ * session's own goal events, promoted memories, and announced
71136
+ * choices). Off by default: it changes what the post-compaction
71137
+ * message contains, and a user must opt into new prompt surface —
71138
+ * the mechanical fold is bounded, but its wording is still model-
71139
+ * visible behavior.
71140
+ */
71141
+ rehydrationArcEnabled: z2.boolean().default(false).volatile(),
71142
+ advisorEnabled: z2.boolean().default(true).volatile(),
71143
+ advisorMode: z2.union([
71144
+ z2.const("async"),
71145
+ z2.const("catchup"),
71146
+ z2.const("blocker-only")
71147
+ ]).default("async").volatile(),
71148
+ advisorProvider: z2.string().default("opencode").volatile(),
71149
+ advisorModel: z2.string().default("auto").volatile(),
71150
+ advisorAllowAgentControl: z2.boolean().default(true).volatile(),
71151
+ advisorInterruptCooldownTurns: z2.number().step(1).min(0).max(20).default(3).volatile(),
71152
+ advisorMemoryDraftsEnabled: z2.boolean().default(true).volatile(),
71153
+ reviewMode: z2.union([
71154
+ z2.const("off"),
71155
+ z2.const("record"),
71156
+ z2.const("gate")
71157
+ ]).default("off").volatile(),
71158
+ reviewThreshold: z2.union([
71159
+ z2.const("critical"),
71160
+ z2.const("high"),
71161
+ z2.const("medium"),
71162
+ z2.const("low")
71163
+ ]).default("high").volatile(),
71164
+ reviewCooldownTurns: z2.number().step(1).min(0).max(20).default(3).volatile(),
71165
+ reviewDeep: z2.boolean().default(false).volatile(),
71166
+ reviewEscalation: z2.boolean().default(false).volatile(),
71167
+ headroomEnabled: z2.boolean().default(true).volatile(),
71168
+ headroomThresholdChars: z2.number().step(1).min(256).max(1e6).default(1200).volatile(),
71169
+ headroomMinSavingsRatio: z2.number().min(0.05).max(0.95).default(0.85).volatile(),
71170
+ headroomDedupEnabled: z2.boolean().default(true).volatile(),
71171
+ /**
71172
+ * Tools to protect *in addition* to the built-in list.
71173
+ *
71174
+ * Additional, not instead of: the built-in set
71175
+ * (`DEFAULT_EXCLUDE_TOOLS`) is a safety guarantee — those outputs are
71176
+ * what the model byte-patches against — and this field defaults to an
71177
+ * empty array, so a reading where it replaced the built-ins would make
71178
+ * the default install protect nothing.
71179
+ */
71180
+ headroomExcludeTools: z2.array(z2.string()).default([]).volatile(),
71181
+ headroomFoldReads: z2.boolean().default(false).volatile(),
71182
+ /**
71183
+ * Skeletonize large source-file reads: retained lines stay byte-exact
71184
+ * while whole body runs collapse to a marker. On by default because
71185
+ * `read` is the largest single source of re-sent context; the original
71186
+ * is always retrievable through `headroom_retrieve`.
71187
+ */
71188
+ headroomCodeSkeletonEnabled: z2.boolean().default(true).volatile(),
71189
+ /**
71190
+ * Reversible-render competition policy. `reversible` (default) delivers a
71191
+ * render that clears `FOLD_DECISIVE_RATIO` on sight — a Stage 2 fold, or a
71192
+ * mixed-content splice; `max` demotes every one of them to a candidate so
71193
+ * the compressor written for the payload's shape is always asked first.
71194
+ * The two settings are two products, not a fine-tuning — see
71195
+ * `HeadroomRuntime.foldPolicy` for the measured difference on the shapes
71196
+ * where they disagree.
71197
+ */
71198
+ headroomFoldPolicy: z2.union([z2.const("reversible"), z2.const("max")]).default("reversible").volatile(),
71199
+ /**
71200
+ * Keep task-specific tool schemas out of the request until the model
71201
+ * asks for them with `tool_search`. Measured saving is ~3.2k tokens per
71202
+ * request on this plugin's own 37 tools alone.
71203
+ */
71204
+ deferredToolSchemasEnabled: z2.boolean().default(true).volatile(),
71205
+ /** Explicit deferral list; when empty the plugin-owned prefix rule applies. */
71206
+ deferredToolNames: z2.array(z2.string()).default([]).volatile(),
71207
+ headroomOutputShaper: z2.boolean().default(false).volatile(),
71208
+ headroomVerbosityLevel: z2.number().step(1).min(0).max(4).default(0).volatile(),
71209
+ /**
71210
+ * Memory consolidation rollout (`memory/rollout.ts`).
71211
+ *
71212
+ * A stage rather than a switch because consolidating runs a model over
71213
+ * the session's successful turns: `record_only` captures without
71214
+ * consolidating, `shadow` consolidates without committing, and only
71215
+ * `active` writes. Off by default, and off is where an unrecognised
71216
+ * value lands too — see `resolveMemoryRollout`, which fails closed
71217
+ * rather than falling back to the legacy search path.
71218
+ */
71219
+ memoryRollout: z2.union([
71220
+ z2.const("off"),
71221
+ z2.const("record_only"),
71222
+ z2.const("shadow"),
71223
+ z2.const("active")
71224
+ ]).default("off").volatile()
70680
71225
  });
70681
71226
  account;
70682
71227
  api;
70683
71228
  credentials;
70684
- /**
70685
- * The registered settings namespace. Nothing outside this class reads it.
70686
- *
70687
- * Every behaviour read goes through {@link policy}, so exactly one place
70688
- * answers "what is configured".
70689
- */
70690
- engineSettings;
70691
71229
  /** The one accessor behaviour reads go through (see `policy.ts`). */
70692
71230
  policy;
70693
71231
  capabilities;
@@ -70935,7 +71473,11 @@ var FreeCodeGoHarnessPlugin = (() => {
70935
71473
  this.config = config;
70936
71474
  reportCommandPolicyDiagnostics(ctx, this.commandPolicy);
70937
71475
  ensureDesktopDshShim();
70938
- this.pendingWrites.run(() => ensureFreeCodeGoAgentPreset());
71476
+ ctx.effect(() => {
71477
+ const presets = installFreeCodeGoAgentPresets(ctx);
71478
+ this.pendingWrites.run(() => presets.done);
71479
+ return presets.release;
71480
+ }, "freecodego: agent presets");
70939
71481
  ctx.effect(() => {
70940
71482
  const known = KNOWN_SESSION_EVENT_TYPES;
70941
71483
  const added = freeCodeGoSessionEventTypes.filter((type) => !known.has(type));
@@ -70992,138 +71534,15 @@ var FreeCodeGoHarnessPlugin = (() => {
70992
71534
  const claudeStatus = this.claudeRuntime.status();
70993
71535
  setEngineAvailability(this.agentEngines, "claude", claudeStatus.installed ? "available" : "unavailable");
70994
71536
  const settings = ctx.get("settings");
70995
- if (settings !== void 0) this.engineSettings = settings.register("freecodego-harness", z2.intersect([
70996
- z2.object({
70997
- defaultModel: z2.string().default(config.defaultModel ?? ""),
70998
- defaultEngine: z2.string().default(config.defaultEngine ?? "deepseek"),
70999
- mediaDefaults: z2.object({
71000
- image: z2.string().default(""),
71001
- video: z2.string().default(""),
71002
- audio: z2.string().default("")
71003
- }).default({
71004
- image: "",
71005
- video: "",
71006
- audio: ""
71007
- })
71008
- }),
71009
- FreeCodeGoCapabilitySettingsSchema,
71010
- FreeCodeGoPluginConflictSettingsSchema,
71011
- FreeCodeGoPluginUpdateSettingsSchema,
71012
- FreeCodeGoEngineeringSettingsSchema,
71013
- FreeCodeGoAutomationSettingsSchema,
71014
- FreeCodeGoGuardSettingsSchema,
71015
- FreeCodeGoTrustSettingsSchema,
71016
- z2.object({
71017
- /**
71018
- * Sandbox profile deny globs, enforced in the plugin policy layer.
71019
- *
71020
- * Empty by default: a deny list is containment the user asked for, and
71021
- * inventing one would refuse paths nobody named.
71022
- */
71023
- sandboxDenyPatterns: z2.array(z2.string()).default([])
71024
- }),
71025
- z2.object({
71026
- rehydrationEnabled: z2.boolean().default(true),
71027
- /**
71028
- * Conversation-arc rehydration (goals + decisions folded from the
71029
- * session's own goal events, promoted memories, and announced
71030
- * choices). Off by default: it changes what the post-compaction
71031
- * message contains, and a user must opt into new prompt surface —
71032
- * the mechanical fold is bounded, but its wording is still model-
71033
- * visible behavior.
71034
- */
71035
- rehydrationArcEnabled: z2.boolean().default(false)
71036
- }),
71037
- z2.object({
71038
- advisorEnabled: z2.boolean().default(config.autoAdvisorEnabled !== false),
71039
- advisorMode: z2.union([
71040
- z2.const("async"),
71041
- z2.const("catchup"),
71042
- z2.const("blocker-only")
71043
- ]).default("async"),
71044
- advisorProvider: z2.string().default("opencode"),
71045
- advisorModel: z2.string().default("auto"),
71046
- advisorAllowAgentControl: z2.boolean().default(true),
71047
- advisorInterruptCooldownTurns: z2.number().step(1).min(0).max(20).default(3),
71048
- advisorMemoryDraftsEnabled: z2.boolean().default(true),
71049
- reviewMode: z2.union([
71050
- z2.const("off"),
71051
- z2.const("record"),
71052
- z2.const("gate")
71053
- ]).default("off"),
71054
- reviewThreshold: z2.union([
71055
- z2.const("critical"),
71056
- z2.const("high"),
71057
- z2.const("medium"),
71058
- z2.const("low")
71059
- ]).default("high"),
71060
- reviewCooldownTurns: z2.number().step(1).min(0).max(20).default(3),
71061
- reviewDeep: z2.boolean().default(false),
71062
- reviewEscalation: z2.boolean().default(false),
71063
- headroomEnabled: z2.boolean().default(true),
71064
- headroomThresholdChars: z2.number().step(1).min(256).max(1e6).default(1200),
71065
- headroomMinSavingsRatio: z2.number().min(0.05).max(0.95).default(0.85),
71066
- headroomDedupEnabled: z2.boolean().default(true),
71067
- /**
71068
- * Tools to protect *in addition* to the built-in list.
71069
- *
71070
- * Additional, not instead of: the built-in set
71071
- * (`DEFAULT_EXCLUDE_TOOLS`) is a safety guarantee — those outputs are
71072
- * what the model byte-patches against — and this field defaults to an
71073
- * empty array, so a reading where it replaced the built-ins would make
71074
- * the default install protect nothing.
71075
- */
71076
- headroomExcludeTools: z2.array(z2.string()).default([]),
71077
- headroomFoldReads: z2.boolean().default(false),
71078
- /**
71079
- * Skeletonize large source-file reads: retained lines stay byte-exact
71080
- * while whole body runs collapse to a marker. On by default because
71081
- * `read` is the largest single source of re-sent context; the original
71082
- * is always retrievable through `headroom_retrieve`.
71083
- */
71084
- headroomCodeSkeletonEnabled: z2.boolean().default(true),
71085
- /**
71086
- * Reversible-render competition policy. `reversible` (default) delivers a
71087
- * render that clears `FOLD_DECISIVE_RATIO` on sight — a Stage 2 fold, or a
71088
- * mixed-content splice; `max` demotes every one of them to a candidate so
71089
- * the compressor written for the payload's shape is always asked first.
71090
- * The two settings are two products, not a fine-tuning — see
71091
- * `HeadroomRuntime.foldPolicy` for the measured difference on the shapes
71092
- * where they disagree.
71093
- */
71094
- headroomFoldPolicy: z2.union([z2.const("reversible"), z2.const("max")]).default("reversible"),
71095
- /**
71096
- * Keep task-specific tool schemas out of the request until the model
71097
- * asks for them with `tool_search`. Measured saving is ~3.2k tokens per
71098
- * request on this plugin's own 37 tools alone.
71099
- */
71100
- deferredToolSchemasEnabled: z2.boolean().default(true),
71101
- /** Explicit deferral list; when empty the plugin-owned prefix rule applies. */
71102
- deferredToolNames: z2.array(z2.string()).default([]),
71103
- headroomOutputShaper: z2.boolean().default(false),
71104
- headroomVerbosityLevel: z2.number().step(1).min(0).max(4).default(0)
71105
- }),
71106
- z2.object({
71107
- /**
71108
- * Memory consolidation rollout (`memory/rollout.ts`).
71109
- *
71110
- * A stage rather than a switch because consolidating runs a model over
71111
- * the session's successful turns: `record_only` captures without
71112
- * consolidating, `shadow` consolidates without committing, and only
71113
- * `active` writes. Off by default, and off is where an unrecognised
71114
- * value lands too — see `resolveMemoryRollout`, which fails closed
71115
- * rather than falling back to the legacy search path.
71116
- */
71117
- memoryRollout: z2.union([
71118
- z2.const("off"),
71119
- z2.const("record_only"),
71120
- z2.const("shadow"),
71121
- z2.const("active")
71122
- ]).default("off")
71123
- })
71124
- ]), { applies: "live" });
71125
- else this.engineSettings = void 0;
71126
- this.policy = new FreeCodeGoPolicy(this.engineSettings);
71537
+ const settingsWriter = settings === void 0 ? void 0 : { update: async (patch) => {
71538
+ const entry = this.ctx.fiber.entry;
71539
+ if (entry === void 0) return;
71540
+ await settings.update(entry.options.id, patch);
71541
+ } };
71542
+ this.ctx.inject(["settings"], (child) => {
71543
+ child.effect(() => child.settings.configure({ auto: false }, this.ctx.fiber));
71544
+ });
71545
+ this.policy = new FreeCodeGoPolicy(this.config, settingsWriter);
71127
71546
  this.capabilities = new FreeCodeGoCapabilityRegistry(ctx, this.policy, (directory) => this.projectScopeTrust(directory), async () => {
71128
71547
  const tier = await this.projectTierFor(process.cwd()).catch(() => void 0);
71129
71548
  return {
@@ -71435,10 +71854,7 @@ var FreeCodeGoHarnessPlugin = (() => {
71435
71854
  const failed = result.stages.filter((stage) => stage.state === "fail" || stage.state === "unavailable");
71436
71855
  if (failed.length === 0) return;
71437
71856
  agent.inject(createUserMessage({
71438
- source: {
71439
- kind: "plugin",
71440
- plugin: "freecodego-engineering-loop"
71441
- },
71857
+ source: { kind: "freecodego-engineering-loop" },
71442
71858
  content: [{
71443
71859
  type: "text",
71444
71860
  text: `Engineering goal "${goalId}" ended in phase "${phase}", but verification did not pass: ${failed.map((stage) => `${stage.id}=${stage.state}`).join(", ")}. Resolve these before treating the plan as done.`
@@ -71450,10 +71866,7 @@ var FreeCodeGoHarnessPlugin = (() => {
71450
71866
  if (loop.phase === "blocked") {
71451
71867
  const budget = loop.roundsStarted === void 0 || loop.maxGoalRounds === void 0 ? "" : ` after ${loop.roundsStarted}/${loop.maxGoalRounds} goal rounds`;
71452
71868
  agent.inject(createUserMessage({
71453
- source: {
71454
- kind: "plugin",
71455
- plugin: "freecodego-engineering-loop"
71456
- },
71869
+ source: { kind: "freecodego-engineering-loop" },
71457
71870
  content: [{
71458
71871
  type: "text",
71459
71872
  text: `Engineering goal "${goalId}" stopped unattended${budget}: ${loop.blockedMessage ?? loop.blockedCode ?? "the round driver blocked it"}. Nothing continues until that is resolved; use \u5DE5\u7A0B\u589E\u5F3A \u2192 \u65E0\u4EBA\u503C\u5B88\u5DE5\u7A0B\u56DE\u8DEF \u2192 \u7EE7\u7EED to resume it.`
@@ -71565,9 +71978,13 @@ var FreeCodeGoHarnessPlugin = (() => {
71565
71978
  this.catalogs.registerNvidiaAdapter();
71566
71979
  this.catalogs.registerKiloAdapter();
71567
71980
  this.catalogs.prewarmProviderCatalogs();
71981
+ this.repairWebSearchBinding();
71568
71982
  this.catalogs.refreshLogfareHealthInBackground();
71569
- this.registerAgnesTools();
71570
- this.registerMediaTools();
71983
+ this.syncMediaGenerationTools();
71984
+ ctx.effect(() => this.registerAudioTools(), "freecodego: media audio tools");
71985
+ ctx.effect(() => () => {
71986
+ this.disposeMediaGenerationTools();
71987
+ }, "freecodego: media generation tools");
71571
71988
  ctx.effect(() => async () => {
71572
71989
  await Promise.all([this.pendingWrites.drain(), this.catalogs.pendingWrites.drain()]);
71573
71990
  }, "freecodego: background preset and catalog writes");
@@ -71612,10 +72029,7 @@ var FreeCodeGoHarnessPlugin = (() => {
71612
72029
  const agent = agents.get(agentId);
71613
72030
  if (typeof agent?.inject !== "function") return;
71614
72031
  agent.inject(createUserMessage({
71615
- source: {
71616
- kind: "plugin",
71617
- plugin: "freecodego-verify-on-stop"
71618
- },
72032
+ source: { kind: "freecodego-verify-on-stop" },
71619
72033
  content: [{
71620
72034
  type: "text",
71621
72035
  text: `<verify-on-stop>
@@ -71701,10 +72115,7 @@ ${text2}
71701
72115
  const agent = agents.get(agentId);
71702
72116
  if (typeof agent?.inject !== "function") return false;
71703
72117
  agent.inject(createUserMessage({
71704
- source: {
71705
- kind: "plugin",
71706
- plugin: "freecodego-review"
71707
- },
72118
+ source: { kind: "freecodego-review" },
71708
72119
  content: [{
71709
72120
  type: "text",
71710
72121
  text: `<stop-time-review>
@@ -72225,6 +72636,32 @@ ${text2}
72225
72636
  await settings.update({ deferredToolSchemasEnabled: enabled });
72226
72637
  return this.deferredTools.status();
72227
72638
  }
72639
+ /**
72640
+ * Read the image/video generation switch and the tool names it governs.
72641
+ *
72642
+ * Two name lists, because "what this switch owns" and "what is mounted right now"
72643
+ * are different facts: the first comes from the registration modules' single
72644
+ * declarations, the second from the registrations themselves. Reported together so a
72645
+ * panel can say which tools a profile actually has without restating either list.
72646
+ * @returns the media Generation Status.
72647
+ */
72648
+ mediaGenerationStatus() {
72649
+ return {
72650
+ enabled: this.policy.get()?.mediaGenerationEnabled !== false,
72651
+ gated: [...Object.values(MEDIA_GENERATION_TOOL_NAMES), ...Object.values(AGNES_MEDIA_TOOL_NAMES)],
72652
+ registered: this.mediaGenerationTools?.names ?? []
72653
+ };
72654
+ }
72655
+ /**
72656
+ * Mount or unmount the image and video generation tools.
72657
+ * @param enabled - whether the capability is switched on.
72658
+ * @returns the media Generation Status.
72659
+ */
72660
+ async mediaGenerationSetEnabled(enabled) {
72661
+ await this.policy.update({ mediaGenerationEnabled: enabled });
72662
+ this.syncMediaGenerationTools();
72663
+ return this.mediaGenerationStatus();
72664
+ }
72228
72665
  /** Probe-and-mount status for the optional LSP stack (fail-soft).
72229
72666
  * @returns the probe-and-mount state of the optional LSP stack.
72230
72667
  */
@@ -73496,10 +73933,7 @@ ${detail.body}`
73496
73933
  const outcome = this.personaRuns.complete(childId, produced);
73497
73934
  if (outcome?.message === void 0) return;
73498
73935
  outcome.parent?.inject?.(createUserMessage({
73499
- source: {
73500
- kind: "plugin",
73501
- plugin: "freecodego-persona"
73502
- },
73936
+ source: { kind: "freecodego-persona" },
73503
73937
  content: [{
73504
73938
  type: "text",
73505
73939
  text: `[freecodego] ${outcome.message}`
@@ -73746,10 +74180,7 @@ ${detail.body}`
73746
74180
  })).agent;
73747
74181
  if (child?.followup === void 0) throw new Error("the child agent was created but cannot be sent its brief; it was not given one");
73748
74182
  child.followup(createUserMessage({
73749
- source: {
73750
- kind: "plugin",
73751
- plugin: "freecodego-persona"
73752
- },
74183
+ source: { kind: "freecodego-persona" },
73753
74184
  content: [{
73754
74185
  type: "text",
73755
74186
  text: input.session.brief
@@ -74333,10 +74764,7 @@ ${detail.body}`
74333
74764
  if (!plan.changed) return;
74334
74765
  if (typeof agent?.inject !== "function") return;
74335
74766
  agent.inject(createUserMessage({
74336
- source: {
74337
- kind: "plugin",
74338
- plugin: "freecodego-context-budget"
74339
- },
74767
+ source: { kind: "freecodego-context-budget" },
74340
74768
  content: [{
74341
74769
  type: "text",
74342
74770
  text: `<context-budget>
@@ -74373,10 +74801,7 @@ ${plan.text}
74373
74801
  if (fragments.length === 0) return;
74374
74802
  if (typeof agent.inject !== "function") return;
74375
74803
  agent.inject(createUserMessage({
74376
- source: {
74377
- kind: "plugin",
74378
- plugin: "freecodego-plan-mode"
74379
- },
74804
+ source: { kind: "freecodego-plan-mode" },
74380
74805
  content: [{
74381
74806
  type: "text",
74382
74807
  text: renderFragments(fragments)
@@ -74682,6 +75107,19 @@ ${plan.text}
74682
75107
  async sessionDelete(sessionId) {
74683
75108
  return sessionDelete(this.engineRemotesHost, sessionId);
74684
75109
  }
75110
+ /** Resolve one of this plugin's models as the DeepSeek web-search provider.
75111
+ * @param input - the provider and the model the user picked.
75112
+ * @returns the endpoint, model, and credential the search page must use.
75113
+ */
75114
+ async webSearchBind(input) {
75115
+ return webSearchBind(this.engineRemotesHost, input);
75116
+ }
75117
+ /** Report whether the saved web-search binding is this plugin's, and whether it still answers.
75118
+ * @returns the state the search page renders from.
75119
+ */
75120
+ async webSearchBindingStatus() {
75121
+ return webSearchBindingStatus(this.webSearchBindingReads());
75122
+ }
74685
75123
  /** Return the current cross-engine MCP and Skill capability inventory.
74686
75124
  * @returns the capability snapshot the Host reports.
74687
75125
  */
@@ -75135,6 +75573,22 @@ ${plan.text}
75135
75573
  return sendVerifyCode(this.accountRemotesHost, email);
75136
75574
  }
75137
75575
  /**
75576
+ * Mail the password-reset code for an existing account.
75577
+ * @param email - the address the reset code is sent to.
75578
+ * @returns whether the gateway accepted the request.
75579
+ */
75580
+ async forgotPassword(email) {
75581
+ return forgotPassword(this.accountRemotesHost, email);
75582
+ }
75583
+ /**
75584
+ * Replace a password using the emailed reset code.
75585
+ * @param input - the address, the emailed code, and the new password.
75586
+ * @returns whether the gateway accepted the new password.
75587
+ */
75588
+ async resetPassword(input) {
75589
+ return resetPassword(this.accountRemotesHost, input);
75590
+ }
75591
+ /**
75138
75592
  * Sign in with a password, keeping the issued tokens in the Host vault.
75139
75593
  * @param input - credentials and the remember-me intent of this attempt.
75140
75594
  * @returns the account snapshot after the sign-in.
@@ -75905,10 +76359,6 @@ ${plan.text}
75905
76359
  });
75906
76360
  if (dispose !== void 0) this.ctx.effect(() => dispose, "freecodego: edit_and_run composite tool");
75907
76361
  }
75908
- /** Model-facing media tools keep Agnes API keys and requests in the Host. */
75909
- registerAgnesTools() {
75910
- registerAgnesTools(this.agentToolsDeps());
75911
- }
75912
76362
  agentToolsDeps() {
75913
76363
  return {
75914
76364
  ...this.coreDeps,
@@ -75921,9 +76371,105 @@ ${plan.text}
75921
76371
  probeCommandPolicy: (agent) => this.projectPolicyForAgent(agent)
75922
76372
  };
75923
76373
  }
75924
- /** Generic media tools resolve the user's live default at execution time. */
75925
- registerMediaTools() {
75926
- registerMediaTools(this.mediaHost);
76374
+ /** The mounted image/video registrations, absent while the switch is off. */
76375
+ mediaGenerationTools;
76376
+ /**
76377
+ * Register the audio generation and transcription tools, which no switch governs.
76378
+ * @returns the disposer that releases both registrations.
76379
+ */
76380
+ registerAudioTools() {
76381
+ return registerAudioTools(this.mediaHost).dispose;
76382
+ }
76383
+ /**
76384
+ * Mount or unmount the image and video tools so that they match the setting.
76385
+ *
76386
+ * Remounted from the switch's own Remote rather than read at call time, because a
76387
+ * tool the model can see *is* a registration: gating `execute` instead would leave the
76388
+ * schema in every request, which is the cost the switch exists to remove.
76389
+ */
76390
+ syncMediaGenerationTools() {
76391
+ const enabled = this.policy.get()?.mediaGenerationEnabled !== false;
76392
+ if (enabled === (this.mediaGenerationTools !== void 0)) return;
76393
+ if (!enabled) {
76394
+ this.disposeMediaGenerationTools();
76395
+ return;
76396
+ }
76397
+ const generation = registerGenerationTools(this.mediaHost);
76398
+ const legacy = registerAgnesMediaTools(this.agentToolsDeps());
76399
+ this.mediaGenerationTools = {
76400
+ names: [...generation.names, ...legacy.names],
76401
+ dispose: () => {
76402
+ generation.dispose();
76403
+ legacy.dispose();
76404
+ }
76405
+ };
76406
+ }
76407
+ /**
76408
+ * Rebuild the web-search binding this plugin wrote, when this process cannot serve it.
76409
+ *
76410
+ * Read once at start and never in a request path: a binding that died with the
76411
+ * previous process has to be whole again before the first search runs, and the
76412
+ * search itself must not be the thing that discovers it. Every read and write the
76413
+ * pass needs is injected, so `web-search-binding.ts` owns the rules and this method
76414
+ * owns only where they are satisfied — including the two services whose absence
76415
+ * leaves a binding unbuildable, which is reported rather than half-applied.
76416
+ */
76417
+ async repairWebSearchBinding() {
76418
+ let reads;
76419
+ let report;
76420
+ try {
76421
+ reads = this.webSearchBindingReads();
76422
+ report = await repairWebSearchBinding({
76423
+ ...reads,
76424
+ bind: (input) => webSearchBind(this.engineRemotesHost, input),
76425
+ writeCredential: async (ref, value) => {
76426
+ const credentials = this.credentials;
76427
+ if (credentials === void 0) throw new Error("the credentials service is not mounted, so the web-search key cannot be written");
76428
+ await credentials.set(credentialRef2(ref), value);
76429
+ },
76430
+ writeSection: async (patch) => {
76431
+ const settings = this.ctx.get("settings");
76432
+ if (settings === void 0) throw new Error("the settings service is not mounted, so the web-search section cannot be rewritten");
76433
+ await settings.update(WEB_SEARCH_SETTINGS_NAMESPACE, patch);
76434
+ }
76435
+ });
76436
+ } catch (error) {
76437
+ this.ctx.logger?.warn?.(`FreeCodeGo could not repair the web-search binding: ${error instanceof Error ? error.message : String(error)}`);
76438
+ return;
76439
+ }
76440
+ if (report.outcome === "rebuilt") this.ctx.logger?.info?.(`FreeCodeGo rebuilt the stale web-search binding for ${reads.remembered?.provider ?? ""}/${reads.remembered?.model ?? ""}`);
76441
+ if (report.outcome === "failed") this.ctx.logger?.warn?.(`FreeCodeGo could not rebuild the web-search binding: ${report.detail ?? "unknown failure"}`);
76442
+ }
76443
+ /**
76444
+ * The reads both web-search passes share, taken from this Host's services.
76445
+ *
76446
+ * One accessor rather than two because the answers have to agree: the boot pass
76447
+ * deciding a saved route is dead while the page reads it as alive would rewrite a
76448
+ * section the user is looking at. The remembered pair is read through the policy —
76449
+ * the one seam a settings read goes through — and a composition with no
76450
+ * configuration at all resolves to `undefined` rather than throwing here.
76451
+ */
76452
+ webSearchBindingReads() {
76453
+ const settings = this.ctx.get("settings");
76454
+ const configured = this.policy.get();
76455
+ const provider = asString(configured?.webSearchBindingProvider) ?? "";
76456
+ const model = asString(configured?.webSearchBindingModel) ?? "";
76457
+ return {
76458
+ remembered: provider === "" || model === "" ? void 0 : {
76459
+ provider,
76460
+ model
76461
+ },
76462
+ readSection: async () => {
76463
+ const descriptor = settings?.describe().find((row) => row.ns === WEB_SEARCH_SETTINGS_NAMESPACE);
76464
+ return descriptor === void 0 ? void 0 : maybeRecord(descriptor.value);
76465
+ },
76466
+ servesBridgeRoute: (routeId) => this.claudeBridge.hasRoute(routeId)
76467
+ };
76468
+ }
76469
+ /** Release the image and video registrations, when any are mounted. */
76470
+ disposeMediaGenerationTools() {
76471
+ this.mediaGenerationTools?.dispose();
76472
+ this.mediaGenerationTools = void 0;
75927
76473
  }
75928
76474
  get mediaHost() {
75929
76475
  return {
@@ -76744,13 +77290,12 @@ function hasInstalledClaudeRuntime() {
76744
77290
  // packages/freecodego/bundle-latest/src/index.ts
76745
77291
  var name = "freecodego";
76746
77292
  var inject = [];
76747
- async function apply2(ctx, config = {}) {
76748
- await ctx.plugin(FreeCodeGoHarnessPlugin, config);
77293
+ async function apply2(ctx, _config = {}) {
76749
77294
  await ctx.plugin(FreeCodeGoAgentEngineRouter, {});
76750
77295
  }
76751
77296
  export {
77297
+ FreeCodeGoHarnessPlugin,
76752
77298
  apply2 as apply,
76753
- bootstrapFreeCodeGoHarness,
76754
77299
  inject,
76755
77300
  name,
76756
77301
  registerFreeCodeGoSessionEventTypes