freecodego 0.1.7-rc.2.2 → 0.1.7-rc.2.3

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
@@ -2,9 +2,9 @@
2
2
  import { createRequire as createRequire2 } from "node:module";
3
3
  import path, { basename as basename2, delimiter, 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
4
  import { credentialRef as credentialRef2 } from "@deepseek-ai/dsh-credentials";
5
- import { constants, createCipheriv, createHash as createHash3, createHmac, createPublicKey, publicEncrypt, randomBytes, randomUUID as randomUUID3, timingSafeEqual } from "node:crypto";
5
+ import { constants, createCipheriv, createHash as createHash3, createHmac, createPublicKey, publicEncrypt, randomBytes, randomUUID as randomUUID4, timingSafeEqual } from "node:crypto";
6
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";
7
- import { availableParallelism, homedir, platform as platform2, release, tmpdir } from "node:os";
7
+ import { availableParallelism, homedir, hostname, platform as platform2, release, tmpdir } from "node:os";
8
8
  import { deflateRawSync, gunzipSync, inflateRawSync, inflateSync } from "node:zlib";
9
9
  import { appendFileSync, chmodSync, constants as constants$1, createWriteStream, existsSync as existsSync2, lstatSync, mkdirSync, mkdtempSync, readFileSync as readFileSync2, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
10
10
  import { writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
@@ -16,7 +16,7 @@ import { AttachmentId, requestImageDimensions } from "@deepseek-ai/dsh-attachmen
16
16
  import { EventSourceParserStream } from "eventsource-parser/stream";
17
17
 
18
18
  // packages/freecodego/freecodego-api/lib/index.js
19
- import { createHash } from "node:crypto";
19
+ import { createHash, randomUUID } from "node:crypto";
20
20
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
21
21
  var FreeCodeGoMobileAuthClient = class {
22
22
  /**
@@ -383,11 +383,21 @@ var HarnessFreeCodeGoCredentialVault = class {
383
383
  * it — and erasing the session must not erase what the sign-in form prefills.
384
384
  */
385
385
  passwordRef;
386
+ /**
387
+ * The installation's device identity, kept beside the session pair.
388
+ *
389
+ * It is a separate value rather than a field of the pair because it outlives
390
+ * every rotation: the pair is replaced on each refresh and erased on sign-out,
391
+ * while this is what makes the next sign-out-and-in land on the same device
392
+ * row instead of adding one.
393
+ */
394
+ deviceRef;
386
395
  constructor(credentials, origin) {
387
396
  this.credentials = credentials;
388
397
  const digest2 = createHash("sha256").update(origin).digest("hex").slice(0, 24).toUpperCase();
389
398
  this.ref = credentialRef(`FREECODEGO_SESSION_${digest2}`);
390
399
  this.passwordRef = credentialRef(`FREECODEGO_PASSWORD_${digest2}`);
400
+ this.deviceRef = credentialRef(`FREECODEGO_DEVICE_${digest2}`);
391
401
  }
392
402
  async load(_origin) {
393
403
  const resolved = await this.credentials.resolve(this.ref);
@@ -414,6 +424,22 @@ var HarnessFreeCodeGoCredentialVault = class {
414
424
  async deletePassword(_origin) {
415
425
  await this.credentials.unset(this.passwordRef);
416
426
  }
427
+ /**
428
+ * Read this installation's device identity.
429
+ * @returns the stored identity, or undefined when this machine never named one.
430
+ */
431
+ async loadDeviceId(_origin) {
432
+ const resolved = await this.credentials.resolve(this.deviceRef);
433
+ const value = resolved === void 0 ? "" : resolved.value.trim();
434
+ return value === "" ? void 0 : value;
435
+ }
436
+ /**
437
+ * Persist this installation's device identity.
438
+ * @param deviceId - the identity every later sign-in and rotation presents.
439
+ */
440
+ async saveDeviceId(_origin, deviceId) {
441
+ await this.credentials.set(this.deviceRef, deviceId);
442
+ }
417
443
  };
418
444
  var FreeCodeGoAccountCoordinator = class {
419
445
  auth;
@@ -421,6 +447,21 @@ var FreeCodeGoAccountCoordinator = class {
421
447
  state = { status: "signed-out" };
422
448
  pendingMfaToken;
423
449
  refreshInFlight;
450
+ /**
451
+ * The settled resolution of this installation's device identity.
452
+ *
453
+ * Cached as the promise rather than the value so two concurrent sign-ins
454
+ * cannot each mint an identity and race the vault write; whichever asks first
455
+ * decides, and every later caller reads the same answer.
456
+ */
457
+ deviceIdInFlight;
458
+ /**
459
+ * Whether a rotation has already been spent on a pair that states no device.
460
+ *
461
+ * Spent at most once per process: a gateway that keeps answering with an
462
+ * unbound pair must not make every authorized call rotate the session.
463
+ */
464
+ unboundSessionRenewed = false;
424
465
  /** Set by logout() so an in-flight refresh never writes after the erase. */
425
466
  signingOut = false;
426
467
  /**
@@ -592,13 +633,75 @@ var FreeCodeGoAccountCoordinator = class {
592
633
  * @param signal - aborts the request when the caller cancels.
593
634
  * @returns the account state after the issued tokens were stored.
594
635
  */
636
+ /**
637
+ * This installation's device identity, creating and persisting one on first use.
638
+ *
639
+ * The gateway binds a session to the device that opened it and will not serve a
640
+ * model to a token whose device has no live session, so a sign-in that states
641
+ * no device is a sign-in the gateway later refuses — every model, every turn.
642
+ * The identity is therefore resolved once and reused for the life of the
643
+ * installation, in the same vault as the session pair so a restart keeps it.
644
+ *
645
+ * `fcg-<uuid>` mirrors the identity the vendor's own desktop client presents,
646
+ * which keeps a mixed account's device list readable rather than one scheme per
647
+ * client.
648
+ * @returns the stable device id this installation signs in with.
649
+ */
650
+ async deviceId() {
651
+ if (this.deviceIdInFlight === void 0) this.deviceIdInFlight = this.mintDeviceId();
652
+ return this.deviceIdInFlight;
653
+ }
654
+ /**
655
+ * The identity to sign in with, honoring a device the caller stated.
656
+ *
657
+ * A stated device is an assertion about which device this session belongs to,
658
+ * so it is remembered rather than merely forwarded: the next rotation resolves
659
+ * the installation identity, and rotating a session onto a different device is
660
+ * how a working pair stops being accepted.
661
+ * @param statedDeviceId - the device the caller named, if any.
662
+ * @returns the identity to present to the gateway.
663
+ */
664
+ async deviceIdFor(statedDeviceId) {
665
+ const stated = statedDeviceId?.trim() ?? "";
666
+ if (stated === "") return this.deviceId();
667
+ this.deviceIdInFlight = Promise.resolve(stated);
668
+ try {
669
+ await this.vault.saveDeviceId?.(this.auth.origin, stated);
670
+ } catch {
671
+ }
672
+ return stated;
673
+ }
674
+ /**
675
+ * Load the stored identity, or mint one and write it back.
676
+ *
677
+ * Both vault failures are recoverable rather than fatal: an unreadable entry is
678
+ * "no identity yet", and an unwritable one leaves the id live for this process
679
+ * — the session it binds is still usable, it just will not survive a restart.
680
+ * @returns the identity to present, stored when the vault accepts it.
681
+ */
682
+ async mintDeviceId() {
683
+ try {
684
+ const stored = await this.vault.loadDeviceId?.(this.auth.origin);
685
+ if (stored !== void 0) return stored;
686
+ } catch {
687
+ }
688
+ const created = `fcg-${randomUUID()}`;
689
+ try {
690
+ await this.vault.saveDeviceId?.(this.auth.origin, created);
691
+ } catch {
692
+ }
693
+ return created;
694
+ }
595
695
  async login(input, signal) {
596
696
  this.pendingRemember = input.remember;
597
697
  if (input.rememberPassword === void 0) this.pendingPassword = void 0;
598
698
  else if (input.rememberPassword) this.pendingPassword = { keep: input.password };
599
699
  else this.pendingPassword = { forget: true };
600
700
  try {
601
- return await this.consumeLoginResult(await this.auth.login(input, signal));
701
+ return await this.consumeLoginResult(await this.auth.login({
702
+ ...input,
703
+ deviceId: await this.deviceIdFor(input.deviceId)
704
+ }, signal));
602
705
  } catch (error) {
603
706
  this.pendingRemember = void 0;
604
707
  this.pendingPassword = void 0;
@@ -613,7 +716,10 @@ var FreeCodeGoAccountCoordinator = class {
613
716
  async register(input, signal) {
614
717
  this.pendingRemember = input.remember;
615
718
  try {
616
- return await this.consumeLoginResult(await this.auth.register(input, signal));
719
+ return await this.consumeLoginResult(await this.auth.register({
720
+ ...input,
721
+ deviceId: await this.deviceIdFor(input.deviceId)
722
+ }, signal));
617
723
  } catch (error) {
618
724
  this.pendingRemember = void 0;
619
725
  throw error;
@@ -622,12 +728,13 @@ var FreeCodeGoAccountCoordinator = class {
622
728
  /** Complete MFA and store credentials only after the second factor succeeds.
623
729
  * @param tempToken - the MFA challenge token the first factor issued.
624
730
  * @param totpCode - the code the user's authenticator produced.
625
- * @param deviceId - device identifier recorded with the session.
731
+ * @param deviceId - device identifier recorded with the session; defaults to
732
+ * this installation's own, without which the session is not bound to a device.
626
733
  * @param signal - aborts the request when the caller cancels.
627
734
  * @returns the account state after the second factor succeeded.
628
735
  */
629
736
  async login2FA(tempToken, totpCode, deviceId, signal) {
630
- return this.consumeLoginResult(await this.auth.login2FA(tempToken, totpCode, deviceId, signal));
737
+ return this.consumeLoginResult(await this.auth.login2FA(tempToken, totpCode, await this.deviceIdFor(deviceId), signal));
631
738
  }
632
739
  /** Complete the pending Host-owned MFA challenge without exposing its temp token.
633
740
  * @param totpCode - the code the user's authenticator produced.
@@ -704,7 +811,9 @@ var FreeCodeGoAccountCoordinator = class {
704
811
  };
705
812
  }
706
813
  /** Rotate the stored session token; callers reauthenticate after any failure.
707
- * @param deviceId - device identifier recorded with the rotated session.
814
+ * @param deviceId - device identifier recorded with the rotated session;
815
+ * defaults to this installation's own, so a rotation never drops the binding
816
+ * the gateway checks.
708
817
  * @param signal - bounds this caller's wait; the shared rotation keeps running.
709
818
  */
710
819
  async refresh(deviceId, signal) {
@@ -723,7 +832,7 @@ var FreeCodeGoAccountCoordinator = class {
723
832
  this.state = { status: "signed-out" };
724
833
  return;
725
834
  }
726
- const exchange = await this.exchangeRefresh(tokens2.refreshToken, deviceId);
835
+ const exchange = await this.exchangeRefresh(tokens2.refreshToken, await this.deviceIdFor(deviceId));
727
836
  if (!exchange.ok) {
728
837
  if (this.signingOut) throw exchange.error;
729
838
  if (erasableRefreshFailure(exchange.kinds)) {
@@ -786,6 +895,9 @@ var FreeCodeGoAccountCoordinator = class {
786
895
  await this.refresh(void 0, signal);
787
896
  tokens2 = await this.loadTokens();
788
897
  if (tokens2 === void 0) throw new Error("FreeCodeGo authentication is required after token refresh");
898
+ } else if (this.shouldRenewUnboundSession(tokens2.accessToken)) {
899
+ await this.refresh(void 0, signal).catch(() => void 0);
900
+ tokens2 = await this.loadTokens() ?? tokens2;
789
901
  }
790
902
  try {
791
903
  return await operation(tokens2.accessToken, signal);
@@ -801,6 +913,24 @@ var FreeCodeGoAccountCoordinator = class {
801
913
  }
802
914
  }
803
915
  /**
916
+ * Whether to spend this process's one rotation on a pair that states no device.
917
+ *
918
+ * The gateway serves no model to a token whose device has no live session and
919
+ * answers a device-less token with `401`. That answer is not about the token's
920
+ * age, so the expiry check above never fires: every turn fails identically for
921
+ * the whole life of the pair. One rotation repairs it, because refresh() now
922
+ * states this installation's identity — and the replacement is bound, so this
923
+ * answers false from then on.
924
+ * @param accessToken - the stored access token about to be presented.
925
+ * @returns whether an unbound pair should be rotated before this call.
926
+ */
927
+ shouldRenewUnboundSession(accessToken) {
928
+ if (this.unboundSessionRenewed) return false;
929
+ if (!tokenStatesNoDevice(accessToken)) return false;
930
+ this.unboundSessionRenewed = true;
931
+ return true;
932
+ }
933
+ /**
804
934
  * Store a token pair issued by an out-of-band flow (browser OAuth) without
805
935
  * returning it to the caller.
806
936
  *
@@ -948,6 +1078,16 @@ function tokenNeedsRefresh(accessToken) {
948
1078
  return false;
949
1079
  }
950
1080
  }
1081
+ function tokenStatesNoDevice(accessToken) {
1082
+ const parts = accessToken.split(".");
1083
+ if (parts.length !== 3) return false;
1084
+ try {
1085
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
1086
+ return typeof payload.device_id !== "string" || payload.device_id.trim() === "";
1087
+ } catch {
1088
+ return false;
1089
+ }
1090
+ }
951
1091
  function isUnauthorized(error) {
952
1092
  const status = error !== null && typeof error === "object" && "status" in error && typeof error.status === "number" ? error.status : void 0;
953
1093
  if (status !== void 0) return status === 401;
@@ -1280,6 +1420,47 @@ var FreeCodeGoApiClient = class {
1280
1420
  }), "revoke device session");
1281
1421
  return typeof payload.message === "string" ? payload.message.trim() : "";
1282
1422
  }
1423
+ /**
1424
+ * Read the announcements this account may see.
1425
+ *
1426
+ * The backend decides the audience: an announcement's targeting rules are
1427
+ * applied server-side against the account's balance and subscriptions, so a
1428
+ * row that comes back is one this account is meant to read. `read_at` is the
1429
+ * account's own record, not the client's, which is what lets a dismissal made
1430
+ * on one machine hold on the next.
1431
+ * @param request - the request this call projects from, and whether read rows may be left out.
1432
+ * @returns the visible announcements, newest first as the backend ordered them.
1433
+ */
1434
+ async getAnnouncements(request) {
1435
+ const query2 = request.unreadOnly === true ? "?unread_only=1" : "";
1436
+ return array(await this.authorized(`/api/v1/announcements${query2}`, request), "announcements").map((value, index) => {
1437
+ const item = object(value, `announcements[${index}]`);
1438
+ return {
1439
+ id: finiteNumber(item.id, `announcements[${index}].id`),
1440
+ title: string(item.title, `announcements[${index}].title`),
1441
+ content: typeof item.content === "string" ? item.content : "",
1442
+ notifyMode: announcementNotifyMode(item.notify_mode),
1443
+ ...typeof item.read_at === "string" && item.read_at.trim() !== "" ? { readAt: item.read_at.trim() } : {},
1444
+ ...typeof item.starts_at === "string" && item.starts_at.trim() !== "" ? { startsAt: item.starts_at.trim() } : {},
1445
+ ...typeof item.ends_at === "string" && item.ends_at.trim() !== "" ? { endsAt: item.ends_at.trim() } : {},
1446
+ createdAt: typeof item.created_at === "string" ? item.created_at : ""
1447
+ };
1448
+ });
1449
+ }
1450
+ /**
1451
+ * Record that this account read one announcement.
1452
+ *
1453
+ * The backend owns the record, so closing a notice is an acknowledged fact
1454
+ * rather than a client-side memory: the same announcement stays closed on the
1455
+ * account's other machines, and the administrator's read report counts it.
1456
+ * @param request - the request this call projects from, and the announcement to mark.
1457
+ * @returns nothing; the backend's acknowledgement is not rendered.
1458
+ */
1459
+ async markAnnouncementRead(request) {
1460
+ const id = request.announcementId;
1461
+ if (!Number.isFinite(id) || id <= 0) throw new Error("FreeCodeGo announcement id is required");
1462
+ await this.authorized(`/api/v1/announcements/${encodeURIComponent(String(id))}/read`, request, { method: "POST" });
1463
+ }
1283
1464
  /** Revoke every session of the account and return how many the backend revoked.
1284
1465
  * @param request - the request this call projects from.
1285
1466
  * @returns how many sessions the backend revoked.
@@ -1978,6 +2159,9 @@ function freeAccessFlags(...sources) {
1978
2159
  function channelMonitorStatus(value) {
1979
2160
  return value === "operational" || value === "degraded" || value === "failed" || value === "error" ? value : "unknown";
1980
2161
  }
2162
+ function announcementNotifyMode(value) {
2163
+ return value === "popup" ? "popup" : "silent";
2164
+ }
1981
2165
  function normalizeBaseUrl(value, allowInsecureLocalhost) {
1982
2166
  let url;
1983
2167
  try {
@@ -2030,7 +2214,7 @@ import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
2030
2214
  // packages/freecodego/native-runtime-host/lib/index.js
2031
2215
  import { createRequire } from "node:module";
2032
2216
  import { spawn } from "node:child_process";
2033
- import { createHash as createHash2, randomUUID } from "node:crypto";
2217
+ import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
2034
2218
 
2035
2219
  // packages/freecodego/native-runtime-protocol/lib/index.js
2036
2220
  var REDACTED = "<redacted>";
@@ -3096,7 +3280,7 @@ var NativeRuntimeHost = class {
3096
3280
  return Promise.reject(error instanceof Error ? error : new Error(String(error)));
3097
3281
  }
3098
3282
  this.start();
3099
- const id = randomUUID();
3283
+ const id = randomUUID2();
3100
3284
  const request = {
3101
3285
  id,
3102
3286
  method,
@@ -4752,7 +4936,7 @@ async function openCodexRootRuntime(runtime, options) {
4752
4936
  }
4753
4937
 
4754
4938
  // packages/freecodego/runtime-claude/lib/index.mjs
4755
- import { randomUUID as randomUUID2 } from "node:crypto";
4939
+ import { randomUUID as randomUUID3 } from "node:crypto";
4756
4940
  import { mkdir as mkdir3, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "node:fs/promises";
4757
4941
  import { join as join3 } from "node:path";
4758
4942
  import { createSdkMcpServer, query, tool } from "@anthropic-ai/claude-agent-sdk";
@@ -5124,7 +5308,7 @@ var DirectClaudeSdkSession = class DirectClaudeSdkSession2 {
5124
5308
  }
5125
5309
  static async open(runtime, options) {
5126
5310
  await mkdir3(runtime.stateDirectory, { recursive: true });
5127
- const session = new DirectClaudeSdkSession2(runtime, options, options.nativeSessionId ?? randomUUID2());
5311
+ const session = new DirectClaudeSdkSession2(runtime, options, options.nativeSessionId ?? randomUUID3());
5128
5312
  await options.prepare?.(async () => void 0);
5129
5313
  return session;
5130
5314
  }
@@ -5196,7 +5380,7 @@ var DirectClaudeSdkSession = class DirectClaudeSdkSession2 {
5196
5380
  behavior: "deny",
5197
5381
  message: sandboxDenial
5198
5382
  };
5199
- const requestId = randomUUID2();
5383
+ const requestId = randomUUID3();
5200
5384
  const pending = this.pending(this.pendingPermissions, requestId);
5201
5385
  this.event("permission/requested", {
5202
5386
  requestId,
@@ -5315,7 +5499,7 @@ var DirectClaudeSdkSession = class DirectClaudeSdkSession2 {
5315
5499
  return promise.promise;
5316
5500
  }
5317
5501
  async askUser(questions) {
5318
- const requestId = randomUUID2();
5502
+ const requestId = randomUUID3();
5319
5503
  const pending = this.pending(this.pendingQuestions, requestId);
5320
5504
  this.event("question/requested", {
5321
5505
  requestId,
@@ -5324,7 +5508,7 @@ var DirectClaudeSdkSession = class DirectClaudeSdkSession2 {
5324
5508
  return await pending;
5325
5509
  }
5326
5510
  async bridgeCall(bridge, op, input) {
5327
- const requestId = randomUUID2();
5511
+ const requestId = randomUUID3();
5328
5512
  const pending = this.pending(this.pendingBridges, requestId);
5329
5513
  this.event("bridge/requested", {
5330
5514
  requestId,
@@ -9680,7 +9864,7 @@ var PlanFileStore = class {
9680
9864
  async write(sessionId, body) {
9681
9865
  const file = planFilePath(sessionId);
9682
9866
  await mkdir4(dirname2(file), { recursive: true });
9683
- const temporary = `${file}.${randomUUID3()}.tmp`;
9867
+ const temporary = `${file}.${randomUUID4()}.tmp`;
9684
9868
  await writeFile3(temporary, body.endsWith("\n") ? body : `${body}
9685
9869
  `, "utf8");
9686
9870
  await rename3(temporary, file);
@@ -12726,6 +12910,11 @@ function mediaCategoryForManagedModel(model) {
12726
12910
  if (protocol === "audio_speech" || protocol === "audio-speech" || protocol === "audio_transcription" || protocol === "audio-transcription") return "audio";
12727
12911
  return inferMediaCategory(`${model.id} ${model.displayName}`);
12728
12912
  }
12913
+ function isTextConversationRoute(model, overrides = {}) {
12914
+ const configured = overrides[mediaCategoryOverrideKey(model.provider, model.id)];
12915
+ if (configured !== void 0) return configured === "text";
12916
+ return mediaCategoryForManagedModel(model) === void 0;
12917
+ }
12729
12918
  function logfareMediaCategory(model) {
12730
12919
  const endpoints = model.endpoints.join(" ").toLowerCase();
12731
12920
  if (endpoints.includes("image")) return "image";
@@ -17018,7 +17207,7 @@ function cosyBuildPayload(info) {
17018
17207
  cosyVersion: COSY_VERSION,
17019
17208
  ideVersion: "",
17020
17209
  info,
17021
- requestId: randomUUID3(),
17210
+ requestId: randomUUID4(),
17022
17211
  version: "v1"
17023
17212
  }), "utf8").toString("base64");
17024
17213
  }
@@ -17288,12 +17477,12 @@ function buildQoderChatBody(input, identity, defaults = {}) {
17288
17477
  }
17289
17478
  const prompt = lastUserPrompt(input.messages);
17290
17479
  const maxTokens = input.maxTokens !== void 0 && input.maxTokens > 0 ? input.maxTokens : defaults.defaultMaxTokens ?? 32768;
17291
- const requestId = randomUUID3();
17292
- const businessId = randomUUID3();
17480
+ const requestId = randomUUID4();
17481
+ const businessId = randomUUID4();
17293
17482
  const title = prompt.length > 30 ? prompt.slice(0, 30) : prompt;
17294
17483
  return {
17295
17484
  request_id: requestId,
17296
- request_set_id: randomUUID3(),
17485
+ request_set_id: randomUUID4(),
17297
17486
  chat_record_id: requestId,
17298
17487
  stream: true,
17299
17488
  chat_task: "FREE_INPUT",
@@ -17320,7 +17509,7 @@ function buildQoderChatBody(input, identity, defaults = {}) {
17320
17509
  image_urls: null,
17321
17510
  is_reply: true,
17322
17511
  is_retry: false,
17323
- session_id: randomUUID3(),
17512
+ session_id: randomUUID4(),
17324
17513
  code_language: "",
17325
17514
  source: 1,
17326
17515
  version: "3",
@@ -20793,7 +20982,9 @@ var FreeCodeGoManagedCatalogs = class {
20793
20982
  },
20794
20983
  inputModalities: imageInputModalities(model.id, model.displayName)
20795
20984
  });
20796
- const options = managed?.models.flatMap((model) => {
20985
+ const modelCategories = this.deps.settings()?.get()?.modelCategories ?? {};
20986
+ const chatModels = (managed?.models ?? []).filter((model) => isTextConversationRoute(model, modelCategories));
20987
+ const options = chatModels.flatMap((model) => {
20797
20988
  const groupedChoices = model.choices.filter((choice) => choice.groupId !== void 0);
20798
20989
  return groupedChoices.length === 0 ? [] : [{
20799
20990
  model: model.id,
@@ -20813,7 +21004,7 @@ var FreeCodeGoManagedCatalogs = class {
20813
21004
  }))
20814
21005
  }];
20815
21006
  }) ?? [];
20816
- const rows = expandGroupPinnedModels(managed?.models.map(availabilityRow) ?? [], options).map((model) => {
21007
+ const rows = expandGroupPinnedModels(chatModels.map(availabilityRow), options).map((model) => {
20817
21008
  const pinned = model.__groupLabel !== void 0;
20818
21009
  const name2 = pinned ? `${model.displayName} \xB7 ${model.__groupLabel}` : model.displayName;
20819
21010
  const groupBlock = modelRowGroupBlock(model);
@@ -23022,7 +23213,7 @@ var ClaudeProtocolBridge = class {
23022
23213
  async endpoint(provider, model, reasoningEffort) {
23023
23214
  await this.start();
23024
23215
  this.pruneRoutes();
23025
- const id = randomUUID3();
23216
+ const id = randomUUID4();
23026
23217
  this.routes.set(id, {
23027
23218
  provider,
23028
23219
  model,
@@ -23042,7 +23233,7 @@ var ClaudeProtocolBridge = class {
23042
23233
  async openAIEndpoint(provider, model) {
23043
23234
  await this.start();
23044
23235
  this.pruneRoutes();
23045
- const id = randomUUID3();
23236
+ const id = randomUUID4();
23046
23237
  this.routes.set(id, {
23047
23238
  provider,
23048
23239
  model,
@@ -23146,7 +23337,7 @@ var ClaudeProtocolBridge = class {
23146
23337
  writeEvent(writer, "message_start", {
23147
23338
  type: "message_start",
23148
23339
  message: {
23149
- id: `msg_${randomUUID3()}`,
23340
+ id: `msg_${randomUUID4()}`,
23150
23341
  type: "message",
23151
23342
  role: "assistant",
23152
23343
  model,
@@ -23170,7 +23361,7 @@ var ClaudeProtocolBridge = class {
23170
23361
  index,
23171
23362
  content_block: {
23172
23363
  type: "tool_use",
23173
- id: call.id ?? `tool_${randomUUID3()}`,
23364
+ id: call.id ?? `tool_${randomUUID4()}`,
23174
23365
  name: call.name ?? "tool",
23175
23366
  input: {}
23176
23367
  }
@@ -23353,8 +23544,8 @@ var ClaudeProtocolBridge = class {
23353
23544
  connection: "keep-alive"
23354
23545
  });
23355
23546
  const writer = new StreamWriter(res);
23356
- const responseId = `resp_${randomUUID3()}`;
23357
- const itemId = `msg_${randomUUID3()}`;
23547
+ const responseId = `resp_${randomUUID4()}`;
23548
+ const itemId = `msg_${randomUUID4()}`;
23358
23549
  writeData(writer, {
23359
23550
  type: "response.created",
23360
23551
  response: {
@@ -23416,7 +23607,7 @@ var ClaudeProtocolBridge = class {
23416
23607
  if (call === void 0) {
23417
23608
  call = {
23418
23609
  itemIndex: nextCall++,
23419
- itemId: `fc_${randomUUID3()}`,
23610
+ itemId: `fc_${randomUUID4()}`,
23420
23611
  id: "",
23421
23612
  name: chunk.name ?? "tool",
23422
23613
  args: "",
@@ -23790,7 +23981,7 @@ async function anthropicMessages(value, resolve3) {
23790
23981
  if (part.type === "tool_use") {
23791
23982
  blocks.push({
23792
23983
  type: "tool-call",
23793
- id: callId$1(String(part.id ?? randomUUID3())),
23984
+ id: callId$1(String(part.id ?? randomUUID4())),
23794
23985
  name: String(part.name ?? "tool"),
23795
23986
  arguments: JSON.stringify(part.input ?? {})
23796
23987
  });
@@ -23880,7 +24071,7 @@ function toNonStreamingResponse(chunks, model) {
23880
24071
  });
23881
24072
  for (const call of accumulateToolCalls(chunks)) blocks.push({
23882
24073
  type: "tool_use",
23883
- id: call.id !== "" ? call.id : `tool_${randomUUID3()}`,
24074
+ id: call.id !== "" ? call.id : `tool_${randomUUID4()}`,
23884
24075
  name: call.name,
23885
24076
  input: parseArguments(call.args)
23886
24077
  });
@@ -23888,7 +24079,7 @@ function toNonStreamingResponse(chunks, model) {
23888
24079
  const usage2 = chunks.find((chunk) => chunk.type === "usage")?.usage;
23889
24080
  const stopReason2 = finish?.reason.kind === "tool-calls" ? "tool_use" : finish?.reason.kind === "max-tokens" ? "max_tokens" : finish?.reason.kind === "error" ? "stop_sequence" : "end_turn";
23890
24081
  return {
23891
- id: `msg_${randomUUID3()}`,
24082
+ id: `msg_${randomUUID4()}`,
23892
24083
  type: "message",
23893
24084
  role: "assistant",
23894
24085
  model,
@@ -23904,7 +24095,7 @@ function toNonStreamingResponse(chunks, model) {
23904
24095
  function toResponsesObject(chunks, model) {
23905
24096
  const text2 = chunks.filter((chunk) => chunk.type === "text-delta").map((chunk) => chunk.text).join("");
23906
24097
  const output = [{
23907
- id: `msg_${randomUUID3()}`,
24098
+ id: `msg_${randomUUID4()}`,
23908
24099
  type: "message",
23909
24100
  status: "completed",
23910
24101
  role: "assistant",
@@ -23917,10 +24108,10 @@ function toResponsesObject(chunks, model) {
23917
24108
  for (const call of accumulateToolCalls(chunks)) {
23918
24109
  hasCalls = true;
23919
24110
  output.push({
23920
- id: `fc_${randomUUID3()}`,
24111
+ id: `fc_${randomUUID4()}`,
23921
24112
  type: "function_call",
23922
24113
  status: "completed",
23923
- call_id: call.id !== "" ? call.id : `call_${randomUUID3()}`,
24114
+ call_id: call.id !== "" ? call.id : `call_${randomUUID4()}`,
23924
24115
  name: call.name,
23925
24116
  arguments: call.args
23926
24117
  });
@@ -23928,7 +24119,7 @@ function toResponsesObject(chunks, model) {
23928
24119
  const finish = chunks.find((chunk) => chunk.type === "finish");
23929
24120
  const usage2 = chunks.find((chunk) => chunk.type === "usage")?.usage;
23930
24121
  return {
23931
- id: `resp_${randomUUID3()}`,
24122
+ id: `resp_${randomUUID4()}`,
23932
24123
  object: "response",
23933
24124
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
23934
24125
  status: hasCalls || finish?.reason.kind !== "error" ? "completed" : "failed",
@@ -24688,7 +24879,7 @@ var GraphifyRuntimeManager = class {
24688
24879
  if (input.packageId === "existing-python" && !existsSync2(resolve2(requestedPython))) throw new Error("\u6307\u5B9A\u7684 Python \u53EF\u6267\u884C\u6587\u4EF6\u4E0D\u5B58\u5728\u3002");
24689
24880
  await this.ensurePrivateDirectory(this.rootDirectory);
24690
24881
  const uv = await this.ensureUv();
24691
- const staging = join4(this.rootDirectory, `.install-${randomUUID3()}`);
24882
+ const staging = join4(this.rootDirectory, `.install-${randomUUID4()}`);
24692
24883
  await rm2(staging, {
24693
24884
  recursive: true,
24694
24885
  force: true
@@ -24826,8 +25017,8 @@ var GraphifyRuntimeManager = class {
24826
25017
  await this.ensurePrivateDirectory(tools);
24827
25018
  const executable = platform2() === "win32" ? "uv.exe" : "uv";
24828
25019
  const target = join4(tools, executable);
24829
- const staged = `${target}.${randomUUID3()}`;
24830
- const archivePath = join4(tools, `.uv-archive-${randomUUID3()}`);
25020
+ const staged = `${target}.${randomUUID4()}`;
25021
+ const archivePath = join4(tools, `.uv-archive-${randomUUID4()}`);
24831
25022
  await downloadVerifiedAsset({
24832
25023
  url: archive.url,
24833
25024
  digest: archive.digest,
@@ -26113,7 +26304,7 @@ var FreeCodeGoCapabilityRegistry = class {
26113
26304
  if (!this.configuration().mcpEnabled) throw new Error("MCP is disabled in FreeCodeGo settings");
26114
26305
  if (!name2.startsWith("mcp__") || !this.mcpTools(agent).some((tool2) => tool2.name === name2)) throw new Error(`MCP tool "${name2}" is not active`);
26115
26306
  const result = await this.ctx.tools.execute({
26116
- callId: callId2(`freecodego-mcp-${randomUUID3()}`),
26307
+ callId: callId2(`freecodego-mcp-${randomUUID4()}`),
26117
26308
  name: name2,
26118
26309
  arguments: args,
26119
26310
  ...agent === void 0 ? {} : { agent },
@@ -26142,7 +26333,7 @@ var FreeCodeGoCapabilityRegistry = class {
26142
26333
  async executeHarnessTool(agent, name2, args, signal, options = {}) {
26143
26334
  if (!this.harnessTools(agent).some((tool2) => tool2.name === name2)) throw new Error(`Harness tool "${name2}" is not available to this native Agent`);
26144
26335
  const result = await this.ctx.tools.execute({
26145
- callId: callId2(`freecodego-harness-${randomUUID3()}`),
26336
+ callId: callId2(`freecodego-harness-${randomUUID4()}`),
26146
26337
  name: name2,
26147
26338
  arguments: args,
26148
26339
  ...agent === void 0 ? {} : { agent },
@@ -26551,7 +26742,7 @@ function restoreRedactedMcpSecrets(input, previous) {
26551
26742
  }
26552
26743
  function normalizeMcpServer(input) {
26553
26744
  const server = {
26554
- id: input.id?.trim() || randomUUID3(),
26745
+ id: input.id?.trim() || randomUUID4(),
26555
26746
  enabled: input.enabled,
26556
26747
  transport: input.transport,
26557
26748
  serverName: input.serverName.trim(),
@@ -26570,7 +26761,7 @@ function normalizeSkillRoot(input) {
26570
26761
  if (configuredPath === "") throw new Error("Skill root path is required");
26571
26762
  if (!isAbsolute2(configuredPath)) throw new Error("Skill root path must be absolute");
26572
26763
  return {
26573
- id: input.id?.trim() || randomUUID3(),
26764
+ id: input.id?.trim() || randomUUID4(),
26574
26765
  enabled: input.enabled,
26575
26766
  path: resolve2(configuredPath)
26576
26767
  };
@@ -28024,7 +28215,7 @@ async function preparePendingUpdateStartup(profile, marker) {
28024
28215
  }
28025
28216
  async function writePendingUpdateMarker(profile, marker) {
28026
28217
  const file = pendingUpdateMarkerPath(profile);
28027
- const temporary = `${file}.${randomUUID3()}.tmp`;
28218
+ const temporary = `${file}.${randomUUID4()}.tmp`;
28028
28219
  try {
28029
28220
  await writeFile3(temporary, `${JSON.stringify(marker)}
28030
28221
  `, {
@@ -30589,7 +30780,7 @@ var FreeCodeGoEngineCouncil = class {
30589
30780
  }
30590
30781
  const cwd = parent.session.header.cwd;
30591
30782
  if (cwd === void 0 || cwd.trim() === "") throw new Error("engineering council child requires a workspace-backed parent session");
30592
- const sessionId = SessionId(randomUUID3());
30783
+ const sessionId = SessionId(randomUUID4());
30593
30784
  const agentOptions = {
30594
30785
  provider: runtime.provider,
30595
30786
  ...runtime.model === void 0 ? {} : { model: runtime.model },
@@ -30995,7 +31186,7 @@ function safeError(error) {
30995
31186
  return redactCredentialShapes2(error instanceof Error ? error.message : String(error));
30996
31187
  }
30997
31188
  function councilId() {
30998
- return `council_${randomUUID3().replaceAll("-", "")}`;
31189
+ return `council_${randomUUID4().replaceAll("-", "")}`;
30999
31190
  }
31000
31191
  function projectIdFor$3(cwd) {
31001
31192
  return createHash3("sha256").update(resolve2(cwd).replaceAll("\\", "/").toLowerCase()).digest("hex").slice(0, 24);
@@ -31375,7 +31566,7 @@ var EngineeringMemoryStore = class {
31375
31566
  const hash2 = createHash3("sha256").update(`${projectId}\0${title}\0${body}\0${kind}\0${JSON.stringify([...tags].sort())}`).digest("hex");
31376
31567
  const duplicate = database.prepare("SELECT id, title, kind, trust, project_id, body, tags_json, source_engine, created_at FROM engineering_memories WHERE project_id = ? AND content_hash = ? ORDER BY created_at DESC LIMIT 1").get(projectId, hash2);
31377
31568
  if (duplicate !== void 0) return this.detailFor(database, duplicate);
31378
- const id = `mem_${randomUUID3().replaceAll("-", "")}`;
31569
+ const id = `mem_${randomUUID4().replaceAll("-", "")}`;
31379
31570
  const now = Date.now();
31380
31571
  database.prepare("INSERT INTO engineering_memories(id, project_id, title, kind, trust, body, tags_json, source_engine, created_at, updated_at, content_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(id, projectId, title, kind, "draft", body, JSON.stringify(tags), sourceEngine ?? null, now, now, hash2);
31381
31572
  return {
@@ -31403,7 +31594,7 @@ var EngineeringMemoryStore = class {
31403
31594
  const payload = normalizeObservation(input);
31404
31595
  const projectId = projectIdFor$2(payload.cwd);
31405
31596
  const now = Date.now();
31406
- const id = `out_${randomUUID3().replaceAll("-", "")}`;
31597
+ const id = `out_${randomUUID4().replaceAll("-", "")}`;
31407
31598
  return {
31408
31599
  queued: database.prepare("INSERT OR IGNORE INTO engineering_memory_outbox(id, project_id, generation_key, payload_json, state, attempts, created_at, updated_at) VALUES (?, ?, ?, ?, ?, 0, ?, ?)").run(id, projectId, payload.generation, JSON.stringify(payload), "queued", now, now).changes === 1,
31409
31600
  id
@@ -31494,7 +31685,7 @@ var EngineeringMemoryStore = class {
31494
31685
  const hash2 = createHash3("sha256").update(`${projectId}\0${payload.generation}\0${title}\0${body}`).digest("hex");
31495
31686
  const duplicate = database.prepare("SELECT id, title, kind, trust, project_id, body, tags_json, source_engine, created_at FROM engineering_memories WHERE project_id = ? AND content_hash = ? LIMIT 1").get(projectId, hash2);
31496
31687
  if (duplicate !== void 0) return this.detailFor(database, duplicate);
31497
- const id = `mem_${randomUUID3().replaceAll("-", "")}`;
31688
+ const id = `mem_${randomUUID4().replaceAll("-", "")}`;
31498
31689
  const now = Date.now();
31499
31690
  database.prepare("INSERT INTO engineering_memories(id, project_id, title, subject, kind, trust, body, tags_json, source_engine, created_at, updated_at, content_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(id, projectId, title, subject ?? null, payload.kind, "captured", body, JSON.stringify(payload.tags), payload.sourceEngine ?? null, now, now, hash2);
31500
31691
  const insertSource = database.prepare("INSERT INTO engineering_memory_sources(memory_id, source_index, session_id, event_sequence, event_type, turn, engine, provider, model, files_read_json, files_written_json, captured_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
@@ -31805,7 +31996,7 @@ var EngineeringMemoryStore = class {
31805
31996
  async backup() {
31806
31997
  const database = this.requireDatabase();
31807
31998
  const createdAt = Date.now();
31808
- const id = `backup_${createdAt}_${randomUUID3().replaceAll("-", "").slice(0, 12)}`;
31999
+ const id = `backup_${createdAt}_${randomUUID4().replaceAll("-", "").slice(0, 12)}`;
31809
32000
  const directory = join4(dirname2(this.databasePath), "backups");
31810
32001
  await mkdir4(directory, {
31811
32002
  recursive: true,
@@ -33026,7 +33217,7 @@ var CodeGraphRuntimeManager = class {
33026
33217
  const archive = codegraphArchive();
33027
33218
  if (archive === void 0) throw new Error("\u5F53\u524D\u7CFB\u7EDF\u6CA1\u6709\u7ECF\u8FC7\u9A8C\u8BC1\u7684 CodeGraph \u5B98\u65B9\u5E73\u53F0\u5305\u3002");
33028
33219
  await ensurePrivateDirectory(this.rootDirectory, "CodeGraph");
33029
- const staging = join4(this.rootDirectory, `.install-${randomUUID3()}`);
33220
+ const staging = join4(this.rootDirectory, `.install-${randomUUID4()}`);
33030
33221
  await rm2(staging, {
33031
33222
  recursive: true,
33032
33223
  force: true
@@ -33424,7 +33615,7 @@ var EngineeringVerificationJobs = class {
33424
33615
  */
33425
33616
  start(cwd, stages, probes = []) {
33426
33617
  const database = this.requireDatabase();
33427
- const id = `job_${randomUUID3().replaceAll("-", "")}`;
33618
+ const id = `job_${randomUUID4().replaceAll("-", "")}`;
33428
33619
  const createdAt = Date.now();
33429
33620
  const projectId = projectIdFor$1(cwd);
33430
33621
  database.prepare("INSERT INTO engineering_jobs(id, project_id, kind, state, created_at) VALUES (?, ?, ?, ?, ?)").run(id, projectId, "verification", "queued", createdAt);
@@ -33468,7 +33659,7 @@ var EngineeringVerificationJobs = class {
33468
33659
  const current = this.get(job.id);
33469
33660
  if (current.verification !== void 0) return current.verification;
33470
33661
  if (current.state === "cancelled" || current.state === "interrupted" || current.state === "failed" || current.state === "completed") return {
33471
- id: `verify_${randomUUID3().replaceAll("-", "")}`,
33662
+ id: `verify_${randomUUID4().replaceAll("-", "")}`,
33472
33663
  checkedAt: Date.now(),
33473
33664
  stages: [{
33474
33665
  id: "scope",
@@ -33590,7 +33781,7 @@ var EngineeringVerificationJobs = class {
33590
33781
  });
33591
33782
  const redacted = (summary2) => redactJobSummary(summary2);
33592
33783
  const view = {
33593
- id: `verify_${randomUUID3().replaceAll("-", "")}`,
33784
+ id: `verify_${randomUUID4().replaceAll("-", "")}`,
33594
33785
  checkedAt: Date.now(),
33595
33786
  stages: result.stages.map((stage) => ({
33596
33787
  ...stage,
@@ -33843,7 +34034,7 @@ var EngineeringCheckpointStore = class {
33843
34034
  continue;
33844
34035
  }
33845
34036
  const checkpoint = {
33846
- id: `ckpt_${randomUUID3().replaceAll("-", "").slice(0, 24)}`,
34037
+ id: `ckpt_${randomUUID4().replaceAll("-", "").slice(0, 24)}`,
33847
34038
  label: input.label.slice(0, 160).trim() || "checkpoint",
33848
34039
  createdAt: Date.now(),
33849
34040
  entries,
@@ -34128,7 +34319,7 @@ var EngineeringCheckpointStore = class {
34128
34319
  recursive: true,
34129
34320
  mode: 448
34130
34321
  });
34131
- const temporary = `${target}.${randomUUID3()}.tmp`;
34322
+ const temporary = `${target}.${randomUUID4()}.tmp`;
34132
34323
  await writeFile3(temporary, content, { mode: 384 });
34133
34324
  try {
34134
34325
  await rename3(temporary, target);
@@ -34378,7 +34569,7 @@ var HunkTracker = class {
34378
34569
  const at2 = input.at ?? Date.now();
34379
34570
  const created = [];
34380
34571
  for (const change of diffLines(before, after)) {
34381
- const id = randomUUID3();
34572
+ const id = randomUUID4();
34382
34573
  created.push({
34383
34574
  id,
34384
34575
  file,
@@ -41399,7 +41590,7 @@ async function generateVideoWithFallback(host, args, signal) {
41399
41590
  });
41400
41591
  }
41401
41592
  async function writeGeneratedMedia(file, bytes) {
41402
- const temporary = `${file}.${randomUUID3()}.tmp`;
41593
+ const temporary = `${file}.${randomUUID4()}.tmp`;
41403
41594
  try {
41404
41595
  await fs.writeFile(temporary, bytes, {
41405
41596
  mode: 384,
@@ -41446,7 +41637,7 @@ async function generateAudioWithFallback(host, args, cwd, signal) {
41446
41637
  }
41447
41638
  const directory = path.join(cwd, ".freecodego", "generated-media");
41448
41639
  await fs.mkdir(directory, { recursive: true });
41449
- const file = path.join(directory, `audio-${Date.now()}-${randomUUID3().slice(0, 8)}.${extension}`);
41640
+ const file = path.join(directory, `audio-${Date.now()}-${randomUUID4().slice(0, 8)}.${extension}`);
41450
41641
  await writeGeneratedMedia(file, bytes);
41451
41642
  const mimeType = contentType.includes("json") || contentType === "" ? `audio/${extension}` : contentType;
41452
41643
  return {
@@ -42454,7 +42645,7 @@ function validatePayloadFiles(files) {
42454
42645
  return { ok: true };
42455
42646
  }
42456
42647
  async function writeStateFile(fs2, path3, text2) {
42457
- const temp = `${path3}.${randomUUID3()}.tmp`;
42648
+ const temp = `${path3}.${randomUUID4()}.tmp`;
42458
42649
  try {
42459
42650
  await fs2.writeFile(temp, text2);
42460
42651
  await fs2.rename(temp, path3);
@@ -42464,10 +42655,10 @@ async function writeStateFile(fs2, path3, text2) {
42464
42655
  }
42465
42656
  }
42466
42657
  function stagingPath(root) {
42467
- return join4(root, `.freecodego-staging-${randomUUID3()}`);
42658
+ return join4(root, `.freecodego-staging-${randomUUID4()}`);
42468
42659
  }
42469
42660
  function backupPath(root) {
42470
- return join4(root, `.freecodego-backup-${randomUUID3()}`);
42661
+ return join4(root, `.freecodego-backup-${randomUUID4()}`);
42471
42662
  }
42472
42663
  async function installSkill(request, fs2 = NODE_INSTALLER_FS) {
42473
42664
  const name2 = validateSkillName(request.payload.name);
@@ -50335,7 +50526,7 @@ var FolderTrustStore = class {
50335
50526
  */
50336
50527
  async write(record3) {
50337
50528
  await mkdir4(dirname2(this.file), { recursive: true });
50338
- const temporary = `${this.file}.${randomUUID3()}.tmp`;
50529
+ const temporary = `${this.file}.${randomUUID4()}.tmp`;
50339
50530
  await writeFile3(temporary, `${JSON.stringify(record3, null, 2)}
50340
50531
  `, "utf8");
50341
50532
  await rename3(temporary, this.file);
@@ -58618,7 +58809,7 @@ var DEFAULT_REVIEW_PLAN_TOOLS = [
58618
58809
  ];
58619
58810
  async function runReview(deps, options) {
58620
58811
  const now = deps.now ?? Date.now;
58621
- const newId = deps.newId ?? randomUUID3;
58812
+ const newId = deps.newId ?? randomUUID4;
58622
58813
  const limits = options.budget ?? DEFAULT_REVIEW_BUDGET;
58623
58814
  const signal = options.signal;
58624
58815
  const reviewerName = options.reviewerName ?? "reviewer";
@@ -59399,7 +59590,7 @@ function createSubagentFileReviewer(parent, options = {}) {
59399
59590
  freeCodeGoReadOnly: true
59400
59591
  };
59401
59592
  return wrapChild(await parent.ctx.agents.create({
59402
- sessionId: SessionId(randomUUID3()),
59593
+ sessionId: SessionId(randomUUID4()),
59403
59594
  meta: {
59404
59595
  cwd,
59405
59596
  parentSession: parent.id,
@@ -60011,6 +60202,28 @@ function upstreamMessage(value, fallback) {
60011
60202
  const message2 = typeof value === "string" ? value.trim() : "";
60012
60203
  return redactCredentialShapes2(message2 === "" ? fallback : message2);
60013
60204
  }
60205
+ async function accountAnnouncements(host) {
60206
+ if (host.account === void 0 || host.api === void 0) return [];
60207
+ try {
60208
+ await host.restoreAccount();
60209
+ return await host.account.withAccessToken((accessToken) => host.api.getAnnouncements({ accessToken }));
60210
+ } catch {
60211
+ return [];
60212
+ }
60213
+ }
60214
+ async function accountMarkAnnouncementRead(host, announcementId) {
60215
+ if (host.account === void 0 || host.api === void 0) return false;
60216
+ try {
60217
+ await host.restoreAccount();
60218
+ await host.account.withAccessToken((accessToken) => host.api.markAnnouncementRead({
60219
+ accessToken,
60220
+ announcementId
60221
+ }));
60222
+ return true;
60223
+ } catch {
60224
+ return false;
60225
+ }
60226
+ }
60014
60227
  async function accountStatus(host) {
60015
60228
  if (host.account === void 0 || host.api === void 0) return { status: "backend-not-configured" };
60016
60229
  try {
@@ -60304,13 +60517,38 @@ async function nvidiaSetKey(host, value) {
60304
60517
  host.ctx.emit("llm/adapters-updated");
60305
60518
  return host.nvidiaStatus();
60306
60519
  }
60520
+ async function bootstrapDeviceIdentity(host) {
60521
+ try {
60522
+ const deviceId = await host.account?.deviceId();
60523
+ if (deviceId === void 0) return {};
60524
+ const name2 = localDeviceName();
60525
+ return name2 === void 0 ? { deviceId } : {
60526
+ deviceId,
60527
+ deviceName: name2
60528
+ };
60529
+ } catch {
60530
+ return {};
60531
+ }
60532
+ }
60533
+ function localDeviceName() {
60534
+ try {
60535
+ const name2 = hostname().trim();
60536
+ return name2 === "" ? void 0 : name2;
60537
+ } catch {
60538
+ return;
60539
+ }
60540
+ }
60307
60541
  async function backendBootstrap(host) {
60308
60542
  if (host.api === void 0 || host.account === void 0) return { status: "backend-not-configured" };
60309
60543
  try {
60310
60544
  await host.restoreAccount();
60545
+ const identity = await bootstrapDeviceIdentity(host);
60311
60546
  return {
60312
60547
  status: "available",
60313
- data: toJsonValue2(await host.account.withAccessToken((accessToken) => host.api.getBootstrap({ accessToken })))
60548
+ data: toJsonValue2(await host.account.withAccessToken((accessToken) => host.api.getBootstrap({
60549
+ accessToken,
60550
+ ...identity
60551
+ })))
60314
60552
  };
60315
60553
  } catch (error) {
60316
60554
  return {
@@ -60397,7 +60635,11 @@ async function confirmAccountAuthorization(host, result) {
60397
60635
  async function deviceSessions(host) {
60398
60636
  if (host.api === void 0 || host.account === void 0) throw backendNotConfigured();
60399
60637
  await host.restoreAccount();
60400
- return host.account.withAccessToken((accessToken) => host.api.getDeviceSessions({ accessToken }));
60638
+ const identity = await bootstrapDeviceIdentity(host);
60639
+ return host.account.withAccessToken((accessToken) => host.api.getDeviceSessions({
60640
+ accessToken,
60641
+ ...identity
60642
+ }));
60401
60643
  }
60402
60644
  async function revokeDeviceSession(host, deviceId) {
60403
60645
  if (host.api === void 0 || host.account === void 0) throw backendNotConfigured();
@@ -60972,7 +61214,7 @@ async function traeStartBrowserLogin(host, realmValue) {
60972
61214
  const loginUrl = buildTraeLoginUrl(identity, traeCallbackUrl(listener.port), realm);
60973
61215
  session = {
60974
61216
  attempt: {
60975
- state: randomUUID3(),
61217
+ state: randomUUID4(),
60976
61218
  realm,
60977
61219
  loginUrl,
60978
61220
  machineId: identity.machineId,
@@ -75994,6 +76236,8 @@ var FreeCodeGoHarnessPlugin = (() => {
75994
76236
  let _refreshAccount_decorators;
75995
76237
  let _logout_decorators;
75996
76238
  let _deviceSessions_decorators;
76239
+ let _announcements_decorators;
76240
+ let _markAnnouncementRead_decorators;
75997
76241
  let _revokeDeviceSession_decorators;
75998
76242
  let _revokeAllSessions_decorators;
75999
76243
  let _backendCatalog_decorators;
@@ -76226,6 +76470,8 @@ var FreeCodeGoHarnessPlugin = (() => {
76226
76470
  _refreshAccount_decorators = [Remote("accountRefresh")];
76227
76471
  _logout_decorators = [Remote("accountLogout")];
76228
76472
  _deviceSessions_decorators = [Remote("accountDeviceSessions")];
76473
+ _announcements_decorators = [Remote("accountAnnouncements")];
76474
+ _markAnnouncementRead_decorators = [Remote("accountMarkAnnouncementRead")];
76229
76475
  _revokeDeviceSession_decorators = [Remote("accountRevokeDeviceSession")];
76230
76476
  _revokeAllSessions_decorators = [Remote("accountRevokeAllSessions")];
76231
76477
  _backendCatalog_decorators = [Remote("backendCatalog")];
@@ -77935,6 +78181,28 @@ var FreeCodeGoHarnessPlugin = (() => {
77935
78181
  },
77936
78182
  metadata: _metadata
77937
78183
  }, null, _instanceExtraInitializers);
78184
+ __esDecorate(this, null, _announcements_decorators, {
78185
+ kind: "method",
78186
+ name: "announcements",
78187
+ static: false,
78188
+ private: false,
78189
+ access: {
78190
+ has: (obj) => "announcements" in obj,
78191
+ get: (obj) => obj.announcements
78192
+ },
78193
+ metadata: _metadata
78194
+ }, null, _instanceExtraInitializers);
78195
+ __esDecorate(this, null, _markAnnouncementRead_decorators, {
78196
+ kind: "method",
78197
+ name: "markAnnouncementRead",
78198
+ static: false,
78199
+ private: false,
78200
+ access: {
78201
+ has: (obj) => "markAnnouncementRead" in obj,
78202
+ get: (obj) => obj.markAnnouncementRead
78203
+ },
78204
+ metadata: _metadata
78205
+ }, null, _instanceExtraInitializers);
77938
78206
  __esDecorate(this, null, _revokeDeviceSession_decorators, {
77939
78207
  kind: "method",
77940
78208
  name: "revokeDeviceSession",
@@ -82241,7 +82509,7 @@ ${detail.body}`
82241
82509
  ...outcome.fallbackReason === void 0 ? {} : { fallbackReason: outcome.fallbackReason }
82242
82510
  };
82243
82511
  },
82244
- newSessionId: () => randomUUID3(),
82512
+ newSessionId: () => randomUUID4(),
82245
82513
  callerOf: (exec) => {
82246
82514
  const agent = exec?.agent;
82247
82515
  const cwd = agent?.session?.header?.cwd;
@@ -83868,6 +84136,25 @@ ${plan.text}
83868
84136
  return deviceSessions(this.accountRemotesHost);
83869
84137
  }
83870
84138
  /**
84139
+ * Read the announcements the backend published to this account.
84140
+ *
84141
+ * Read by the notice bar on the chat surface, which polls it — the plugin has
84142
+ * no push channel to the backend, so "in time" means the next poll, not a
84143
+ * socket. An empty list is the answer for a signed-out or unreachable backend.
84144
+ * @returns the visible announcements, in the backend's own order.
84145
+ */
84146
+ async announcements() {
84147
+ return accountAnnouncements(this.accountRemotesHost);
84148
+ }
84149
+ /**
84150
+ * Mark one announcement read for this account, so closing it holds everywhere.
84151
+ * @param announcementId - the announcement the user closed.
84152
+ * @returns whether the backend recorded the read.
84153
+ */
84154
+ async markAnnouncementRead(announcementId) {
84155
+ return accountMarkAnnouncementRead(this.accountRemotesHost, announcementId);
84156
+ }
84157
+ /**
83871
84158
  * Revoke one device session and report the sessions that remain.
83872
84159
  * @param deviceId - id of the device session to revoke.
83873
84160
  * @returns the sessions the account still has.