@fased/fased 0.1.48 → 0.1.51

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.
Files changed (157) hide show
  1. package/dist/agents/auth-health.js +11 -5
  2. package/dist/agents/current-model-catalog.js +45 -0
  3. package/dist/agents/model-catalog-source.js +28 -3
  4. package/dist/agents/provider-id.js +1 -1
  5. package/dist/brand.js +1 -1
  6. package/dist/build-info.json +3 -3
  7. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  8. package/dist/cli/lightweight/gateway-runtime-probe.js +50 -0
  9. package/dist/cli/update-cli/update-command.js +120 -0
  10. package/dist/commands/doctor-state-integrity.js +25 -2
  11. package/dist/config/sessions/transcript.js +30 -16
  12. package/dist/config/sessions.js +2 -2
  13. package/dist/control-ui/assets/{app-CHqTx3P_.js → app-wJm5kuje.js} +1274 -1259
  14. package/dist/control-ui/assets/{chat-model-ref-BbrdMKjQ.js → chat-model-ref-Cs2TTOtF.js} +2 -2
  15. package/dist/control-ui/assets/{config-FFZLTSLS.js → config-CU5Zi8Dh.js} +2 -2
  16. package/dist/control-ui/assets/{index-CFHfNlLv.js → index-vTDTf5Zv.js} +3 -3
  17. package/dist/control-ui/assets/{mining-D_VAMFMY.js → mining-BdhMn18r.js} +3 -3
  18. package/dist/control-ui/assets/{providers-DqFL6WrJ.js → providers-C0DEXi8e.js} +14 -3
  19. package/dist/control-ui/index.html +1 -1
  20. package/dist/gateway/probe-payload.js +14 -0
  21. package/dist/gateway/probe.js +8 -4
  22. package/dist/gateway/protocol/schema/agents-models-skills.js +1 -0
  23. package/dist/gateway/protocol/schema/frames.js +5 -0
  24. package/dist/gateway/protocol/schema/snapshot.js +1 -0
  25. package/dist/gateway/server/ws-connection/message-handler.js +2 -1
  26. package/dist/gateway/server-http-lazy.js +2 -4
  27. package/dist/gateway/server-http.js +3 -4
  28. package/dist/gateway/server-methods/chat.js +4 -29
  29. package/dist/gateway/server-methods/models.js +3 -2
  30. package/dist/infra/system-presence.js +4 -2
  31. package/dist/light-update-precheck.js +1 -1
  32. package/dist/light-update-status.js +1 -1
  33. package/dist/memory/inventory.js +19 -0
  34. package/dist/plugin-sdk/{accounts-DI5226U8.js → accounts-BfdIB9Ud.js} +1 -1
  35. package/dist/plugin-sdk/{actions-CEj4gOU2.js → actions-DHN_x97P.js} +2 -2
  36. package/dist/plugin-sdk/{active-listener-Ss4UKXZb.js → active-listener-CIP_Ht8d.js} +1 -1
  37. package/dist/plugin-sdk/{audio-preflight-Cu4alGjb.js → audio-preflight-CKGhXtyV.js} +26 -26
  38. package/dist/plugin-sdk/{audit-BTskqjJ_.js → audit-Cmg38KPG.js} +1 -1
  39. package/dist/plugin-sdk/{channel-access-B1KciKmw.js → channel-access-JHDguu4T.js} +1 -1
  40. package/dist/plugin-sdk/{channel-access-configure-Y91FLOJc.js → channel-access-configure-B8mwKp-Z.js} +1 -1
  41. package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
  42. package/dist/plugin-sdk/channel-plugin-common.js +4 -4
  43. package/dist/plugin-sdk/{chunk-items-Q8HhMuSe.js → chunk-items-iKdlZyWZ.js} +1 -1
  44. package/dist/plugin-sdk/{chunk-CSUjIQSx.js → chunk-oJRyTNTL.js} +1 -1
  45. package/dist/plugin-sdk/command-status.js +26 -26
  46. package/dist/plugin-sdk/{commands-registry-DJ0pfCcY.js → commands-registry-Cjo7OjIf.js} +2 -2
  47. package/dist/plugin-sdk/{common-Cjm8psuW.js → common-Dx4sUa9f.js} +1 -1
  48. package/dist/plugin-sdk/{config-DutIeFge.js → config-DdS0zHiU.js} +80 -4
  49. package/dist/plugin-sdk/{config-schema-Cg6PYSHR.js → config-schema-BJQEjMPz.js} +2090 -523
  50. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  51. package/dist/plugin-sdk/discord.d.ts +4 -4
  52. package/dist/plugin-sdk/discord.js +19 -19
  53. package/dist/plugin-sdk/{handle-action-Dp2741iu.js → handle-action-wR0x9b0c.js} +13 -13
  54. package/dist/plugin-sdk/{helpers-BCZ9d3fl.js → helpers-DkauqZNP.js} +1 -1
  55. package/dist/plugin-sdk/{image-ops-B-QR_M9Z.js → image-ops-DmSjpAeh.js} +1 -1
  56. package/dist/plugin-sdk/index.d.ts +17 -17
  57. package/dist/plugin-sdk/index.js +28 -28
  58. package/dist/plugin-sdk/{login-BFvQxf9o.js → login-DB9LIbi8.js} +3 -3
  59. package/dist/plugin-sdk/{login-qr-CERTe_nD.js → login-qr-CDhMa-c7.js} +3 -3
  60. package/dist/plugin-sdk/{manager-Dhq3SXpO.js → manager-DGZsMQWR.js} +2 -2
  61. package/dist/plugin-sdk/{markdown-tables-B1Tx7c77.js → markdown-tables-BX7JXDUV.js} +1 -1
  62. package/dist/plugin-sdk/{media-BwsqooWC.js → media-DyzsScOp.js} +2 -2
  63. package/dist/plugin-sdk/{monitor-BYzN5Qr4.js → monitor-B91PZ2mD.js} +31 -31
  64. package/dist/plugin-sdk/{monitor-CJiacqtR.js → monitor-BH9rTJFq.js} +30 -30
  65. package/dist/plugin-sdk/{openresponses-http-VLSSqCZZ.js → openresponses-http-CmBLZ9bH.js} +26 -26
  66. package/dist/plugin-sdk/{optional-runtime-dependency-BEBP4VFJ.js → optional-runtime-dependency-DaMkPVe_.js} +1 -1
  67. package/dist/plugin-sdk/{outbound-BqWgaC5M.js → outbound-X-m2mBZn.js} +6 -6
  68. package/dist/plugin-sdk/{outbound-attachment-L1q40kiu.js → outbound-attachment-M0NCUSG-.js} +2 -2
  69. package/dist/plugin-sdk/{pairing-message-DWXHX-jt.d.ts → pairing-message-BaQiXKLE.d.ts} +1 -1
  70. package/dist/plugin-sdk/{pairing-message-CZBahREg.js → pairing-message-hnmpJlW2.js} +1 -1
  71. package/dist/plugin-sdk/{pairing-store-D7I1Qmiw.js → pairing-store-DyghUm9G.js} +2 -2
  72. package/dist/plugin-sdk/{pi-model-discovery-C5klknQZ.js → pi-model-discovery-ReorOh7W.js} +1 -1
  73. package/dist/plugin-sdk/{pi-model-discovery-runtime-DIEMhCZ5.js → pi-model-discovery-runtime-C-CHZo03.js} +2 -2
  74. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
  75. package/dist/plugin-sdk/{pw-ai-Bv-q25gY.js → pw-ai-D_gli3_r.js} +3 -3
  76. package/dist/plugin-sdk/{replies-Bd9ktEkq.js → replies-Djyehmcu.js} +2 -2
  77. package/dist/plugin-sdk/{resolve-outbound-target-CCIyBb5H.js → resolve-outbound-target-Chznq8rS.js} +1 -1
  78. package/dist/plugin-sdk/{resolve-route-B7vZ3nKp.js → resolve-route-Ce08Fx-G.js} +1 -1
  79. package/dist/plugin-sdk/{retry-policy-ChC3G4lv.js → retry-policy-eS25atAo.js} +1 -1
  80. package/dist/plugin-sdk/{runtime-BLknHFGz.js → runtime-Cv3gX3Wc.js} +31 -18
  81. package/dist/plugin-sdk/{runtime-dependency-C5rJfC0u.js → runtime-dependency-p_iKbESf.js} +2 -2
  82. package/dist/plugin-sdk/sat-runtime.d.ts +2 -2
  83. package/dist/plugin-sdk/sat-runtime.js +5 -5
  84. package/dist/plugin-sdk/{send-Bd3Z-jAd.js → send-B3RAELvw.js} +6 -6
  85. package/dist/plugin-sdk/{send-Bmtz9ICm.js → send-BuPGxx3-.js} +8 -8
  86. package/dist/plugin-sdk/{send-mkyZzhCO.js → send-C-jaxZJG.js} +5 -5
  87. package/dist/plugin-sdk/{send-CUGEhiUa.js → send-CSob7dn9.js} +6 -6
  88. package/dist/plugin-sdk/{send-CMoWt-EX.js → send-CsLJsIvv.js} +7 -7
  89. package/dist/plugin-sdk/{session-D2zmelOL.js → session-DKJGakm1.js} +2 -2
  90. package/dist/plugin-sdk/{skill-commands-BybEjJ1_.js → skill-commands-BqpZG5nW.js} +2 -2
  91. package/dist/plugin-sdk/{slack-DogXvGG3.js → slack-CQqN9uBH.js} +38 -38
  92. package/dist/plugin-sdk/{slack-actions-uXb55QWW.js → slack-actions-DlbVH5SS.js} +11 -11
  93. package/dist/plugin-sdk/slack.d.ts +4 -4
  94. package/dist/plugin-sdk/slack.js +9 -9
  95. package/dist/plugin-sdk/{sticker-cache-BuPw7fTf.js → sticker-cache-CDyt2ZMh.js} +3 -3
  96. package/dist/plugin-sdk/{tables-CVDOT5dX.js → tables-Cij4iAXw.js} +1 -1
  97. package/dist/plugin-sdk/{targets-DskkhJAp.js → targets-VrYeAmeT.js} +1 -1
  98. package/dist/plugin-sdk/{telegram-actions-_KQPbjpI.js → telegram-actions-ZwUBOkwa.js} +29 -29
  99. package/dist/plugin-sdk/telegram.d.ts +4 -4
  100. package/dist/plugin-sdk/telegram.js +8 -8
  101. package/dist/plugin-sdk/{thread-bindings.lifecycle-CrhQXYEU.js → thread-bindings.lifecycle-BWIzwpPM.js} +3 -3
  102. package/dist/plugin-sdk/{thread-bindings.state-ssxdG7xV.js → thread-bindings.state-CpAQrGco.js} +2 -2
  103. package/dist/plugin-sdk/{tmp-fased-dir-Be1wBhVz.d.ts → tmp-fased-dir-DLba1ePP.d.ts} +1 -1
  104. package/dist/plugin-sdk/{types-CXLEOrJX.d.ts → types-CKR2Lvsa.d.ts} +1 -1
  105. package/dist/plugin-sdk/{types-CJGVZX6j.d.ts → types-NR-f-rw-.d.ts} +3 -3
  106. package/dist/plugin-sdk/{wallet-status-CQI89c_6.js → wallet-status-ClmXigGN.js} +10 -3
  107. package/dist/plugin-sdk/{web-C8DwA0eO.js → web-BK2j9z6c.js} +31 -31
  108. package/dist/plugin-sdk/{whatsapp-actions-DfvM-mlZ.js → whatsapp-actions-CaenMy79.js} +13 -13
  109. package/dist/plugin-sdk/{whatsapp-heartbeat-DyJasLDB.js → whatsapp-heartbeat-Cr9K5Sil.js} +2 -2
  110. package/dist/plugin-sdk/whatsapp.d.ts +11 -11
  111. package/dist/plugin-sdk/whatsapp.js +12 -12
  112. package/dist/plugin-sdk/{wsl-JwmI6Zu8.js → wsl-D_I-iL4U.js} +3 -3
  113. package/dist/plugin-sdk/{zod-schema.providers-core-BE_5DNhj.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +71 -71
  114. package/dist/plugins/status-cache.js +1 -1
  115. package/dist/providers/refresh.js +1 -1
  116. package/dist/providers/refreshed-model-capabilities.js +1935 -406
  117. package/dist/providers/registry.js +14 -3
  118. package/dist/version.js +11 -1
  119. package/extensions/acpx/package.json +1 -1
  120. package/extensions/bluebubbles/package.json +1 -1
  121. package/extensions/copilot-proxy/package.json +1 -1
  122. package/extensions/diagnostics-otel/package.json +1 -1
  123. package/extensions/fased-federation/package.json +1 -1
  124. package/extensions/google-gemini-cli-auth/package.json +1 -1
  125. package/extensions/imessage/package.json +1 -1
  126. package/extensions/irc/package.json +1 -1
  127. package/extensions/line/package.json +1 -1
  128. package/extensions/llm-task/package.json +1 -1
  129. package/extensions/lobster/package.json +1 -1
  130. package/extensions/matrix/CHANGELOG.md +18 -0
  131. package/extensions/matrix/package.json +1 -1
  132. package/extensions/mattermost/package.json +1 -1
  133. package/extensions/memory-core/package.json +2 -2
  134. package/extensions/memory-lancedb/package.json +1 -1
  135. package/extensions/minimax-portal-auth/package.json +1 -1
  136. package/extensions/msteams/CHANGELOG.md +18 -0
  137. package/extensions/msteams/package.json +1 -1
  138. package/extensions/nextcloud-talk/package.json +1 -1
  139. package/extensions/nostr/CHANGELOG.md +18 -0
  140. package/extensions/nostr/package.json +1 -1
  141. package/extensions/open-prose/package.json +1 -1
  142. package/extensions/sat-mining/index.ts +121 -12
  143. package/extensions/sat-mining/package.json +1 -1
  144. package/extensions/sat-mining/src/runtime.ts +9 -3
  145. package/extensions/signal/package.json +1 -1
  146. package/extensions/synology-chat/package.json +1 -1
  147. package/extensions/tlon/package.json +1 -1
  148. package/extensions/twitch/CHANGELOG.md +18 -0
  149. package/extensions/twitch/package.json +1 -1
  150. package/extensions/voice-call/CHANGELOG.md +18 -0
  151. package/extensions/voice-call/package.json +1 -1
  152. package/extensions/zalo/CHANGELOG.md +18 -0
  153. package/extensions/zalo/package.json +1 -1
  154. package/extensions/zalouser/CHANGELOG.md +18 -0
  155. package/extensions/zalouser/package.json +1 -1
  156. package/package.json +1 -1
  157. package/scripts/start-managed.sh +35 -1
@@ -119,7 +119,7 @@ function buildProfileHealth(params) {
119
119
  }
120
120
  const hasRefreshToken = typeof credential.refresh === "string" && credential.refresh.length > 0;
121
121
  const { status: rawStatus, remainingMs } = resolveOAuthStatus(credential.expires, now, warnAfterMs);
122
- const status = hasRefreshToken && (rawStatus === "expired" || rawStatus === "expiring") ? "ok" : rawStatus;
122
+ const status = hasRefreshToken && (rawStatus === "expired" || rawStatus === "expiring") ? "refresh-required" : rawStatus;
123
123
  return {
124
124
  profileId,
125
125
  provider,
@@ -190,13 +190,19 @@ function buildAuthHealthSummary(params) {
190
190
  provider.expiresAt = Math.min(...expiryCandidates);
191
191
  provider.remainingMs = provider.expiresAt - now;
192
192
  }
193
- const statuses = new Set(expirable.map((p) => p.status));
194
- if (statuses.has("expired") || statuses.has("missing")) {
195
- provider.status = "expired";
193
+ const statuses = new Set(provider.profiles.map((p) => p.status));
194
+ if (statuses.has("ok")) {
195
+ provider.status = "ok";
196
+ } else if (statuses.has("static")) {
197
+ provider.status = "static";
196
198
  } else if (statuses.has("expiring")) {
197
199
  provider.status = "expiring";
200
+ } else if (statuses.has("refresh-required")) {
201
+ provider.status = "refresh-required";
202
+ } else if (statuses.has("expired")) {
203
+ provider.status = "expired";
198
204
  } else {
199
- provider.status = "ok";
205
+ provider.status = "missing";
200
206
  }
201
207
  }
202
208
  const providers = Array.from(providersMap.values()).toSorted((a, b) => a.provider.localeCompare(b.provider));
@@ -40,6 +40,51 @@ function model(params) {
40
40
  };
41
41
  }
42
42
  const OPENAI_MODELS = [
43
+ model({
44
+ id: "gpt-5.6",
45
+ name: "GPT-5.6 Sol",
46
+ reasoning: true,
47
+ input: ["text", "image"],
48
+ contextWindow: 105e4,
49
+ maxTokens: 128e3,
50
+ api: "openai-responses",
51
+ cost: {
52
+ input: 5,
53
+ output: 30,
54
+ cacheRead: .5,
55
+ cacheWrite: 6.25
56
+ }
57
+ }),
58
+ model({
59
+ id: "gpt-5.6-terra",
60
+ name: "GPT-5.6 Terra",
61
+ reasoning: true,
62
+ input: ["text", "image"],
63
+ contextWindow: 105e4,
64
+ maxTokens: 128e3,
65
+ api: "openai-responses",
66
+ cost: {
67
+ input: 2.5,
68
+ output: 15,
69
+ cacheRead: .25,
70
+ cacheWrite: 3.125
71
+ }
72
+ }),
73
+ model({
74
+ id: "gpt-5.6-luna",
75
+ name: "GPT-5.6 Luna",
76
+ reasoning: true,
77
+ input: ["text", "image"],
78
+ contextWindow: 105e4,
79
+ maxTokens: 128e3,
80
+ api: "openai-responses",
81
+ cost: {
82
+ input: 1,
83
+ output: 6,
84
+ cacheRead: .1,
85
+ cacheWrite: 1.25
86
+ }
87
+ }),
43
88
  model({
44
89
  id: "gpt-5.5",
45
90
  name: "GPT-5.5",
@@ -1,4 +1,4 @@
1
- import { isOpenAISignInRuntimeModelSupported } from "../providers/registry.js";
1
+ import { isOpenAISignInRuntimeModelSupported, isStandardProviderCatalogEntry, listProviderBrandManifests } from "../providers/registry.js";
2
2
  import { MODEL_APIS } from "../config/types.models.js";
3
3
  import { buildModelCatalogMergeKey, mergeModelCatalogRowsByAuthority, normalizeModelCatalogModelId, normalizeModelCatalogProviderId, normalizeProviderCatalogRows } from "./model-catalog-normalized.js";
4
4
  import { CURRENT_MODEL_PROVIDER_CATALOG, listCurrentModelCatalogRows } from "./current-model-catalog.js";
@@ -86,11 +86,33 @@ function runtimeCatalogRows(params) {
86
86
  }
87
87
  return rows;
88
88
  }
89
+ function manifestCatalogRows() {
90
+ return listProviderBrandManifests().flatMap((manifest) => manifest.models.recommended.flatMap((ref) => {
91
+ const slash = ref.indexOf("/");
92
+ if (slash <= 0 || slash === ref.length - 1) {
93
+ return [];
94
+ }
95
+ const provider = normalizeModelCatalogProviderId(ref.slice(0, slash));
96
+ const id = normalizeModelCatalogModelId(ref.slice(slash + 1));
97
+ if (!provider || !id) {
98
+ return [];
99
+ }
100
+ return [{
101
+ id,
102
+ name: id,
103
+ provider,
104
+ mergeKey: buildModelCatalogMergeKey(provider, id),
105
+ source: "manifest",
106
+ status: "stable",
107
+ input: ["text"]
108
+ }];
109
+ }));
110
+ }
89
111
  function providerConfigForRow(cfg, row) {
90
112
  return cfg.models?.providers?.[row.provider] ?? CURRENT_MODEL_PROVIDER_CATALOG[row.provider];
91
113
  }
92
114
  function buildFasedModelCatalogRows(params) {
93
- return mergeModelCatalogRowsByAuthority([
115
+ const discoveredRows = mergeModelCatalogRowsByAuthority([
94
116
  ...listCurrentModelCatalogRows(),
95
117
  ...providerPluginCatalogRows(params.providerPluginProviders),
96
118
  ...runtimeCatalogRows({
@@ -99,6 +121,9 @@ function buildFasedModelCatalogRows(params) {
99
121
  }),
100
122
  ...configuredProviderCatalogRows(params.config)
101
123
  ]);
124
+ const discoveredKeys = new Set(discoveredRows.map((row) => row.mergeKey));
125
+ const missingManifestRows = manifestCatalogRows().filter((row) => !discoveredKeys.has(row.mergeKey));
126
+ return mergeModelCatalogRowsByAuthority([...discoveredRows, ...missingManifestRows]);
102
127
  }
103
128
  function buildFasedModelCatalogEntries(params) {
104
129
  return buildFasedModelCatalogRows({
@@ -126,7 +151,7 @@ function buildFasedModelCatalogEntries(params) {
126
151
  model,
127
152
  cfg: params.config,
128
153
  providerConfig,
129
- recommended: row.source === "current-preview"
154
+ recommended: isStandardProviderCatalogEntry(row)
130
155
  }) } : {}
131
156
  };
132
157
  });
@@ -11,7 +11,7 @@ function normalizeProviderId(provider) {
11
11
  return "opencode";
12
12
  }
13
13
  if (normalized === "kimi" || normalized === "kimi-code" || normalized === "kimi-coding") {
14
- return "kimi";
14
+ return "kimi-coding";
15
15
  }
16
16
  if (normalized === "moonshotai" || normalized === "moonshot-ai") {
17
17
  return "moonshot";
package/dist/brand.js CHANGED
@@ -2,7 +2,7 @@
2
2
  const FASED_BRAND_NAME = "Fased";
3
3
  const FASED_AGENT_NAME = "Fased Agent";
4
4
  const FASED_CONTROL_NAME = "Fased Control";
5
- const FASED_PRODUCT_VERSION = "0.1.48";
5
+ const FASED_PRODUCT_VERSION = "0.1.51";
6
6
  const FASED_DISPLAY_VERSION = `v${FASED_PRODUCT_VERSION}`;
7
7
  function formatFasedDisplayLine() {
8
8
  return `${FASED_AGENT_NAME} ${FASED_DISPLAY_VERSION}`;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.48",
3
- "commit": "fcf8a39da25b2eff820717322d264c05b8e63a07",
4
- "builtAt": "2026-07-12T01:26:25.284Z"
2
+ "version": "0.1.51",
3
+ "commit": "3be1ab5ffce9188ec35cd4e953a1d14c32ce6ef9",
4
+ "builtAt": "2026-07-12T11:39:29.796Z"
5
5
  }
@@ -1 +1 @@
1
- 2fffaf667087a0523ddbef46b1172870ffdf0a36c6a048a0f9c168e0ff0249a8
1
+ ce58b9c3b432284fff09586f15666c4fbc34583b177e95201b58bbdde0532978
@@ -0,0 +1,50 @@
1
+ import { resolvePluginStatusConfigPath } from "../../plugins/status-cache.js";
2
+ import fs from "node:fs";
3
+
4
+ //#region src/cli/lightweight/gateway-runtime-probe.ts
5
+ function readGatewayProbeConfig(configPath) {
6
+ try {
7
+ const parsed = JSON.parse(fs.readFileSync(configPath, "utf8"));
8
+ const configuredPort = parsed.gateway?.port;
9
+ return {
10
+ port: typeof configuredPort === "number" && Number.isInteger(configuredPort) ? configuredPort : 18789,
11
+ tls: parsed.gateway?.tls?.enabled === true
12
+ };
13
+ } catch {
14
+ return {
15
+ port: 18789,
16
+ tls: false
17
+ };
18
+ }
19
+ }
20
+ async function probeRunningGatewayRuntimeIdentity(params = {}) {
21
+ const config = readGatewayProbeConfig(params.configPath ?? resolvePluginStatusConfigPath());
22
+ try {
23
+ const response = await (params.fetchImpl ?? fetch)(`${config.tls ? "https" : "http"}://127.0.0.1:${config.port}/healthz`, {
24
+ signal: AbortSignal.timeout(params.timeoutMs ?? 600),
25
+ headers: { accept: "application/json" }
26
+ });
27
+ if (!response.ok) {
28
+ return {
29
+ reachable: true,
30
+ version: null,
31
+ runtimeSource: null
32
+ };
33
+ }
34
+ const payload = await response.json();
35
+ return {
36
+ reachable: true,
37
+ version: typeof payload.version === "string" ? payload.version.trim() || null : null,
38
+ runtimeSource: typeof payload.runtimeSource === "string" ? payload.runtimeSource.trim() || null : null
39
+ };
40
+ } catch {
41
+ return {
42
+ reachable: config.tls,
43
+ version: null,
44
+ runtimeSource: null
45
+ };
46
+ }
47
+ }
48
+
49
+ //#endregion
50
+ export { probeRunningGatewayRuntimeIdentity };
@@ -12,6 +12,7 @@ import { stylePromptMessage } from "../../terminal/prompt-style.js";
12
12
  import { DEFAULT_GIT_CHANNEL, DEFAULT_PACKAGE_CHANNEL, channelToNpmTag, normalizeUpdateChannel } from "../../infra/update-channels.js";
13
13
  import { cleanupGlobalRenameDirs, globalInstallArgs, resolveGlobalPackageRoot, resolveNodeModulesRootForPackageRoot } from "../../infra/update-global.js";
14
14
  import { checkUpdateStatus, compareSemverStrings, resolveNpmChannelTag } from "../../infra/update-check.js";
15
+ import { probeGateway } from "../../gateway/probe.js";
15
16
  import { runDaemonInstall } from "../daemon-cli/install.js";
16
17
  import { renderRestartDiagnostics, terminateStaleGatewayPids, waitForGatewayHealthyRestart } from "../daemon-cli/restart-health.js";
17
18
  import { runDaemonRestart } from "../daemon-cli/lifecycle.js";
@@ -24,6 +25,7 @@ import { checkShellCompletionStatus, ensureCompletionCacheExists } from "../../c
24
25
  import { runPostUpdateDoctorRepair } from "../../commands/doctor-update.js";
25
26
  import { doctorCommand } from "../../commands/doctor.js";
26
27
  import { DEFAULT_PACKAGE_NAME, createGlobalCommandRunner, ensureGitCheckout, normalizeTag, parseTimeoutMsOrExit, resolveGitInstallDir, resolveGlobalManager, resolveNodeRunner, resolveTargetVersion, resolveUpdateRoot, runUpdateStep } from "./shared.js";
28
+ import { probeRunningGatewayRuntimeIdentity } from "../lightweight/gateway-runtime-probe.js";
27
29
  import { createUpdateProgress, printResult } from "./progress.js";
28
30
  import { prepareRestartScript, runRestartScript } from "./restart-helper.js";
29
31
  import { resolveUpdateGatewayServiceTarget } from "./service-target.js";
@@ -58,6 +60,36 @@ function printUpdateLifecycleTimings(timings, jsonMode) {
58
60
  }
59
61
  defaultRuntime.log("");
60
62
  }
63
+ async function verifyGatewayRuntimeVersion(params) {
64
+ const probe = await probeGateway({
65
+ url: params.rpc.url,
66
+ auth: {
67
+ token: params.rpc.token,
68
+ password: params.rpc.password
69
+ },
70
+ tlsFingerprint: params.rpc.tlsFingerprint,
71
+ timeoutMs: Math.max(params.rpc.timeoutMs, 3e3)
72
+ });
73
+ const actualVersion = probe.server?.version?.trim() || null;
74
+ if (!probe.ok) {
75
+ return {
76
+ ok: false,
77
+ actualVersion,
78
+ error: probe.error ?? "gateway probe failed"
79
+ };
80
+ }
81
+ if (actualVersion !== params.expectedVersion) {
82
+ return {
83
+ ok: false,
84
+ actualVersion,
85
+ error: `running gateway version ${actualVersion ?? "unknown"} does not match installed version ${params.expectedVersion}`
86
+ };
87
+ }
88
+ return {
89
+ ok: true,
90
+ actualVersion
91
+ };
92
+ }
61
93
  const UPDATE_QUIPS = [
62
94
  "Leveled up! New skills unlocked. You're welcome.",
63
95
  "Fresh code, same agent. Miss me?",
@@ -546,6 +578,22 @@ async function maybeRestartService(params) {
546
578
  rpc: params.rpc
547
579
  }));
548
580
  }
581
+ if (health.healthy && params.expectedVersion) {
582
+ const identity = await measureUpdateStage(timings, "gateway version verification", () => verifyGatewayRuntimeVersion({
583
+ expectedVersion: params.expectedVersion,
584
+ rpc: params.rpc
585
+ }));
586
+ if (!identity.ok) {
587
+ health = {
588
+ ...health,
589
+ healthy: false
590
+ };
591
+ partial = true;
592
+ if (!params.opts.json) {
593
+ defaultRuntime.log(theme.warn(`Gateway runtime verification failed: ${identity.error}`));
594
+ }
595
+ }
596
+ }
549
597
  if (health.healthy) {
550
598
  if (!params.opts.json) {
551
599
  defaultRuntime.log(theme.success("Daemon restart completed."));
@@ -754,6 +802,77 @@ async function updateCommand(opts) {
754
802
  }
755
803
  }
756
804
  if (alreadyCurrent && currentVersion && targetVersion) {
805
+ const runningRuntime = await probeRunningGatewayRuntimeIdentity({ timeoutMs: 750 });
806
+ if (runningRuntime.reachable && runningRuntime.version !== currentVersion) {
807
+ if (!shouldRestart) {
808
+ defaultRuntime.error(`Installed version is ${currentVersion}, but the running gateway reports ${runningRuntime.version ?? "an older runtime without identity"}. Re-run without --no-restart.`);
809
+ defaultRuntime.exit(1);
810
+ return;
811
+ }
812
+ const serviceTarget = await resolveUpdateGatewayServiceTarget();
813
+ const serviceLoaded = await serviceTarget.service.isLoaded({ env: process.env });
814
+ if (!serviceLoaded) {
815
+ defaultRuntime.error(`Installed version is ${currentVersion}, but a different unmanaged gateway is running. Stop it, then run ${formatCliCommand("fased gateway install --force")} and ${formatCliCommand("fased gateway restart")}.`);
816
+ defaultRuntime.exit(1);
817
+ return;
818
+ }
819
+ if (!opts.json) {
820
+ defaultRuntime.log(theme.warn(`Installed files are current, but gateway runtime is ${runningRuntime.version ?? "legacy/unknown"}. Refreshing the managed service...`));
821
+ }
822
+ if (serviceTarget.scope === "system") {
823
+ await serviceTarget.service.restart({
824
+ env: process.env,
825
+ stdout: process.stdout
826
+ });
827
+ } else {
828
+ const restarted = await runDaemonRestart();
829
+ if (!restarted) {
830
+ defaultRuntime.error("Gateway service restart did not start.");
831
+ defaultRuntime.exit(1);
832
+ return;
833
+ }
834
+ }
835
+ const gatewayPort = resolveGatewayPort(configSnapshot.valid ? configSnapshot.config : undefined);
836
+ const gatewayConfig = configSnapshot.valid ? configSnapshot.config.gateway : undefined;
837
+ const tlsRuntime = await loadGatewayTlsRuntime(gatewayConfig?.tls);
838
+ const rpc = {
839
+ url: `${tlsRuntime.enabled ? "wss" : "ws"}://127.0.0.1:${gatewayPort}`,
840
+ token: gatewayConfig?.auth?.token,
841
+ password: gatewayConfig?.auth?.password,
842
+ tlsFingerprint: tlsRuntime.enabled ? tlsRuntime.fingerprintSha256 : undefined,
843
+ timeoutMs: 1500
844
+ };
845
+ const health = await waitForGatewayHealthyRestart({
846
+ service: serviceTarget.service,
847
+ port: gatewayPort,
848
+ rpc
849
+ });
850
+ const identity = health.healthy ? await verifyGatewayRuntimeVersion({
851
+ expectedVersion: currentVersion,
852
+ rpc
853
+ }) : {
854
+ ok: false,
855
+ actualVersion: null,
856
+ error: "gateway did not become healthy"
857
+ };
858
+ if (!identity.ok) {
859
+ defaultRuntime.error(`Gateway runtime refresh failed: ${identity.error}`);
860
+ defaultRuntime.exit(1);
861
+ return;
862
+ }
863
+ if (opts.json) {
864
+ defaultRuntime.log(JSON.stringify({
865
+ status: "repaired",
866
+ currentVersion,
867
+ targetVersion,
868
+ gatewayVersion: identity.actualVersion,
869
+ channel
870
+ }, null, 2));
871
+ } else {
872
+ defaultRuntime.log(`Gateway runtime refreshed: ${identity.actualVersion}`);
873
+ }
874
+ return;
875
+ }
757
876
  if (opts.json) {
758
877
  defaultRuntime.log(JSON.stringify({
759
878
  status: "current",
@@ -847,6 +966,7 @@ async function updateCommand(opts) {
847
966
  restartScriptPath,
848
967
  serviceTarget,
849
968
  serviceLoaded,
969
+ expectedVersion: result.after?.version ?? targetVersion,
850
970
  rpc: restartRpc
851
971
  });
852
972
  if (!restartResult.healthy) {
@@ -8,6 +8,7 @@ import { formatSessionArchiveTimestamp, isPrimarySessionTranscriptFileName } fro
8
8
  import { resolveMainSessionKey } from "../config/sessions/main-session.js";
9
9
  import { resolveSessionFilePath, resolveSessionFilePathOptions, resolveSessionTranscriptsDirForAgent, resolveStorePath } from "../config/sessions/paths.js";
10
10
  import { loadSessionStore } from "../config/sessions/store.js";
11
+ import { ensureSessionTranscriptHeader } from "../config/sessions/transcript.js";
11
12
  import "../config/sessions.js";
12
13
  import { note } from "../terminal/note.js";
13
14
  import fs from "node:fs";
@@ -381,9 +382,31 @@ async function noteStateIntegrity(cfg, prompter, configPath) {
381
382
  warnings.push([
382
383
  `- ${missing.length}/${recentTranscriptCandidates.length} recent sessions are missing transcripts.`,
383
384
  ` Verify sessions in store: ${formatCliCommand(`fased sessions --store "${absoluteStorePath}"`)}`,
384
- ` Preview cleanup impact: ${formatCliCommand(`fased sessions cleanup --store "${absoluteStorePath}" --dry-run`)}`,
385
- ` Prune missing entries: ${formatCliCommand(`fased sessions cleanup --store "${absoluteStorePath}" --enforce --fix-missing`)}`
385
+ ` Preserve entries and rebuild empty transcript headers: ${formatCliCommand("fased doctor --fix")}`,
386
+ " Earlier missing transcript content cannot be reconstructed."
386
387
  ].join("\n"));
388
+ const repairMissing = prompter.shouldRepair === true || await prompter.confirmSkipInNonInteractive({
389
+ message: "Recreate missing transcript headers while preserving session entries?",
390
+ initialValue: true
391
+ });
392
+ if (repairMissing) {
393
+ for (const [, entry] of missing) {
394
+ const sessionId = entry.sessionId;
395
+ if (!sessionId) {
396
+ continue;
397
+ }
398
+ const transcriptPath = resolveSessionFilePath(sessionId, entry, sessionPathOpts);
399
+ const repair = ensureSessionTranscriptHeader({
400
+ sessionFile: transcriptPath,
401
+ sessionId
402
+ });
403
+ if (repair.error) {
404
+ warnings.push(`- Failed to recreate transcript header ${shortenHomePath(transcriptPath)}: ${repair.error}`);
405
+ } else if (repair.created) {
406
+ changes.push(`- Recreated missing transcript header for session ${sessionId}; prior transcript content was unavailable`);
407
+ }
408
+ }
409
+ }
387
410
  }
388
411
  const mainKey = resolveMainSessionKey(cfg);
389
412
  const mainEntry = store[mainKey];
@@ -64,22 +64,30 @@ async function resolveSessionTranscriptFile(params) {
64
64
  activeSessionKey: params.activeSessionKey
65
65
  });
66
66
  }
67
- async function ensureSessionHeader(params) {
67
+ function ensureSessionTranscriptHeader(params) {
68
68
  if (fs.existsSync(params.sessionFile)) {
69
- return;
69
+ return { created: false };
70
+ }
71
+ try {
72
+ fs.mkdirSync(path.dirname(params.sessionFile), { recursive: true });
73
+ const header = {
74
+ type: "session",
75
+ version: CURRENT_SESSION_VERSION,
76
+ id: params.sessionId,
77
+ timestamp: new Date().toISOString(),
78
+ cwd: process.cwd()
79
+ };
80
+ fs.writeFileSync(params.sessionFile, `${JSON.stringify(header)}\n`, {
81
+ encoding: "utf-8",
82
+ mode: 384
83
+ });
84
+ return { created: true };
85
+ } catch (error) {
86
+ return {
87
+ created: false,
88
+ error: error instanceof Error ? error.message : String(error)
89
+ };
70
90
  }
71
- await fs.promises.mkdir(path.dirname(params.sessionFile), { recursive: true });
72
- const header = {
73
- type: "session",
74
- version: CURRENT_SESSION_VERSION,
75
- id: params.sessionId,
76
- timestamp: new Date().toISOString(),
77
- cwd: process.cwd()
78
- };
79
- await fs.promises.writeFile(params.sessionFile, `${JSON.stringify(header)}\n`, {
80
- encoding: "utf-8",
81
- mode: 384
82
- });
83
91
  }
84
92
  async function appendAssistantMessageToSessionTranscript(params) {
85
93
  const sessionKey = params.sessionKey.trim();
@@ -126,10 +134,16 @@ async function appendAssistantMessageToSessionTranscript(params) {
126
134
  reason: err instanceof Error ? err.message : String(err)
127
135
  };
128
136
  }
129
- await ensureSessionHeader({
137
+ const ensured = ensureSessionTranscriptHeader({
130
138
  sessionFile,
131
139
  sessionId: entry.sessionId
132
140
  });
141
+ if (ensured.error) {
142
+ return {
143
+ ok: false,
144
+ reason: ensured.error
145
+ };
146
+ }
133
147
  const sessionManager = SessionManager.open(sessionFile);
134
148
  const message = {
135
149
  role: "assistant",
@@ -171,4 +185,4 @@ async function appendAssistantMessageToSessionTranscript(params) {
171
185
  }
172
186
 
173
187
  //#endregion
174
- export { appendAssistantMessageToSessionTranscript, resolveMirroredTranscriptText, resolveSessionTranscriptFile };
188
+ export { appendAssistantMessageToSessionTranscript, ensureSessionTranscriptHeader, resolveMirroredTranscriptText, resolveSessionTranscriptFile };
@@ -10,7 +10,7 @@ import { deriveSessionKey, resolveSessionKey } from "./sessions/session-key.js";
10
10
  import { enforceSessionDiskBudget } from "./sessions/disk-budget.js";
11
11
  import { capEntryCount, clearSessionStoreCacheForTest, drainSessionStoreLockQueuesForTest, getActiveSessionMaintenanceWarning, getSessionStoreLockQueueSizeForTest, loadSessionStore, pruneStaleEntries, readSessionUpdatedAt, recordSessionMetaFromInbound, resolveMaintenanceConfig, rotateSessionFile, saveSessionStore, updateLastRoute, updateSessionStore, updateSessionStoreEntry, withSessionStoreLockForTest } from "./sessions/store.js";
12
12
  import { resolveAndPersistSessionFile } from "./sessions/session-file.js";
13
- import { appendAssistantMessageToSessionTranscript, resolveMirroredTranscriptText, resolveSessionTranscriptFile } from "./sessions/transcript.js";
13
+ import { appendAssistantMessageToSessionTranscript, ensureSessionTranscriptHeader, resolveMirroredTranscriptText, resolveSessionTranscriptFile } from "./sessions/transcript.js";
14
14
  import { extractDeliveryInfo, parseSessionThreadInfo } from "./sessions/delivery-info.js";
15
15
 
16
- export { DEFAULT_IDLE_MINUTES, DEFAULT_RESET_AT_HOUR, DEFAULT_RESET_MODE, DEFAULT_RESET_TRIGGER, DEFAULT_RESET_TRIGGERS, SAFE_SESSION_ID_RE, appendAssistantMessageToSessionTranscript, buildGroupDisplayName, canonicalizeMainSessionAlias, capEntryCount, clearSessionStoreCacheForTest, deriveGroupSessionPatch, deriveSessionKey, deriveSessionMetaPatch, deriveSessionOrigin, drainSessionStoreLockQueuesForTest, enforceSessionDiskBudget, evaluateSessionFreshness, extractDeliveryInfo, formatSessionArchiveTimestamp, getActiveSessionMaintenanceWarning, getSessionStoreLockQueueSizeForTest, isPrimarySessionTranscriptFileName, isSessionArchiveArtifactName, isSessionCompactionCheckpointArtifactName, isSessionTotalTokensFresh, isThreadSessionKey, loadSessionStore, mergeSessionEntry, normalizeSessionRuntimeModelFields, parseSessionArchiveTimestamp, parseSessionThreadInfo, pruneStaleEntries, readSessionUpdatedAt, recordSessionMetaFromInbound, resolveAgentIdFromSessionKey, resolveAgentMainSessionKey, resolveAndPersistSessionFile, resolveChannelResetConfig, resolveDailyResetAtMs, resolveDefaultSessionStorePath, resolveExplicitAgentSessionKey, resolveFreshSessionTotalTokens, resolveGroupSessionKey, resolveMainSessionKey, resolveMainSessionKeyFromConfig, resolveMaintenanceConfig, resolveMirroredTranscriptText, resolveSessionFilePath, resolveSessionFilePathOptions, resolveSessionKey, resolveSessionResetPolicy, resolveSessionResetType, resolveSessionTranscriptFile, resolveSessionTranscriptPath, resolveSessionTranscriptPathInDir, resolveSessionTranscriptsDir, resolveSessionTranscriptsDirForAgent, resolveStorePath, resolveThreadFlag, rotateSessionFile, saveSessionStore, setSessionRuntimeModel, snapshotSessionOrigin, updateLastRoute, updateSessionStore, updateSessionStoreEntry, validateSessionId, withSessionStoreLockForTest };
16
+ export { DEFAULT_IDLE_MINUTES, DEFAULT_RESET_AT_HOUR, DEFAULT_RESET_MODE, DEFAULT_RESET_TRIGGER, DEFAULT_RESET_TRIGGERS, SAFE_SESSION_ID_RE, appendAssistantMessageToSessionTranscript, buildGroupDisplayName, canonicalizeMainSessionAlias, capEntryCount, clearSessionStoreCacheForTest, deriveGroupSessionPatch, deriveSessionKey, deriveSessionMetaPatch, deriveSessionOrigin, drainSessionStoreLockQueuesForTest, enforceSessionDiskBudget, ensureSessionTranscriptHeader, evaluateSessionFreshness, extractDeliveryInfo, formatSessionArchiveTimestamp, getActiveSessionMaintenanceWarning, getSessionStoreLockQueueSizeForTest, isPrimarySessionTranscriptFileName, isSessionArchiveArtifactName, isSessionCompactionCheckpointArtifactName, isSessionTotalTokensFresh, isThreadSessionKey, loadSessionStore, mergeSessionEntry, normalizeSessionRuntimeModelFields, parseSessionArchiveTimestamp, parseSessionThreadInfo, pruneStaleEntries, readSessionUpdatedAt, recordSessionMetaFromInbound, resolveAgentIdFromSessionKey, resolveAgentMainSessionKey, resolveAndPersistSessionFile, resolveChannelResetConfig, resolveDailyResetAtMs, resolveDefaultSessionStorePath, resolveExplicitAgentSessionKey, resolveFreshSessionTotalTokens, resolveGroupSessionKey, resolveMainSessionKey, resolveMainSessionKeyFromConfig, resolveMaintenanceConfig, resolveMirroredTranscriptText, resolveSessionFilePath, resolveSessionFilePathOptions, resolveSessionKey, resolveSessionResetPolicy, resolveSessionResetType, resolveSessionTranscriptFile, resolveSessionTranscriptPath, resolveSessionTranscriptPathInDir, resolveSessionTranscriptsDir, resolveSessionTranscriptsDirForAgent, resolveStorePath, resolveThreadFlag, rotateSessionFile, saveSessionStore, setSessionRuntimeModel, snapshotSessionOrigin, updateLastRoute, updateSessionStore, updateSessionStoreEntry, validateSessionId, withSessionStoreLockForTest };