@undefineds.co/linx 0.3.4 → 0.3.7

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 (172) hide show
  1. package/README.md +58 -23
  2. package/dist/generated/version.js +1 -1
  3. package/dist/generated/version.js.map +1 -1
  4. package/dist/index.js +334 -162
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/account-session.js +4 -8
  7. package/dist/lib/account-session.js.map +1 -1
  8. package/dist/lib/ai-command.js +228 -178
  9. package/dist/lib/ai-command.js.map +1 -1
  10. package/dist/lib/auto-mode/archive.js +38 -7
  11. package/dist/lib/auto-mode/archive.js.map +1 -1
  12. package/dist/lib/auto-mode/auth.js.map +1 -1
  13. package/dist/lib/auto-mode/display.js +71 -45
  14. package/dist/lib/auto-mode/display.js.map +1 -1
  15. package/dist/lib/auto-mode/format.js +9 -7
  16. package/dist/lib/auto-mode/format.js.map +1 -1
  17. package/dist/lib/auto-mode/hooks/claude.js +12 -2
  18. package/dist/lib/auto-mode/hooks/claude.js.map +1 -1
  19. package/dist/lib/auto-mode/hooks/codex.js +17 -7
  20. package/dist/lib/auto-mode/hooks/codex.js.map +1 -1
  21. package/dist/lib/auto-mode/hooks/index.js +28 -8
  22. package/dist/lib/auto-mode/hooks/index.js.map +1 -1
  23. package/dist/lib/auto-mode/pod-ai.js +20 -37
  24. package/dist/lib/auto-mode/pod-ai.js.map +1 -1
  25. package/dist/lib/auto-mode/pod-approval.js +124 -195
  26. package/dist/lib/auto-mode/pod-approval.js.map +1 -1
  27. package/dist/lib/auto-mode/pod-persistence.js +169 -90
  28. package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
  29. package/dist/lib/auto-mode/runner.js +683 -81
  30. package/dist/lib/auto-mode/runner.js.map +1 -1
  31. package/dist/lib/auto-mode/secretary.js +186 -41
  32. package/dist/lib/auto-mode/secretary.js.map +1 -1
  33. package/dist/lib/auto-mode-command.js +32 -32
  34. package/dist/lib/auto-mode-command.js.map +1 -1
  35. package/dist/lib/chat-api.js +242 -50
  36. package/dist/lib/chat-api.js.map +1 -1
  37. package/dist/lib/codex-plugin/bridge.js +164 -17
  38. package/dist/lib/codex-plugin/bridge.js.map +1 -1
  39. package/dist/lib/codex-plugin/codex-native-proxy.js +370 -34
  40. package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
  41. package/dist/lib/credentials-store.js +33 -42
  42. package/dist/lib/credentials-store.js.map +1 -1
  43. package/dist/lib/linx-cloud-errors.js +61 -0
  44. package/dist/lib/linx-cloud-errors.js.map +1 -0
  45. package/dist/lib/linx-tui-contract.js +8 -5
  46. package/dist/lib/linx-tui-contract.js.map +1 -1
  47. package/dist/lib/login-command.js +9 -2
  48. package/dist/lib/login-command.js.map +1 -1
  49. package/dist/lib/models.js +3 -20
  50. package/dist/lib/models.js.map +1 -1
  51. package/dist/lib/oidc-auth.js +143 -17
  52. package/dist/lib/oidc-auth.js.map +1 -1
  53. package/dist/lib/oidc-session-storage.js +2 -6
  54. package/dist/lib/oidc-session-storage.js.map +1 -1
  55. package/dist/lib/pi-adapter/auto-input-controller.js +988 -0
  56. package/dist/lib/pi-adapter/auto-input-controller.js.map +1 -0
  57. package/dist/lib/pi-adapter/backend-command.js +2 -0
  58. package/dist/lib/pi-adapter/backend-command.js.map +1 -0
  59. package/dist/lib/pi-adapter/backend-credentials.js +80 -0
  60. package/dist/lib/pi-adapter/backend-credentials.js.map +1 -0
  61. package/dist/lib/pi-adapter/branding.js +246 -108
  62. package/dist/lib/pi-adapter/branding.js.map +1 -1
  63. package/dist/lib/pi-adapter/control-state.js +72 -0
  64. package/dist/lib/pi-adapter/control-state.js.map +1 -0
  65. package/dist/lib/pi-adapter/interactive.js +2634 -30
  66. package/dist/lib/pi-adapter/interactive.js.map +1 -1
  67. package/dist/lib/pi-adapter/pod-approval.js +382 -210
  68. package/dist/lib/pi-adapter/pod-approval.js.map +1 -1
  69. package/dist/lib/pi-adapter/pod-mirror-mapping.js +71 -17
  70. package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +1 -1
  71. package/dist/lib/pi-adapter/pod-mirror.js +531 -64
  72. package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
  73. package/dist/lib/pi-adapter/pod-native.js +81 -85
  74. package/dist/lib/pi-adapter/pod-native.js.map +1 -1
  75. package/dist/lib/pi-adapter/pod-status-output.js +54 -0
  76. package/dist/lib/pi-adapter/pod-status-output.js.map +1 -0
  77. package/dist/lib/pi-adapter/runtime.js +458 -228
  78. package/dist/lib/pi-adapter/runtime.js.map +1 -1
  79. package/dist/lib/pi-adapter/session-control.js +509 -0
  80. package/dist/lib/pi-adapter/session-control.js.map +1 -0
  81. package/dist/lib/pi-adapter/session.js +35 -22
  82. package/dist/lib/pi-adapter/session.js.map +1 -1
  83. package/dist/lib/pi-adapter/stream.js +89 -32
  84. package/dist/lib/pi-adapter/stream.js.map +1 -1
  85. package/dist/lib/pi-adapter/sync-recovery.js +89 -0
  86. package/dist/lib/pi-adapter/sync-recovery.js.map +1 -0
  87. package/dist/lib/pi-adapter/web-fetch.js +13 -14
  88. package/dist/lib/pi-adapter/web-fetch.js.map +1 -1
  89. package/dist/lib/pod-chat-store.js +254 -78
  90. package/dist/lib/pod-chat-store.js.map +1 -1
  91. package/dist/lib/pod-data-session.js +156 -35
  92. package/dist/lib/pod-data-session.js.map +1 -1
  93. package/dist/lib/solid-auth-store.js +27 -0
  94. package/dist/lib/solid-auth-store.js.map +1 -0
  95. package/dist/lib/solid-auth.js +2 -4
  96. package/dist/lib/solid-auth.js.map +1 -1
  97. package/dist/lib/solid-client-credentials-login.js +100 -0
  98. package/dist/lib/solid-client-credentials-login.js.map +1 -0
  99. package/dist/lib/solid-local-store.js +31 -0
  100. package/dist/lib/solid-local-store.js.map +1 -0
  101. package/dist/lib/symphony/archive.js +328 -18
  102. package/dist/lib/symphony/archive.js.map +1 -1
  103. package/dist/lib/symphony/pod-projection.js +2222 -0
  104. package/dist/lib/symphony/pod-projection.js.map +1 -0
  105. package/dist/lib/symphony-command.js +602 -178
  106. package/dist/lib/symphony-command.js.map +1 -1
  107. package/dist/lib/sync-checkpoint-store.js +74 -0
  108. package/dist/lib/sync-checkpoint-store.js.map +1 -0
  109. package/dist/skills/symphony/SKILL.md +665 -0
  110. package/package.json +15 -9
  111. package/vendor/agent-runtime/dist/agent-runtime.d.ts +137 -0
  112. package/vendor/agent-runtime/dist/agent-runtime.js +211 -0
  113. package/vendor/agent-runtime/dist/auto-mode.d.ts +78 -13
  114. package/vendor/agent-runtime/dist/auto-mode.js +288 -31
  115. package/vendor/agent-runtime/dist/control-plane.d.ts +28 -0
  116. package/vendor/agent-runtime/dist/control-plane.js +79 -0
  117. package/vendor/agent-runtime/dist/file-sync.d.ts +157 -0
  118. package/vendor/agent-runtime/dist/file-sync.js +314 -0
  119. package/vendor/agent-runtime/dist/index.d.ts +7 -0
  120. package/vendor/agent-runtime/dist/index.js +7 -0
  121. package/vendor/agent-runtime/dist/reconciler.d.ts +117 -0
  122. package/vendor/agent-runtime/dist/reconciler.js +361 -0
  123. package/vendor/agent-runtime/dist/symphony.d.ts +128 -8
  124. package/vendor/agent-runtime/dist/symphony.js +362 -57
  125. package/vendor/agent-runtime/dist/sync.d.ts +271 -0
  126. package/vendor/agent-runtime/dist/sync.js +550 -0
  127. package/vendor/agent-runtime/dist/thread-reconciler-controller.d.ts +58 -0
  128. package/vendor/agent-runtime/dist/thread-reconciler-controller.js +137 -0
  129. package/vendor/agent-runtime/dist/turn-controller.js +2 -2
  130. package/vendor/agent-runtime/dist/wake-scheduler.d.ts +67 -0
  131. package/vendor/agent-runtime/dist/wake-scheduler.js +194 -0
  132. package/vendor/agent-runtime/package.json +8 -1
  133. package/vendor/pi-web-access/CHANGELOG.md +387 -0
  134. package/vendor/pi-web-access/LICENSE +21 -0
  135. package/vendor/pi-web-access/README.md +352 -0
  136. package/vendor/pi-web-access/activity.ts +101 -0
  137. package/vendor/pi-web-access/banner.png +0 -0
  138. package/vendor/pi-web-access/chrome-cookies.ts +322 -0
  139. package/vendor/pi-web-access/code-search.ts +107 -0
  140. package/vendor/pi-web-access/curator-page.ts +3359 -0
  141. package/vendor/pi-web-access/curator-server.ts +605 -0
  142. package/vendor/pi-web-access/exa.ts +520 -0
  143. package/vendor/pi-web-access/extract.ts +641 -0
  144. package/vendor/pi-web-access/gemini-api.ts +112 -0
  145. package/vendor/pi-web-access/gemini-search.ts +361 -0
  146. package/vendor/pi-web-access/gemini-url-context.ts +126 -0
  147. package/vendor/pi-web-access/gemini-web-config.ts +52 -0
  148. package/vendor/pi-web-access/gemini-web.ts +396 -0
  149. package/vendor/pi-web-access/github-api.ts +196 -0
  150. package/vendor/pi-web-access/github-extract.ts +634 -0
  151. package/vendor/pi-web-access/index.ts +2346 -0
  152. package/vendor/pi-web-access/package.json +45 -0
  153. package/vendor/pi-web-access/pdf-extract.ts +192 -0
  154. package/vendor/pi-web-access/perplexity.ts +195 -0
  155. package/vendor/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
  156. package/vendor/pi-web-access/rsc-extract.ts +338 -0
  157. package/vendor/pi-web-access/skills/librarian/SKILL.md +195 -0
  158. package/vendor/pi-web-access/storage.ts +72 -0
  159. package/vendor/pi-web-access/summary-review.ts +276 -0
  160. package/vendor/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +41 -0
  161. package/vendor/pi-web-access/test/pdf-extract.test.mjs +95 -0
  162. package/vendor/pi-web-access/utils.ts +44 -0
  163. package/vendor/pi-web-access/video-extract.ts +378 -0
  164. package/vendor/pi-web-access/youtube-extract.ts +310 -0
  165. package/dist/lib/pi-adapter/auth.js +0 -68
  166. package/dist/lib/pi-adapter/auth.js.map +0 -1
  167. package/dist/lib/pi-adapter/pod-tools.js +0 -140
  168. package/dist/lib/pi-adapter/pod-tools.js.map +0 -1
  169. package/dist/skills/drizzle-solid/SKILL.md +0 -340
  170. package/dist/skills/pod-storage/SKILL.md +0 -100
  171. package/dist/skills/solid-modeling/SKILL.md +0 -274
  172. package/dist/skills/xpod-componentsjs/SKILL.md +0 -284
@@ -1,23 +1,32 @@
1
- import { createPiAgentStreamAdapter } from './stream.js';
2
- import { resolveLinxPiCloudOAuthCredential } from './auth.js';
3
- import { ensureBrowserConsentLogin, isOidcLoginExpiredError } from '../oidc-auth.js';
1
+ import { createLinxAgentStreamAdapter } from './stream.js';
2
+ import { ensureBrowserConsentLogin, isOidcLoginExpiredError, isOidcTransientRemoteError } from '../oidc-auth.js';
4
3
  import { DEFAULT_LINX_CLOUD_MODEL_ID, FALLBACK_LINX_CLOUD_MODEL_IDS, resolvePreferredLinxCloudModelId } from '../default-model.js';
5
4
  import { ensureLinxPiTheme } from './theme.js';
6
- import { AuthStorage, ModelRegistry, SettingsManager, createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createBashToolDefinition, createCodingTools, createLocalBashOperations, } from '@mariozechner/pi-coding-agent';
7
- import { webFetchTool, webSearchTool } from './web-fetch.js';
8
- import { podReadTool, podWriteTool } from './pod-tools.js';
9
- import { existsSync } from 'node:fs';
10
- import { delimiter, dirname, join, resolve } from 'node:path';
5
+ import { AuthStorage, ModelRegistry, SettingsManager, createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createCodingTools, createLocalBashOperations, } from '@earendil-works/pi-coding-agent';
6
+ // web_fetch / web_search are now handled by pi-web-access
7
+ import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { basename, dirname, join, resolve } from 'node:path';
10
+ import { createRequire } from 'node:module';
11
11
  import { fileURLToPath } from 'node:url';
12
- import { isRemoteAuthExpiredError } from '../chat-api.js';
13
- import { installLinxPiRemoteApproval } from './pod-approval.js';
12
+ import { RemoteChatRequestError, isRemoteAuthExpiredError } from '../chat-api.js';
13
+ import { formatLinxCloudTransientMessage } from '../linx-cloud-errors.js';
14
+ import { clearDefaultPodDataSession, getDefaultPodDataSession } from '../pod-data-session.js';
15
+ import { loadCredentials } from '../credentials-store.js';
16
+ import { getSolidLinxAppDir, getSolidLinxPiWebAccessConfigPath } from '../solid-local-store.js';
14
17
  const UNDEFINEDS_PROVIDER_ID = 'undefineds';
15
- const UNDEFINEDS_PROVIDER_LABEL = 'undefineds';
16
- const UNDEFINEDS_PROVIDER_API = 'openai-completions';
18
+ const UNDEFINEDS_PROVIDER_LABEL = 'LinX Cloud';
19
+ const UNDEFINEDS_PROVIDER_API = 'linx-cloud-chat-completions';
17
20
  const UNDEFINEDS_SESSION_ID = 'undefineds_pi_frontend';
18
21
  const UNDEFINEDS_AUTH_BRIDGE_ID = 'undefineds-cloud-oauth-bridge';
22
+ export const LINX_RUNTIME_MANAGED_AUTH_KEY = 'linx-runtime-managed-auth';
19
23
  const LINX_PACKAGE_SOURCE = '@undefineds.co/linx';
24
+ const LINX_WEB_ACCESS_PACKAGE_SOURCE = 'pi-web-access';
25
+ const LINX_PRODUCT_SKILL_NAMES = new Set(['symphony']);
26
+ const MARKET_XPOD_CLI_SKILL_SOURCE = 'xpod-cli@undefineds';
20
27
  export const DEFAULT_LINX_PI_BASH_TIMEOUT_SECONDS = 15;
28
+ const DEFAULT_LINX_CLOUD_CONTEXT_WINDOW = 1_000_000;
29
+ const DEFAULT_LINX_CLOUD_COMPLETION_TIMEOUT_MS = 10 * 60 * 1000;
21
30
  export async function resolveLinxStartupLoginPromptDecision(options) {
22
31
  if (options.print) {
23
32
  return { shouldPrompt: false, reason: 'print-mode' };
@@ -25,10 +34,16 @@ export async function resolveLinxStartupLoginPromptDecision(options) {
25
34
  if (options.backend === 'native') {
26
35
  return { shouldPrompt: false, reason: 'native-backend' };
27
36
  }
28
- const resolveCredential = options.resolveCredential ?? resolveLinxPiCloudOAuthCredential;
37
+ if (!options.resolveSession) {
38
+ return (options.loadStoredCredentials ?? loadCredentials)()
39
+ ? { shouldPrompt: false, reason: 'credential-present' }
40
+ : { shouldPrompt: true, reason: 'missing-credential' };
41
+ }
42
+ const resolveSession = options.resolveSession;
43
+ let session = null;
29
44
  try {
30
- const credential = await resolveCredential(options.issuerUrl);
31
- return credential
45
+ session = await resolveSession();
46
+ return session
32
47
  ? { shouldPrompt: false, reason: 'credential-present' }
33
48
  : { shouldPrompt: true, reason: 'missing-credential' };
34
49
  }
@@ -36,8 +51,14 @@ export async function resolveLinxStartupLoginPromptDecision(options) {
36
51
  if (isOidcLoginExpiredError(error)) {
37
52
  return { shouldPrompt: true, reason: 'expired-credential' };
38
53
  }
54
+ if (isOidcTransientRemoteError(error) && (options.loadStoredCredentials ?? loadCredentials)()) {
55
+ return { shouldPrompt: false, reason: 'credential-present' };
56
+ }
39
57
  throw error;
40
58
  }
59
+ finally {
60
+ await session?.close().catch(() => undefined);
61
+ }
41
62
  }
42
63
  export function resolveLinxStartupLoginReason(decision) {
43
64
  if (!decision.shouldPrompt) {
@@ -51,8 +72,9 @@ export function resolveLinxInteractiveLoginReason(options) {
51
72
  }
52
73
  return resolveLinxStartupLoginReason(options.startupDecision);
53
74
  }
54
- export function createPiRuntimeAdapter(dependencies, options = {}) {
75
+ export function createLinxRuntimeAdapter(dependencies, options = {}) {
55
76
  const backendMode = options.backend ?? 'cloud';
77
+ const workerBackend = options.workerBackend ?? (backendMode === 'native' ? 'codex' : undefined);
56
78
  const cwd = options.cwd ?? process.cwd();
57
79
  const requestedModel = options.model?.trim() || undefined;
58
80
  let activeModelId = requestedModel ?? DEFAULT_LINX_CLOUD_MODEL_ID;
@@ -62,17 +84,22 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
62
84
  const proxy = backendMode === 'native'
63
85
  ? dependencies.createNativeProxy?.({
64
86
  cwd,
65
- model: activeModelId,
87
+ model: requestedModel,
66
88
  listenPort: options.port,
89
+ autoEnabled: options.autoEnabled,
90
+ codexApprovalPolicy: options.codexApprovalPolicy,
91
+ passthroughArgs: options.passthroughArgs,
92
+ env: options.backendEnv,
93
+ resolveEnv: options.resolveBackendEnv,
67
94
  })
68
95
  : null;
69
96
  if (backendMode === 'native' && !proxy) {
70
- throw new Error('Native Pi runtime backend requires createNativeProxy');
97
+ throw new Error('Native LinX runtime backend requires createNativeProxy');
71
98
  }
72
99
  if (backendMode === 'cloud' && !options.providerConfig?.oauth && !dependencies.createRemoteCompletion) {
73
- throw new Error('Cloud Pi runtime backend requires createRemoteCompletion');
100
+ throw new Error('Cloud LinX runtime backend requires createRemoteCompletion');
74
101
  }
75
- const streamAdapter = createPiAgentStreamAdapter({
102
+ const streamAdapter = createLinxAgentStreamAdapter({
76
103
  sessionId: proxy?.record.id ?? UNDEFINEDS_SESSION_ID,
77
104
  cwd: proxy?.record.cwd ?? cwd,
78
105
  model: proxy?.record.model ?? activeModelId,
@@ -89,30 +116,55 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
89
116
  completionBackend: !proxy && dependencies.createRemoteCompletion
90
117
  ? {
91
118
  async complete(input) {
92
- const apiKey = input.apiKey && input.apiKey !== 'linx-runtime-managed-auth'
93
- ? input.apiKey
94
- : await resolveLinxPiCloudApiKey({
119
+ const authFetch = options.providerConfig?.oauth
120
+ ? input.authFetch
121
+ ?? resolveRuntimeAuthFetchFromApiKey(input.apiKey)
122
+ ?? await resolveLinxPiCloudAuthFetch({
123
+ issuerUrl: options.providerConfig?.issuerUrl,
124
+ getPodDataSession: options.getPodDataSession,
125
+ })
126
+ : await resolveLinxPiCloudAuthFetch({
95
127
  issuerUrl: options.providerConfig?.issuerUrl,
96
- explicitApiKey: options.providerConfig?.apiKey,
128
+ getPodDataSession: options.getPodDataSession,
97
129
  });
98
- return completeWithAuthRecovery(apiKey, {
130
+ return completeWithAuthRecovery(authFetch, {
99
131
  runtimeUrl: baseUrl,
100
132
  model: input.model,
101
133
  messages: withSystemPrompt(input.systemPrompt, input.messages),
102
134
  tools: input.tools,
103
- reasoning: input.reasoning,
104
135
  signal: input.signal,
105
136
  });
106
137
  },
107
138
  }
108
139
  : undefined,
109
140
  });
141
+ const backendCommandRouter = proxy && typeof proxy.executeCommand === 'function'
142
+ ? {
143
+ backend: proxy.record.backend,
144
+ execute(input) {
145
+ return proxy.executeCommand(input);
146
+ },
147
+ setCwd: typeof proxy.setCwd === 'function'
148
+ ? (nextCwd) => proxy.setCwd(nextCwd)
149
+ : undefined,
150
+ subscribe(listener) {
151
+ return proxy.subscribe(listener);
152
+ },
153
+ setSessionControl: typeof proxy.setSessionControl === 'function'
154
+ ? (control) => proxy.setSessionControl(control)
155
+ : undefined,
156
+ }
157
+ : undefined;
110
158
  return {
111
159
  remoteUrl: proxy?.remoteUrl ?? baseUrl,
112
160
  sessionId: proxy?.record.id ?? UNDEFINEDS_SESSION_ID,
113
161
  cwd: proxy?.record.cwd ?? cwd,
114
162
  model: proxy?.record.model ?? activeModelId,
115
- backend: proxy?.record.backend ?? UNDEFINEDS_PROVIDER_ID,
163
+ backend: 'linx',
164
+ runtimeBackend: workerBackend,
165
+ autoEnabled: options.autoEnabled === true,
166
+ symphonyEnabled: options.symphonyEnabled === true,
167
+ backendCommandRouter,
116
168
  streamAdapter,
117
169
  createRuntime: async (context) => {
118
170
  const authStorage = AuthStorage.inMemory();
@@ -136,74 +188,86 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
136
188
  },
137
189
  manualRedirectUrl: callbacks.onManualCodeInput,
138
190
  });
191
+ clearDefaultPodDataSession();
139
192
  if (result.reusedExistingSession) {
140
193
  callbacks.onProgress?.('Reused existing LinX Cloud session.');
141
194
  }
142
- await syncProviderModels(result.tokenSet.accessToken ?? '', { throwAuthExpired: true });
195
+ const authFetch = await resolveLinxPiCloudAuthFetch({
196
+ issuerUrl: options.providerConfig?.issuerUrl,
197
+ getPodDataSession: options.getPodDataSession,
198
+ });
199
+ await syncProviderModels({ runtimeFetch: authFetch });
143
200
  return {
144
201
  refresh: result.tokenSet.refreshToken ?? '',
145
- access: result.tokenSet.accessToken ?? '',
202
+ access: LINX_RUNTIME_MANAGED_AUTH_KEY,
146
203
  expires: result.tokenSet.expiresAt ? result.tokenSet.expiresAt * 1000 : Date.now() + 60 * 60 * 1000,
147
204
  };
148
205
  },
149
- async refreshToken() {
150
- const refreshed = await resolveLinxPiCloudOAuthCredential(options.providerConfig?.issuerUrl);
151
- if (!refreshed) {
152
- throw new Error('Failed to refresh LinX cloud credential for Pi runtime adapter');
153
- }
154
- await syncProviderModels(refreshed.access);
155
- return refreshed;
206
+ async refreshToken(credentials) {
207
+ clearDefaultPodDataSession();
208
+ const authFetch = await resolveLinxPiCloudAuthFetch({
209
+ issuerUrl: options.providerConfig?.issuerUrl,
210
+ getPodDataSession: options.getPodDataSession,
211
+ });
212
+ await syncProviderModels({ runtimeFetch: authFetch });
213
+ return {
214
+ type: 'oauth',
215
+ refresh: credentials.refresh,
216
+ access: LINX_RUNTIME_MANAGED_AUTH_KEY,
217
+ expires: Date.now() + 60 * 60 * 1000,
218
+ };
156
219
  },
157
220
  getApiKey(credentials) {
158
- return credentials.access;
221
+ return credentials.access === LINX_RUNTIME_MANAGED_AUTH_KEY
222
+ ? LINX_RUNTIME_MANAGED_AUTH_KEY
223
+ : LINX_RUNTIME_MANAGED_AUTH_KEY;
159
224
  },
160
225
  };
161
- const resolvedOAuth = options.providerConfig?.oauth
162
- ? null
163
- : await resolveLinxPiCloudOAuthCredential(options.providerConfig?.issuerUrl).catch((error) => {
164
- if (isOidcLoginExpiredError(error)) {
165
- return null;
166
- }
167
- throw error;
168
- });
226
+ const storedCredentials = options.providerConfig?.oauth ? null : loadCredentials();
227
+ const hasManagedPodSession = !options.providerConfig?.oauth && Boolean(options.getPodDataSession);
169
228
  const explicitOAuthCredential = options.providerConfig?.oauth
170
229
  ? await options.providerConfig.oauth.login()
171
230
  : null;
172
- const explicitApiKey = options.providerConfig?.apiKey;
173
- const authMode = options.providerConfig?.oauth || resolvedOAuth || !explicitApiKey ? 'oauth' : 'apiKey';
174
- if (resolvedOAuth?.access) {
175
- await syncProviderModels(resolvedOAuth.access, { refreshOnAuthExpired: true });
231
+ if (storedCredentials || hasManagedPodSession) {
232
+ const authFetch = await resolveLinxPiCloudAuthFetch({
233
+ issuerUrl: options.providerConfig?.issuerUrl,
234
+ getPodDataSession: options.getPodDataSession,
235
+ });
236
+ await syncProviderModels({ runtimeFetch: authFetch }, { refreshOnAuthExpired: true });
176
237
  }
177
238
  else if (explicitOAuthCredential?.access) {
178
- await syncProviderModels(explicitOAuthCredential.access);
179
- }
180
- else if (explicitApiKey) {
181
- await syncProviderModels(explicitApiKey);
239
+ await syncProviderModels({ runtimeFetch: createBearerAuthFetch(explicitOAuthCredential.access) });
182
240
  }
183
241
  modelRegistry.registerProvider(UNDEFINEDS_PROVIDER_ID, {
184
242
  api: UNDEFINEDS_PROVIDER_API,
185
243
  baseUrl,
186
- apiKey: 'LINX_RUNTIME_AUTH',
244
+ apiKey: '$LINX_RUNTIME_AUTH',
187
245
  oauth: linxOAuthProvider,
188
246
  authHeader: false,
189
247
  streamSimple: streamAdapter.streamFn,
190
248
  models: providerModels,
191
249
  });
192
- if (!options.providerConfig?.oauth && !resolvedOAuth && !explicitApiKey) {
193
- authStorage.setRuntimeApiKey(UNDEFINEDS_PROVIDER_ID, 'linx-runtime-managed-auth');
250
+ if (!options.providerConfig?.oauth) {
251
+ authStorage.setRuntimeApiKey(UNDEFINEDS_PROVIDER_ID, LINX_RUNTIME_MANAGED_AUTH_KEY);
194
252
  }
195
253
  if (options.providerConfig?.oauth && explicitOAuthCredential) {
196
254
  authStorage.set(UNDEFINEDS_PROVIDER_ID, { type: 'oauth', ...explicitOAuthCredential });
197
255
  }
198
- else if (resolvedOAuth) {
199
- authStorage.set(UNDEFINEDS_PROVIDER_ID, resolvedOAuth);
200
- }
201
256
  const settingsManager = SettingsManager.create(context.cwd, context.agentDir);
202
257
  ensureLinxPiTheme(context.agentDir);
258
+ ensurePiWebAccessConfig();
203
259
  settingsManager.setTheme('linx');
204
260
  const defaultModelId = sanitizeLinxCloudDefaults(settingsManager, requestedModel, providerModels);
205
261
  activeModelId = defaultModelId;
206
262
  const bundledSkillsDir = resolveBundledLinxSkillsDir();
263
+ const marketSkillDirs = resolveInstalledMarketSkillDirs();
264
+ const additionalSkillPaths = [
265
+ ...(bundledSkillsDir ? [bundledSkillsDir] : []),
266
+ ...marketSkillDirs,
267
+ ];
268
+ const bundledPackagePaths = [
269
+ resolveBundledPiPackageRoot(LINX_WEB_ACCESS_PACKAGE_SOURCE),
270
+ ].filter((path) => Boolean(path));
207
271
  const services = await createAgentSessionServices({
208
272
  cwd: context.cwd,
209
273
  agentDir: context.agentDir,
@@ -211,48 +275,29 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
211
275
  settingsManager,
212
276
  modelRegistry,
213
277
  resourceLoaderOptions: {
214
- additionalSkillPaths: bundledSkillsDir ? [bundledSkillsDir] : [],
215
- skillsOverride: bundledSkillsDir
216
- ? (base) => withBundledLinxSkillSourceInfo(base, bundledSkillsDir)
217
- : undefined,
278
+ // Built-in: pi-web-access handles web_search, fetch_content, and related web tools.
279
+ additionalExtensionPaths: bundledPackagePaths,
280
+ additionalSkillPaths,
281
+ skillsOverride: (base) => withLinxSkillSourceInfo(base, {
282
+ bundledSkillsDir,
283
+ marketSkillDirs,
284
+ }),
218
285
  systemPromptOverride: overrideLinxSystemPrompt,
219
286
  },
220
287
  });
221
288
  const selectedModel = modelRegistry.find(UNDEFINEDS_PROVIDER_ID, defaultModelId)
222
289
  ?? modelRegistry.getAvailable().find((candidate) => candidate.provider === UNDEFINEDS_PROVIDER_ID);
223
290
  if (!selectedModel) {
224
- throw new Error('Failed to resolve undefineds model from the LinX Pi runtime adapter');
291
+ throw new Error('Failed to resolve undefineds model from the LinX runtime adapter');
225
292
  }
226
293
  const created = await createAgentSessionFromServices({
227
294
  services,
228
295
  sessionManager: context.sessionManager,
229
296
  sessionStartEvent: context.sessionStartEvent,
230
297
  model: selectedModel,
231
- tools: [
232
- 'read',
233
- 'bash',
234
- 'edit',
235
- 'write',
236
- 'web_fetch',
237
- 'web_search',
238
- 'pod_read',
239
- 'pod_write',
240
- ],
241
- customTools: [
242
- createLinxPiBashToolDefinition(context.cwd),
243
- webFetchTool,
244
- webSearchTool,
245
- podReadTool,
246
- podWriteTool,
247
- ],
248
298
  });
249
299
  const session = created.session;
250
300
  enableLinxXhighThinking(session);
251
- restoreLinxThinkingDefault(session, settingsManager);
252
- installLinxPiRemoteApproval({
253
- session,
254
- cwd: context.cwd,
255
- });
256
301
  if (session.model?.provider !== selectedModel.provider || session.model.id !== selectedModel.id) {
257
302
  await session.setModel(selectedModel);
258
303
  }
@@ -267,14 +312,29 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
267
312
  sessionManager: context.sessionManager,
268
313
  sessionStartEvent: context.sessionStartEvent,
269
314
  });
315
+ runtime.backend = 'linx';
316
+ runtime.runtimeBackend = workerBackend;
317
+ runtime.autoEnabled = options.autoEnabled === true;
318
+ runtime.symphonyEnabled = options.symphonyEnabled === true;
270
319
  runtime.linxAuthBridge = {
271
320
  description: UNDEFINEDS_AUTH_BRIDGE_ID,
272
- authMode,
273
321
  providerId: UNDEFINEDS_PROVIDER_ID,
274
322
  providerLabel: UNDEFINEDS_PROVIDER_LABEL,
275
323
  runtimeUrl: baseUrl,
276
324
  shouldPromptLoginOnStart,
277
325
  };
326
+ if (backendCommandRouter) {
327
+ ;
328
+ runtime.backendCommandRouter = backendCommandRouter;
329
+ }
330
+ if (proxy) {
331
+ ;
332
+ runtime.backendSessionRef = proxy.record;
333
+ }
334
+ if (options.getPodDataSession) {
335
+ ;
336
+ runtime.getPodDataSession = options.getPodDataSession;
337
+ }
278
338
  return runtime;
279
339
  },
280
340
  async start() {
@@ -284,17 +344,17 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
284
344
  await proxy?.close();
285
345
  },
286
346
  };
287
- async function syncProviderModels(apiKey, options = {}) {
288
- if (!apiKey || !dependencies.listRemoteModels) {
347
+ async function syncProviderModels(authSession, options = {}) {
348
+ if (!dependencies.listRemoteModels) {
289
349
  return;
290
350
  }
291
- const remoteModels = await listRemoteModelsWithAuthRecovery(apiKey, options);
351
+ const remoteModels = await listRemoteModelsWithAuthRecovery(authSession.runtimeFetch, options);
292
352
  if (remoteModels.length === 0) {
293
353
  return;
294
354
  }
295
355
  const mergedModels = mergeLinxProviderModels(remoteModels.map((entry) => ({
296
356
  id: entry.id,
297
- contextWindow: entry.contextWindow ?? 1_000_000,
357
+ contextWindow: entry.contextWindow,
298
358
  })), activeModelId);
299
359
  const nextModels = mergedModels.map((entry) => buildProviderModel(entry));
300
360
  providerModels.splice(0, providerModels.length, ...nextModels);
@@ -302,29 +362,24 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
302
362
  activeModelId = resolvePreferredLinxCloudModelId(nextModels, activeModelId);
303
363
  }
304
364
  }
305
- async function listRemoteModelsWithAuthRecovery(apiKey, recoveryOptions) {
365
+ async function listRemoteModelsWithAuthRecovery(authFetch, recoveryOptions) {
306
366
  try {
307
- return await dependencies.listRemoteModels(null, baseUrl, apiKey);
367
+ return await dependencies.listRemoteModels(authFetch, baseUrl, { fallback: false, timeoutMs: 5000 });
308
368
  }
309
369
  catch (error) {
310
370
  if (!isAuthExpiredError(error)) {
311
371
  return [];
312
372
  }
313
373
  if (recoveryOptions.refreshOnAuthExpired) {
314
- const refreshed = await resolveLinxPiCloudOAuthCredential(options.providerConfig?.issuerUrl).catch((refreshError) => {
315
- if (isOidcLoginExpiredError(refreshError)) {
316
- return null;
374
+ try {
375
+ const refreshedAuthFetch = await resolveRefreshedLinxPiCloudAuthFetch();
376
+ if (refreshedAuthFetch) {
377
+ return await dependencies.listRemoteModels(refreshedAuthFetch, baseUrl, { fallback: false, timeoutMs: 5000 });
317
378
  }
318
- throw refreshError;
319
- });
320
- if (refreshed?.access) {
321
- try {
322
- return await dependencies.listRemoteModels(null, baseUrl, refreshed.access);
323
- }
324
- catch (retryError) {
325
- if (!isAuthExpiredError(retryError)) {
326
- return [];
327
- }
379
+ }
380
+ catch (retryError) {
381
+ if (!isAuthExpiredError(retryError)) {
382
+ return [];
328
383
  }
329
384
  }
330
385
  }
@@ -335,39 +390,48 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
335
390
  return [];
336
391
  }
337
392
  }
338
- async function completeWithAuthRecovery(apiKey, request) {
393
+ async function completeWithAuthRecovery(authFetch, request) {
339
394
  try {
340
395
  return await dependencies.createRemoteCompletion({
341
396
  ...request,
342
- apiKey,
397
+ authFetch,
343
398
  });
344
399
  }
345
400
  catch (error) {
346
401
  if (!isAuthExpiredError(error)) {
347
402
  throw error;
348
403
  }
349
- const refreshed = await resolveLinxPiCloudOAuthCredential(options.providerConfig?.issuerUrl).catch((refreshError) => {
350
- if (isOidcLoginExpiredError(refreshError)) {
351
- return null;
352
- }
353
- throw refreshError;
354
- });
355
- if (!refreshed?.access) {
404
+ const refreshedAuthFetch = await resolveRefreshedLinxPiCloudAuthFetch();
405
+ if (!refreshedAuthFetch) {
356
406
  throw error;
357
407
  }
358
408
  return dependencies.createRemoteCompletion({
359
409
  ...request,
360
- apiKey: refreshed.access,
410
+ authFetch: refreshedAuthFetch,
361
411
  });
362
412
  }
363
413
  }
414
+ async function resolveRefreshedLinxPiCloudAuthFetch() {
415
+ clearDefaultPodDataSession();
416
+ const storedCredentials = loadCredentials();
417
+ if (storedCredentials || options.getPodDataSession) {
418
+ return resolveLinxPiCloudAuthFetch({
419
+ issuerUrl: options.providerConfig?.issuerUrl,
420
+ getPodDataSession: options.getPodDataSession,
421
+ });
422
+ }
423
+ return null;
424
+ }
364
425
  }
426
+ /** @deprecated Use createLinxRuntimeAdapter. */
427
+ export const createPiRuntimeAdapter = createLinxRuntimeAdapter;
365
428
  export function resolveBundledLinxSkillsDir(importMetaUrl = import.meta.url) {
366
429
  const moduleDir = dirname(fileURLToPath(importMetaUrl));
367
430
  const candidates = [
368
- // Published package: dist/lib/pi-adapter/runtime.js -> dist/skills
431
+ // Published package: dist/lib/pi-adapter/runtime.js -> dist/skills.
432
+ // This directory is a curated product-skill bundle, not the repo skill root.
369
433
  join(moduleDir, '..', '..', 'skills'),
370
- // Test/dev bundle: <tmp>/dist/lib/pi-adapter/runtime.js -> repo skills
434
+ // Test/dev bundle: <tmp>/dist/lib/pi-adapter/runtime.js -> curated product skills.
371
435
  resolve(moduleDir, '..', '..', '..', '..', 'skills'),
372
436
  // Source-tree fallback when running through a TS loader.
373
437
  resolve(moduleDir, '..', '..', '..', '..', '..', 'skills'),
@@ -379,34 +443,156 @@ export function resolveBundledLinxSkillsDir(importMetaUrl = import.meta.url) {
379
443
  }
380
444
  return null;
381
445
  }
382
- function withBundledLinxSkillSourceInfo(base, bundledSkillsDir) {
446
+ function ensurePiWebAccessConfig() {
447
+ const config = JSON.stringify({ workflow: 'none' }, null, 2) + '\n';
448
+ const linxPath = getSolidLinxPiWebAccessConfigPath();
449
+ const linxDir = getSolidLinxAppDir();
450
+ if (!existsSync(linxDir)) {
451
+ mkdirSync(linxDir, { recursive: true });
452
+ }
453
+ if (!existsSync(linxPath))
454
+ writeFileSync(linxPath, config);
455
+ }
456
+ export function resolveBundledPiPackageRoot(packageName, importMetaUrl = import.meta.url) {
457
+ const moduleDir = dirname(fileURLToPath(importMetaUrl));
458
+ const vendoredCandidates = [
459
+ // Published/built package: dist/lib/pi-adapter/runtime.js -> vendor/<package>.
460
+ resolve(moduleDir, '..', '..', '..', 'vendor', packageName),
461
+ // Defensive fallback for layouts that place vendor under dist.
462
+ resolve(moduleDir, '..', '..', 'vendor', packageName),
463
+ ];
464
+ for (const candidate of vendoredCandidates) {
465
+ if (existsSync(join(candidate, 'package.json'))) {
466
+ return candidate;
467
+ }
468
+ }
469
+ try {
470
+ const requireFromRuntime = createRequire(importMetaUrl);
471
+ return dirname(requireFromRuntime.resolve(`${packageName}/package.json`));
472
+ }
473
+ catch {
474
+ return null;
475
+ }
476
+ }
477
+ function withLinxSkillSourceInfo(base, options) {
478
+ const { bundledSkillsDir, marketSkillDirs } = options;
479
+ const filteredSkills = base.skills.filter((skill) => (!bundledSkillsDir
480
+ || !skill.filePath.startsWith(bundledSkillsDir)
481
+ || LINX_PRODUCT_SKILL_NAMES.has(skill.name)));
383
482
  return {
384
483
  ...base,
385
- skills: base.skills.map((skill) => (skill.filePath.startsWith(bundledSkillsDir)
386
- ? {
387
- ...skill,
388
- sourceInfo: {
389
- path: skill.filePath,
390
- source: LINX_PACKAGE_SOURCE,
391
- scope: 'temporary',
392
- origin: 'package',
393
- baseDir: bundledSkillsDir,
394
- },
484
+ skills: filteredSkills.map((skill) => {
485
+ if (bundledSkillsDir && skill.filePath.startsWith(bundledSkillsDir)) {
486
+ return {
487
+ ...skill,
488
+ sourceInfo: {
489
+ path: skill.filePath,
490
+ source: LINX_PACKAGE_SOURCE,
491
+ scope: 'temporary',
492
+ origin: 'package',
493
+ baseDir: bundledSkillsDir,
494
+ },
495
+ };
496
+ }
497
+ const marketSkillDir = marketSkillDirs.find((dir) => skill.filePath.startsWith(dir));
498
+ if (marketSkillDir) {
499
+ return {
500
+ ...skill,
501
+ sourceInfo: {
502
+ path: skill.filePath,
503
+ source: MARKET_XPOD_CLI_SKILL_SOURCE,
504
+ scope: 'temporary',
505
+ origin: 'marketplace',
506
+ version: resolveMarketSkillVersion(marketSkillDir),
507
+ baseDir: marketSkillDir,
508
+ },
509
+ };
395
510
  }
396
- : skill)),
511
+ return skill;
512
+ }),
397
513
  };
398
514
  }
515
+ export function resolveInstalledMarketSkillDirs() {
516
+ return [resolveInstalledXpodCliMarketSkillDir()].filter((path) => Boolean(path));
517
+ }
518
+ function resolveInstalledXpodCliMarketSkillDir() {
519
+ const codexHome = process.env.CODEX_HOME?.trim() || join(homedir(), '.codex');
520
+ const versionsRoot = join(codexHome, 'plugins', 'cache', 'undefineds', 'xpod-cli');
521
+ if (!existsSync(versionsRoot)) {
522
+ return null;
523
+ }
524
+ const candidates = [];
525
+ for (const entry of safeReadDir(versionsRoot)) {
526
+ const versionDir = join(versionsRoot, entry);
527
+ if (!safeIsDirectory(versionDir)) {
528
+ continue;
529
+ }
530
+ const skillDir = join(versionDir, 'skills', 'xpod-cli');
531
+ if (existsSync(join(skillDir, 'SKILL.md'))) {
532
+ candidates.push({ version: entry, dir: skillDir });
533
+ }
534
+ }
535
+ candidates.sort((a, b) => compareVersionLike(b.version, a.version));
536
+ return candidates[0]?.dir ?? null;
537
+ }
538
+ function resolveMarketSkillVersion(skillDir) {
539
+ const version = basename(dirname(dirname(skillDir)));
540
+ return version && version !== 'skills' ? version : undefined;
541
+ }
542
+ function safeReadDir(dir) {
543
+ try {
544
+ return readdirSync(dir);
545
+ }
546
+ catch {
547
+ return [];
548
+ }
549
+ }
550
+ function safeIsDirectory(path) {
551
+ try {
552
+ return statSync(path).isDirectory();
553
+ }
554
+ catch {
555
+ return false;
556
+ }
557
+ }
558
+ function compareVersionLike(a, b) {
559
+ const left = a.split(/[.-]/u).map((part) => Number(part));
560
+ const right = b.split(/[.-]/u).map((part) => Number(part));
561
+ const length = Math.max(left.length, right.length);
562
+ for (let i = 0; i < length; i += 1) {
563
+ const l = Number.isFinite(left[i]) ? left[i] : 0;
564
+ const r = Number.isFinite(right[i]) ? right[i] : 0;
565
+ if (l !== r) {
566
+ return l - r;
567
+ }
568
+ }
569
+ return a.localeCompare(b);
570
+ }
571
+ function enableLinxXhighThinking(session) {
572
+ const originalSupportsXhighThinking = session.supportsXhighThinking?.bind(session);
573
+ const originalGetAvailableThinkingLevels = session.getAvailableThinkingLevels?.bind(session);
574
+ session.supportsXhighThinking = () => (session.model?.provider === UNDEFINEDS_PROVIDER_ID && session.model.reasoning
575
+ ? (session.getAvailableThinkingLevels?.().includes('xhigh') ?? true)
576
+ : (originalSupportsXhighThinking?.() ?? false));
577
+ if (originalGetAvailableThinkingLevels) {
578
+ session.getAvailableThinkingLevels = () => {
579
+ const levels = originalGetAvailableThinkingLevels();
580
+ if (session.model?.provider === UNDEFINEDS_PROVIDER_ID && session.model.reasoning && !levels.includes('xhigh')) {
581
+ return [...levels, 'xhigh'];
582
+ }
583
+ return levels;
584
+ };
585
+ }
586
+ }
399
587
  export function createLinxPiCodingTools(cwd, options = {}) {
400
588
  const localBashOperations = options.bashOperations ?? createLocalBashOperations();
401
589
  const bashTimeoutSeconds = options.bashTimeoutSeconds ?? DEFAULT_LINX_PI_BASH_TIMEOUT_SECONDS;
402
590
  return createCodingTools(cwd, {
403
591
  bash: {
404
- commandPrefix: buildLinxToolCommandPrefix(),
405
592
  operations: {
406
593
  exec(command, workingDirectory, options) {
407
- return localBashOperations.exec(command, workingDirectory ?? process.cwd(), {
594
+ return localBashOperations.exec(command, workingDirectory ?? cwd, {
408
595
  ...options,
409
- env: withLinxToolPath(options.env),
410
596
  timeout: typeof options.timeout === 'number'
411
597
  ? options.timeout
412
598
  : bashTimeoutSeconds,
@@ -416,104 +602,147 @@ export function createLinxPiCodingTools(cwd, options = {}) {
416
602
  },
417
603
  });
418
604
  }
419
- export function createLinxPiBashToolDefinition(cwd, options = {}) {
420
- const localBashOperations = options.bashOperations ?? createLocalBashOperations();
421
- const bashTimeoutSeconds = options.bashTimeoutSeconds ?? DEFAULT_LINX_PI_BASH_TIMEOUT_SECONDS;
422
- return createBashToolDefinition(cwd, {
423
- commandPrefix: buildLinxToolCommandPrefix(),
424
- operations: {
425
- exec(command, workingDirectory, options) {
426
- return localBashOperations.exec(command, workingDirectory ?? process.cwd(), {
427
- ...options,
428
- env: withLinxToolPath(options.env),
429
- timeout: typeof options.timeout === 'number'
430
- ? options.timeout
431
- : bashTimeoutSeconds,
432
- });
433
- },
434
- },
435
- });
605
+ function isAuthExpiredError(error) {
606
+ return isRemoteAuthExpiredError(error);
607
+ }
608
+ function createBearerAuthFetch(apiKey) {
609
+ return async (url, init) => {
610
+ const headers = new Headers(init?.headers);
611
+ headers.set('Authorization', `Bearer ${apiKey}`);
612
+ return fetch(url, { ...init, headers });
613
+ };
436
614
  }
437
- function buildLinxToolCommandPrefix() {
438
- const udfsBin = resolveUdfsToolBinFile();
439
- if (!udfsBin) {
440
- return undefined;
615
+ function resolveRuntimeAuthFetchFromApiKey(apiKey) {
616
+ const trimmed = apiKey?.trim();
617
+ if (!trimmed || trimmed === LINX_RUNTIME_MANAGED_AUTH_KEY) {
618
+ return null;
441
619
  }
442
- return `udfs() { node ${shellQuote(udfsBin)} "$@"; }`;
620
+ return createBearerAuthFetch(trimmed);
443
621
  }
444
- function withLinxToolPath(env) {
445
- const nextEnv = { ...(env ?? process.env) };
446
- const udfsBinDir = resolveUdfsToolBinDir();
447
- if (!udfsBinDir) {
448
- return nextEnv;
622
+ async function resolveLinxPiCloudAuthFetch(options) {
623
+ if (options.getPodDataSession) {
624
+ return createPodDataSessionAuthFetch(options.getPodDataSession);
449
625
  }
450
- const pathKey = Object.keys(nextEnv).find((key) => key.toLowerCase() === 'path') ?? 'PATH';
451
- const currentPath = nextEnv[pathKey] ?? '';
452
- const entries = currentPath.split(delimiter).filter(Boolean);
453
- if (!entries.includes(udfsBinDir)) {
454
- nextEnv[pathKey] = [udfsBinDir, currentPath].filter(Boolean).join(delimiter);
626
+ const session = await getDefaultPodDataSession();
627
+ if (session) {
628
+ return withCloudCompletionTimeout(session.runtimeFetch);
455
629
  }
456
- return nextEnv;
630
+ throw new Error('No LinX cloud login found. Interactive TUI supports /login in-app. For non-interactive --print mode, run `linx login` first.');
457
631
  }
458
- function resolveUdfsToolBinFile(importMetaUrl = import.meta.url) {
459
- const udfsBinDir = resolveUdfsToolBinDir(importMetaUrl);
460
- if (!udfsBinDir) {
461
- return null;
632
+ function createPodDataSessionAuthFetch(getPodDataSession) {
633
+ if (getPodDataSession !== getDefaultPodDataSession) {
634
+ return withCloudCompletionTimeout(async (url, init) => {
635
+ const session = await getPodDataSession();
636
+ if (session) {
637
+ try {
638
+ return await session.runtimeFetch(url, init);
639
+ }
640
+ finally {
641
+ await session.close().catch(() => undefined);
642
+ }
643
+ }
644
+ throw new Error('No LinX cloud login found. Interactive TUI supports /login in-app. For non-interactive --print mode, run `linx login` first.');
645
+ });
462
646
  }
463
- return join(udfsBinDir, 'udfs.js');
647
+ let cachedSession = null;
648
+ let cachedSessionPromise = null;
649
+ const getCachedSession = async () => {
650
+ if (cachedSession) {
651
+ return cachedSession;
652
+ }
653
+ if (!cachedSessionPromise) {
654
+ cachedSessionPromise = getPodDataSession().then((session) => {
655
+ cachedSession = session;
656
+ return session;
657
+ }).finally(() => {
658
+ cachedSessionPromise = null;
659
+ });
660
+ }
661
+ return cachedSessionPromise;
662
+ };
663
+ return withCloudCompletionTimeout(async (url, init) => {
664
+ const session = await getCachedSession();
665
+ if (session) {
666
+ return await session.runtimeFetch(url, init);
667
+ }
668
+ throw new Error('No LinX cloud login found. Interactive TUI supports /login in-app. For non-interactive --print mode, run `linx login` first.');
669
+ });
464
670
  }
465
- function resolveUdfsToolBinDir(importMetaUrl = import.meta.url) {
466
- const moduleDir = dirname(fileURLToPath(importMetaUrl));
467
- const candidates = [
468
- // Published package: @undefineds.co/models dependency next to @undefineds.co/linx.
469
- resolve(moduleDir, '..', '..', '..', '..', '@undefineds.co', 'models', 'dist', 'bin'),
470
- // Workspace/dev tree: apps/cli/dist/lib/pi-adapter/runtime.js -> packages/models/dist/bin
471
- resolve(moduleDir, '..', '..', '..', '..', '..', 'packages', 'models', 'dist', 'bin'),
472
- // Source-tree fallback when running through a TS loader.
473
- resolve(moduleDir, '..', '..', '..', '..', '..', '..', 'packages', 'models', 'dist', 'bin'),
474
- ];
475
- return candidates.find((candidate) => existsSync(join(candidate, 'udfs.js'))) ?? null;
671
+ function withCloudCompletionTimeout(fetcher) {
672
+ return async (url, init) => {
673
+ if (!isChatCompletionRuntimeUrl(String(url))) {
674
+ return fetcher(url, init);
675
+ }
676
+ const timeoutMs = resolveLinxCloudCompletionTimeoutMs();
677
+ const controller = new AbortController();
678
+ const signal = init?.signal
679
+ ? combineAbortSignals(init.signal, controller.signal)
680
+ : controller.signal;
681
+ let timedOut = false;
682
+ const timer = setTimeout(() => {
683
+ timedOut = true;
684
+ controller.abort();
685
+ }, timeoutMs);
686
+ try {
687
+ return await Promise.race([
688
+ fetcher(url, { ...init, signal }),
689
+ new Promise((_resolve, reject) => {
690
+ controller.signal.addEventListener('abort', () => {
691
+ if (timedOut) {
692
+ reject(new RemoteChatRequestError(formatLinxCloudTransientMessage(`Request exceeded ${formatTimeoutSeconds(timeoutMs)}s.`), 0, `Timed out waiting for POST ${url}`));
693
+ }
694
+ }, { once: true });
695
+ }),
696
+ ]);
697
+ }
698
+ catch (error) {
699
+ if (timedOut) {
700
+ throw new RemoteChatRequestError(formatLinxCloudTransientMessage(`Request exceeded ${formatTimeoutSeconds(timeoutMs)}s.`), 0, error instanceof Error ? error.message : String(error));
701
+ }
702
+ throw error;
703
+ }
704
+ finally {
705
+ clearTimeout(timer);
706
+ }
707
+ };
476
708
  }
477
- function shellQuote(value) {
478
- return `'${value.replace(/'/g, `'\\''`)}'`;
709
+ function resolveLinxCloudCompletionTimeoutMs() {
710
+ const raw = process.env.LINX_CHAT_TIMEOUT_MS;
711
+ if (!raw) {
712
+ return DEFAULT_LINX_CLOUD_COMPLETION_TIMEOUT_MS;
713
+ }
714
+ const parsed = Number(raw);
715
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_LINX_CLOUD_COMPLETION_TIMEOUT_MS;
479
716
  }
480
- function enableLinxXhighThinking(session) {
481
- const originalSupportsXhighThinking = session.supportsXhighThinking?.bind(session);
482
- const originalSupportsThinking = session.supportsThinking?.bind(session);
483
- const originalGetAvailableThinkingLevels = session.getAvailableThinkingLevels?.bind(session);
484
- const supportsLinxXhigh = () => (session.model?.provider === UNDEFINEDS_PROVIDER_ID && session.model.reasoning
485
- ? true
486
- : originalSupportsXhighThinking?.() ?? false);
487
- session.supportsXhighThinking = supportsLinxXhigh;
488
- if (originalSupportsThinking) {
489
- session.supportsThinking = () => (session.model?.provider === UNDEFINEDS_PROVIDER_ID && session.model.reasoning
490
- ? true
491
- : originalSupportsThinking());
717
+ function formatTimeoutSeconds(timeoutMs) {
718
+ return Math.max(1, Math.round(timeoutMs / 1000));
719
+ }
720
+ function isChatCompletionRuntimeUrl(value) {
721
+ try {
722
+ const target = new URL(value);
723
+ const segments = target.pathname.split('/').filter(Boolean);
724
+ return segments.length >= 3
725
+ && /^v\d+$/.test(segments.at(-3) ?? '')
726
+ && segments.at(-2) === 'chat'
727
+ && segments.at(-1) === 'completions';
492
728
  }
493
- if (originalGetAvailableThinkingLevels) {
494
- session.getAvailableThinkingLevels = () => {
495
- const levels = originalGetAvailableThinkingLevels();
496
- if (supportsLinxXhigh() && !levels.includes('xhigh')) {
497
- return [...levels, 'xhigh'];
498
- }
499
- return levels;
500
- };
729
+ catch {
730
+ return false;
501
731
  }
502
732
  }
503
- function restoreLinxThinkingDefault(session, settingsManager) {
504
- if (session.model?.provider !== UNDEFINEDS_PROVIDER_ID || !session.model.reasoning) {
505
- return;
733
+ function combineAbortSignals(left, right) {
734
+ if (typeof AbortSignal.any === 'function') {
735
+ return AbortSignal.any([left, right]);
506
736
  }
507
- const defaultThinkingLevel = settingsManager.getDefaultThinkingLevel();
508
- if (!defaultThinkingLevel || defaultThinkingLevel === 'off') {
509
- return;
737
+ const controller = new AbortController();
738
+ const abort = () => controller.abort();
739
+ if (left.aborted || right.aborted) {
740
+ abort();
741
+ return controller.signal;
510
742
  }
511
- if (session.getAvailableThinkingLevels?.().includes(defaultThinkingLevel)) {
512
- session.setThinkingLevel?.(defaultThinkingLevel);
513
- }
514
- }
515
- function isAuthExpiredError(error) {
516
- return isRemoteAuthExpiredError(error);
743
+ left.addEventListener('abort', abort, { once: true });
744
+ right.addEventListener('abort', abort, { once: true });
745
+ return controller.signal;
517
746
  }
518
747
  function sanitizeLinxCloudDefaults(settingsManager, requestedModel, providerModels) {
519
748
  const availableModelIds = new Set(providerModels.map((model) => model.id));
@@ -532,8 +761,11 @@ function buildProviderModel(input) {
532
761
  return {
533
762
  id: input.id,
534
763
  name: input.id,
535
- api: 'openai-completions',
764
+ api: UNDEFINEDS_PROVIDER_API,
536
765
  reasoning: true,
766
+ thinkingLevelMap: {
767
+ xhigh: 'xhigh',
768
+ },
537
769
  input: ['text'],
538
770
  cost: {
539
771
  input: 0,
@@ -561,7 +793,7 @@ function mergeLinxProviderModels(models, activeModelId) {
561
793
  ]) {
562
794
  byId.set(id, {
563
795
  id,
564
- contextWindow: 1_000_000,
796
+ contextWindow: normalizeLinxCloudContextWindow(undefined),
565
797
  });
566
798
  }
567
799
  for (const model of models) {
@@ -571,20 +803,15 @@ function mergeLinxProviderModels(models, activeModelId) {
571
803
  }
572
804
  byId.set(id, {
573
805
  id,
574
- contextWindow: model.contextWindow,
806
+ contextWindow: normalizeLinxCloudContextWindow(model.contextWindow),
575
807
  });
576
808
  }
577
809
  return [...byId.values()];
578
810
  }
579
- async function resolveLinxPiCloudApiKey(options) {
580
- if (options.explicitApiKey) {
581
- return options.explicitApiKey;
582
- }
583
- const credential = await resolveLinxPiCloudOAuthCredential(options.issuerUrl);
584
- if (credential?.access) {
585
- return credential.access;
586
- }
587
- throw new Error('No LinX cloud login found. Interactive TUI supports /login in-app. For non-interactive --print mode, run `linx login` first.');
811
+ function normalizeLinxCloudContextWindow(contextWindow) {
812
+ return typeof contextWindow === 'number' && Number.isFinite(contextWindow) && contextWindow > 0
813
+ ? contextWindow
814
+ : DEFAULT_LINX_CLOUD_CONTEXT_WINDOW;
588
815
  }
589
816
  function withSystemPrompt(systemPrompt, messages) {
590
817
  const prompt = systemPrompt?.trim();
@@ -603,6 +830,9 @@ function overrideLinxSystemPrompt(base) {
603
830
  'When replying in Chinese, describe yourself as "AI主理人".',
604
831
  'Use a friendly, direct style like: "你好!我是 LinX,一个 AI 主理人,很高兴为你服务!"',
605
832
  'Keep Pi-compatible coding agent behavior: read files, run commands, edit code, use tools, and follow project instructions.',
833
+ 'When introducing capabilities, describe only user-facing LinX product abilities and the currently available runtime actions.',
834
+ 'Do not advertise repository-local agent instructions, internal command names, bundled plugin skill names, package names, or developer-only workflows as features the user can call.',
835
+ 'If a capability depends on the current workspace, installed tools, login state, backend, or Symphony mode, state that dependency instead of implying it is always available.',
606
836
  ].join('\n');
607
837
  if (!original) {
608
838
  return identity;