@jini-ai/agent-runtime 0.4.4 → 0.4.6

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 (72) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/agent-protocol/acp/models.d.ts +11 -0
  3. package/dist/agent-protocol/acp/models.js +12 -3
  4. package/dist/capabilities.d.ts +1 -8
  5. package/dist/capabilities.js +8 -1
  6. package/dist/claude-code-models.d.ts +13 -1
  7. package/dist/claude-code-models.js +15 -17
  8. package/dist/claude-segment-usage.d.ts +9 -2
  9. package/dist/claude-segment-usage.js +20 -4
  10. package/dist/claude-stream.d.ts +4 -0
  11. package/dist/claude-stream.js +5 -1
  12. package/dist/defs/aider.js +1 -1
  13. package/dist/defs/amp.js +1 -1
  14. package/dist/defs/amr.js +1 -1
  15. package/dist/defs/antigravity.js +1 -1
  16. package/dist/defs/auggie.js +1 -1
  17. package/dist/defs/claude.d.ts +2 -0
  18. package/dist/defs/claude.js +11 -3
  19. package/dist/defs/cline.js +1 -1
  20. package/dist/defs/codebuddy.js +2 -2
  21. package/dist/defs/codex.js +1 -1
  22. package/dist/defs/copilot.js +1 -1
  23. package/dist/defs/crush.js +1 -1
  24. package/dist/defs/cursor-agent.js +2 -2
  25. package/dist/defs/deepseek.js +1 -1
  26. package/dist/defs/devin.js +1 -1
  27. package/dist/defs/droid.js +1 -1
  28. package/dist/defs/goose.js +1 -1
  29. package/dist/defs/grok-build.js +1 -1
  30. package/dist/defs/hermes.js +1 -1
  31. package/dist/defs/kilo.js +1 -1
  32. package/dist/defs/kimi.js +1 -1
  33. package/dist/defs/kiro.js +1 -1
  34. package/dist/defs/mimo.js +1 -1
  35. package/dist/defs/opencode.js +2 -2
  36. package/dist/defs/pi.js +1 -1
  37. package/dist/defs/qoder.js +1 -1
  38. package/dist/defs/qwen.js +1 -1
  39. package/dist/defs/reasonix.js +1 -1
  40. package/dist/defs/trae-cli.js +1 -1
  41. package/dist/defs/vibe.js +1 -1
  42. package/dist/detection.d.ts +28 -4
  43. package/dist/detection.js +82 -43
  44. package/dist/diagnostics.d.ts +6 -1
  45. package/dist/diagnostics.js +2 -2
  46. package/dist/env.js +1 -1
  47. package/dist/executables.d.ts +15 -9
  48. package/dist/executables.js +73 -65
  49. package/dist/index.d.ts +3 -2
  50. package/dist/index.js +3 -2
  51. package/dist/invocation.d.ts +24 -2
  52. package/dist/invocation.js +19 -7
  53. package/dist/launch.d.ts +2 -1
  54. package/dist/launch.js +2 -2
  55. package/dist/model-discovery-adapters.d.ts +6 -2
  56. package/dist/model-discovery-adapters.js +64 -51
  57. package/dist/model-discovery-native.d.ts +14 -0
  58. package/dist/model-discovery-native.js +22 -0
  59. package/dist/model-discovery-sdk.js +1 -1
  60. package/dist/model-discovery-shared.d.ts +14 -0
  61. package/dist/model-discovery-shared.js +53 -0
  62. package/dist/model-discovery-types.d.ts +26 -0
  63. package/dist/model-discovery.d.ts +17 -22
  64. package/dist/model-discovery.js +18 -69
  65. package/dist/models.d.ts +9 -4
  66. package/dist/models.js +11 -11
  67. package/dist/probe-scheduler.d.ts +19 -0
  68. package/dist/probe-scheduler.js +30 -0
  69. package/dist/runtime-state.d.ts +41 -0
  70. package/dist/runtime-state.js +31 -0
  71. package/dist/types.d.ts +4 -0
  72. package/package.json +36 -9
@@ -1,6 +1,7 @@
1
+ import { type AgentRuntimeState } from './runtime-state.js';
1
2
  import type { ModelDiscoveryContext, ModelDiscoveryDeps, ModelCatalogSnapshot, DefaultModelResolution } from './model-discovery-types.js';
2
3
  import { type AmrProfileResolver } from './amr-profile-resolver.js';
3
- import type { AgentDiagnostic, DetectedAgent, RuntimeAgentDef, RuntimeModelSource, RuntimeModelOption } from './types.js';
4
+ import type { AgentDiagnostic, DetectedAgent, RuntimeAgentDef, RuntimeCapabilityMap, RuntimeModelSource, RuntimeModelOption } from './types.js';
4
5
  type FetchedRuntimeModels = {
5
6
  models: RuntimeModelOption[];
6
7
  source: RuntimeModelSource;
@@ -30,6 +31,14 @@ export declare function ensureAgentCapabilities({ def, launchPath, env }: {
30
31
  def: RuntimeAgentDef;
31
32
  launchPath: string;
32
33
  env: NodeJS.ProcessEnv;
34
+ }, { state, probe }?: {
35
+ state?: AgentRuntimeState;
36
+ probe?: (required: {
37
+ helpArgs: readonly string[];
38
+ capabilityFlags: Readonly<Record<string, string>>;
39
+ launchPath: string;
40
+ env: NodeJS.ProcessEnv;
41
+ }, optional: Record<string, never>) => Promise<RuntimeCapabilityMap>;
33
42
  }): Promise<void>;
34
43
  /** Result of {@link probeAgentModels}: the models to surface and where they came from. */
35
44
  export type ProbedAgentModels = FetchedRuntimeModels;
@@ -47,7 +56,9 @@ export type ProbedAgentModels = FetchedRuntimeModels;
47
56
  */
48
57
  export declare function probeAgentModels({ def }: {
49
58
  def: RuntimeAgentDef;
50
- }, { configuredEnv, amrProfileResolver, context, deps, force }?: {
59
+ }, { configuredEnv, amrProfileResolver, context, deps, force, state, env }?: {
60
+ state?: AgentRuntimeState;
61
+ env?: NodeJS.ProcessEnv;
51
62
  configuredEnv?: Record<string, string>;
52
63
  amrProfileResolver?: AmrProfileResolver;
53
64
  context?: ModelDiscoveryContext;
@@ -58,13 +69,23 @@ export interface AgentDetectionModelOptions {
58
69
  context?: ModelDiscoveryContext;
59
70
  deps?: ModelDiscoveryDeps;
60
71
  force?: boolean;
72
+ state?: AgentRuntimeState;
73
+ env?: NodeJS.ProcessEnv;
74
+ probeAgent?: (input: {
75
+ def: RuntimeAgentDef;
76
+ configuredEnv: Record<string, string>;
77
+ amrProfileResolver: AmrProfileResolver;
78
+ }, options: AgentDetectionModelOptions) => Promise<DetectedAgent>;
61
79
  }
62
- export declare function detectAgents({}: {}, { configuredEnvByAgent, amrProfileResolver, modelContextByAgent, modelDiscoveryDeps, force }?: {
80
+ export declare function detectAgents({}: {}, { configuredEnvByAgent, amrProfileResolver, modelContextByAgent, modelDiscoveryDeps, force, state, env, probeAgent }?: {
81
+ state?: AgentRuntimeState;
82
+ env?: NodeJS.ProcessEnv;
63
83
  configuredEnvByAgent?: Record<string, Record<string, string>>;
64
84
  amrProfileResolver?: AmrProfileResolver;
65
85
  modelContextByAgent?: Record<string, ModelDiscoveryContext>;
66
86
  modelDiscoveryDeps?: ModelDiscoveryDeps;
67
87
  force?: boolean;
88
+ probeAgent?: AgentDetectionModelOptions['probeAgent'];
68
89
  }): Promise<(Omit<RuntimeAgentDef, "discoverModels" | "resolveDefaultModel" | "buildArgs" | "listModels" | "fetchModels" | "deriveReasoningOptions" | "fallbackModels" | "fallbackModelsAssertedAt" | "helpArgs" | "capabilityFlags" | "fallbackBins" | "versionProbeTimeoutMs" | "maxPromptArgBytes" | "env" | "inactivityTimeoutMs" | "authProbe" | "needsAgentLogFile" | "stdoutPolicy" | "runtimeLock"> & {
69
90
  models: RuntimeModelOption[];
70
91
  modelsSource: RuntimeModelSource;
@@ -77,12 +98,15 @@ export declare function detectAgents({}: {}, { configuredEnvByAgent, amrProfileR
77
98
  version?: string | null;
78
99
  diagnostics?: AgentDiagnostic[];
79
100
  })[]>;
80
- export declare function detectAgentsStream({}: {}, { configuredEnvByAgent, amrProfileResolver, modelContextByAgent, modelDiscoveryDeps, force }?: {
101
+ export declare function detectAgentsStream({}: {}, { configuredEnvByAgent, amrProfileResolver, modelContextByAgent, modelDiscoveryDeps, force, state, env, probeAgent }?: {
102
+ state?: AgentRuntimeState;
103
+ env?: NodeJS.ProcessEnv;
81
104
  configuredEnvByAgent?: Record<string, Record<string, string>>;
82
105
  amrProfileResolver?: AmrProfileResolver;
83
106
  modelContextByAgent?: Record<string, ModelDiscoveryContext>;
84
107
  modelDiscoveryDeps?: ModelDiscoveryDeps;
85
108
  force?: boolean;
109
+ probeAgent?: AgentDetectionModelOptions['probeAgent'];
86
110
  }): AsyncGenerator<DetectedAgent>;
87
111
  export {};
88
112
  //# sourceMappingURL=detection.d.ts.map
package/dist/detection.js CHANGED
@@ -1,3 +1,7 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { userToolchainBinDirs } from './executables.js';
3
+ import { defaultRuntimeState } from './runtime-state.js';
4
+ import { getRuntimeModelDiscoveryDeps } from './model-discovery-adapters.js';
1
5
  /**
2
6
  * @module detection
3
7
  *
@@ -12,9 +16,8 @@
12
16
  * `archived provenance ledger`.
13
17
  */
14
18
  import { execAgentFile } from './invocation.js';
15
- import { offlineSnapshot, modelDiscoveryForDef } from './model-discovery.js';
19
+ import { offlineSnapshot, modelDiscoveryForDef } from './model-discovery-native.js';
16
20
  import { rememberLiveModels } from './models.js';
17
- import { agentCapabilities } from './capabilities.js';
18
21
  import { installMetaForAgent } from './metadata.js';
19
22
  import { AGENT_DEFS } from './registry.js';
20
23
  import { applyAgentLaunchEnv, resolveAgentLaunch } from './launch.js';
@@ -109,8 +112,8 @@ function unavailableAgent(def, diagnostics = []) {
109
112
  }
110
113
  // Probe the agent's `--help` once and record which advertised flags the
111
114
  // installed CLI supports, so buildArgs can consult the cache. Extracted
112
- // from the main probe so it can run concurrently with model + auth probing
113
- // instead of blocking them. Returns the capability map (or null when the
115
+ // from the main probe so it can run before model discovery. It shares the existing
116
+ // capability cache with real launches. Returns the capability map (or null when the
114
117
  // agent declares no help/capability metadata or the probe failed).
115
118
  async function probeCapabilities(def, launchPath, env) {
116
119
  if (!def.helpArgs || !def.capabilityFlags)
@@ -133,8 +136,6 @@ async function readCapabilities(helpArgs, capabilityFlags, launchPath, env) {
133
136
  }
134
137
  return caps;
135
138
  }
136
- /** One in-flight or settled `--help` probe per def id + launch path, so a run never re-spawns it. */
137
- const capabilityProbes = new Map();
138
139
  /**
139
140
  * Makes sure `agentCapabilities` holds this def's `--help`-probed flags before its `buildArgs` runs.
140
141
  *
@@ -154,7 +155,9 @@ const capabilityProbes = new Map();
154
155
  * @param env - The env the run will spawn with.
155
156
  * @complexity O(1) after the first call per def + path; the first call costs one `--help` spawn (5 s cap).
156
157
  */
157
- export function ensureAgentCapabilities({ def, launchPath, env }) {
158
+ export function ensureAgentCapabilities({ def, launchPath, env }, { state = defaultRuntimeState, probe = ({ helpArgs, capabilityFlags, launchPath, env }) => readCapabilities(helpArgs, capabilityFlags, launchPath, env) } = {}) {
159
+ const agentCapabilities = state.capabilities;
160
+ const capabilityProbes = state.capabilityProbes;
158
161
  const { helpArgs, capabilityFlags } = def;
159
162
  if (!helpArgs || !capabilityFlags || agentCapabilities.has(def.id))
160
163
  return Promise.resolve();
@@ -162,12 +165,12 @@ export function ensureAgentCapabilities({ def, launchPath, env }) {
162
165
  const existing = capabilityProbes.get(key);
163
166
  if (existing)
164
167
  return existing;
165
- const probe = readCapabilities(helpArgs, capabilityFlags, launchPath, env).then((caps) => {
168
+ const pending = probe({ helpArgs, capabilityFlags, launchPath, env }, {}).then((caps) => {
166
169
  if (!agentCapabilities.has(def.id))
167
170
  agentCapabilities.set(def.id, caps);
168
171
  }, () => undefined);
169
- capabilityProbes.set(key, probe);
170
- return probe;
172
+ capabilityProbes.set(key, pending);
173
+ return pending;
171
174
  }
172
175
  /**
173
176
  * The effort-level override a live model catalog earns, if any.
@@ -186,8 +189,8 @@ function derivedReasoningOverride(def, models) {
186
189
  return derived && derived.length > 0 ? { reasoningOptions: derived } : {};
187
190
  }
188
191
  /** The env every probe of one def spawns with — the same env a real run would see. */
189
- function probeEnvForLaunch(def, configuredEnv, launch) {
190
- return applyAgentLaunchEnv({ env: spawnEnvForAgent({ agentId: def.id, baseEnv: { ...process.env, ...(def.env || {}) } }, { configuredEnv: configuredEnv, hooks: {} }), launch: launch });
192
+ function probeEnvForLaunch(def, configuredEnv, launch, options = {}) {
193
+ return applyAgentLaunchEnv({ env: spawnEnvForAgent({ agentId: def.id, baseEnv: { ...(options.env ?? process.env), ...(def.env || {}) } }, { configuredEnv: configuredEnv, hooks: {} }), launch: launch }, { appendPathDirs: userToolchainBinDirs({}, options) });
191
194
  }
192
195
  function unavailableModels(def) {
193
196
  const catalog = offlineSnapshot('', Date.now(), def.fallbackModels);
@@ -205,15 +208,15 @@ function unavailableModels(def) {
205
208
  * `source: 'fallback'` (including when the binary is not found). Never rejects.
206
209
  * @complexity At most one model-listing spawn, bounded by the def's own timeout.
207
210
  */
208
- export async function probeAgentModels({ def }, { configuredEnv = {}, amrProfileResolver = noopAmrProfileResolver, context, deps, force = false } = {}) {
211
+ export async function probeAgentModels({ def }, { configuredEnv = {}, amrProfileResolver = noopAmrProfileResolver, context, deps, force = false, state = defaultRuntimeState, env = process.env } = {}) {
209
212
  try {
210
- const launch = resolveAgentLaunch({ def: def }, { configuredEnv: configuredEnv });
213
+ const launch = resolveAgentLaunch({ def: def }, { configuredEnv: configuredEnv, state, env });
211
214
  if (!launch.launchPath)
212
215
  return unavailableModels(def);
213
- const probeEnv = probeEnvForLaunch(def, configuredEnv, launch);
214
- const fetched = await fetchModels(def, launch.launchPath, probeEnv, { ...(context ? { context } : {}), ...(deps ? { deps } : {}), force });
216
+ const probeEnv = probeEnvForLaunch(def, configuredEnv, launch, { state, env });
217
+ const fetched = await fetchModels(def, launch.launchPath, probeEnv, { ...(context ? { context } : {}), deps: deps ?? getRuntimeModelDiscoveryDeps({ state }), force });
215
218
  const result = withRememberedAmrModels(def, probeEnv, fetched, amrProfileResolver);
216
- rememberDetectedModelList(def, configuredEnv, result.models, amrProfileResolver);
219
+ rememberDetectedModelList(def, configuredEnv, result.models, amrProfileResolver, { state, env });
217
220
  return result;
218
221
  }
219
222
  catch {
@@ -229,28 +232,34 @@ async function probe(def, configuredEnv = {}, amrProfileResolver, options = {})
229
232
  // detection probes the shim but chat/run spawns the native binary, the
230
233
  // UI incorrectly reports "not installed" until the user pins the bin
231
234
  // override by hand even though the real launch path is healthy.
232
- const launch = resolveAgentLaunch({ def: def }, { configuredEnv: configuredEnv });
235
+ const launch = resolveAgentLaunch({ def: def }, { configuredEnv: configuredEnv, ...(options.state ? { state: options.state } : {}), ...(options.env ? { env: options.env } : {}) });
233
236
  if (!launch.selectedPath || !launch.launchPath) {
234
- return unavailableAgent(def, [buildExecutableDiagnostic({ def: def }, { configuredEnv: configuredEnv })]);
237
+ return unavailableAgent(def, [buildExecutableDiagnostic({ def: def }, { configuredEnv: configuredEnv, ...(options.state ? { state: options.state } : {}), ...(options.env ? { env: options.env } : {}) })]);
235
238
  }
236
- const probeEnv = probeEnvForLaunch(def, configuredEnv, launch);
239
+ const probeEnv = probeEnvForLaunch(def, configuredEnv, launch, options);
237
240
  const outcome = await probeVersionAtPath(def, launch.launchPath, probeEnv);
238
241
  if (outcome.kind === 'not-invocable') {
239
242
  return unavailableAgent(def, [buildNotInvocableDiagnostic({ def: def, launch: launch, cause: outcome.cause })]);
240
243
  }
241
- // The version probe must finish first (it gates availability), but the
242
- // three post-version probes are independent reads — run them
243
- // concurrently so a single agent's detection wall is
244
- // max(help, models, auth) instead of the sum. `--help` capabilities are
245
- // cached on `agentCapabilities` for buildArgs to consult.
246
- const [caps, modelResult, auth] = await Promise.all([
247
- probeCapabilities(def, launch.launchPath, probeEnv),
248
- fetchModels(def, launch.launchPath, probeEnv, { version: outcome.version, ...options }),
249
- probeAgentAuthStatus({ def: def, resolvedBin: launch.launchPath, env: probeEnv }),
250
- ]);
244
+ // The version probe must finish first (it gates availability). Cheap help/auth checks
245
+ // precede model discovery; the shared process scheduler prevents concurrent CLI startups.
246
+ // Capability evidence belongs to the existing ensureAgentCapabilities cache, also used by runs.
247
+ const runtimeState = options.state ?? defaultRuntimeState;
248
+ const capabilityKey = `${def.id}\u0000${launch.launchPath}`;
249
+ if (options.force || !runtimeState.capabilityProbes.has(capabilityKey)) {
250
+ runtimeState.capabilities.delete(def.id);
251
+ if (options.force)
252
+ runtimeState.capabilityProbes.delete(capabilityKey);
253
+ }
254
+ await ensureAgentCapabilities({ def, launchPath: launch.launchPath, env: probeEnv }, { state: runtimeState,
255
+ probe: async () => (await probeCapabilities(def, launch.launchPath, probeEnv)) ?? {},
256
+ });
257
+ const caps = runtimeState.capabilities.get(def.id);
258
+ const auth = await probeAgentAuthStatus({ def, resolvedBin: launch.launchPath, env: probeEnv });
259
+ const modelResult = await fetchModels(def, launch.launchPath, probeEnv, { version: outcome.version, ...options, deps: options.deps ?? getRuntimeModelDiscoveryDeps({ state: runtimeState }) });
251
260
  const surfacedModelResult = withRememberedAmrModels(def, probeEnv, modelResult, amrProfileResolver);
252
261
  if (caps) {
253
- agentCapabilities.set(def.id, caps);
262
+ (options.state ?? defaultRuntimeState).capabilities.set(def.id, caps);
254
263
  }
255
264
  const authDiagnostic = auth ? buildAuthDiagnostic({ def: def, auth: auth }) : null;
256
265
  return {
@@ -290,8 +299,38 @@ function stripFns(def) {
290
299
  const { discoverModels: _discoverModels, resolveDefaultModel: _resolveDefaultModel, buildArgs: _buildArgs, listModels: _listModels, fetchModels: _fetchModels, deriveReasoningOptions: _deriveReasoningOptions, fallbackModels: _fallbackModels, fallbackModelsAssertedAt: _fallbackModelsAssertedAt, helpArgs: _helpArgs, capabilityFlags: _capabilityFlags, fallbackBins: _fallbackBins, versionProbeTimeoutMs: _versionProbeTimeoutMs, maxPromptArgBytes: _maxPromptArgBytes, env: _env, inactivityTimeoutMs: _inactivityTimeoutMs, authProbe: _authProbe, needsAgentLogFile: _needsAgentLogFile, stdoutPolicy: _stdoutPolicy, runtimeLock: _runtimeLock, ...rest } = def;
291
300
  return rest;
292
301
  }
293
- async function safeProbe(def, configuredEnv = {}, amrProfileResolver, options = {}) {
302
+ /** Cache complete detection per host and effective launch. No TTL re-spawns version/help/auth.
303
+ * Explicit force queues after the current scan, so a rescan cannot overlap its predecessor.
304
+ * Hash credentials rather than retaining them in keys. @complexity O(e) launch-env hashing. */
305
+ function safeProbe(def, configuredEnv, amrProfileResolver, options = {}) {
306
+ const state = options.state ?? defaultRuntimeState;
307
+ let key;
308
+ try {
309
+ key = detectionCacheKey({ def, configuredEnv, options, state });
310
+ }
311
+ catch {
312
+ return Promise.resolve(unavailableAgent(def));
313
+ }
314
+ const previous = state.detections.get(key);
315
+ if (previous && !options.force)
316
+ return previous;
317
+ const pending = (previous ?? Promise.resolve()).then(() => executeSafeProbe(def, configuredEnv, amrProfileResolver, options));
318
+ state.detections.set(key, pending);
319
+ return pending;
320
+ }
321
+ /** Effective launch material is hashed, so cache keys never expose credentials.
322
+ * Legacy remembered-model profile resolution must not gate executable availability. */
323
+ function detectionCacheKey({ def, configuredEnv, options, state }, _options = {}) {
324
+ const launch = resolveAgentLaunch({ def }, { configuredEnv, state, ...(options.env ? { env: options.env } : {}) });
325
+ const env = options.env ?? process.env;
326
+ return createHash('sha256').update(JSON.stringify({ id: def.id, path: launch.launchPath,
327
+ env: Object.entries(env).sort(([a], [b]) => a.localeCompare(b)), configuredEnv,
328
+ context: options.context })).digest('hex');
329
+ }
330
+ async function executeSafeProbe(def, configuredEnv = {}, amrProfileResolver, options = {}) {
294
331
  try {
332
+ if (options.probeAgent)
333
+ return await options.probeAgent({ def, configuredEnv, amrProfileResolver }, options);
295
334
  return await probe(def, configuredEnv, amrProfileResolver, options);
296
335
  }
297
336
  catch {
@@ -302,19 +341,19 @@ async function safeProbe(def, configuredEnv = {}, amrProfileResolver, options =
302
341
  return unavailableAgent(def);
303
342
  }
304
343
  }
305
- function rememberDetectedLiveModels(def, configuredEnv, agent, amrProfileResolver) {
306
- rememberDetectedModelList(def, configuredEnv, agent.models, amrProfileResolver);
344
+ function rememberDetectedLiveModels(def, configuredEnv, agent, amrProfileResolver, options = {}) {
345
+ rememberDetectedModelList(def, configuredEnv, agent.models, amrProfileResolver, options);
307
346
  }
308
- function rememberDetectedModelList(def, configuredEnv, models, amrProfileResolver) {
347
+ function rememberDetectedModelList(def, configuredEnv, models, amrProfileResolver, options = {}) {
309
348
  if (def.id === 'amr' && models.length === 0)
310
349
  return;
311
350
  const scope = def.id === 'amr'
312
- ? amrModelScopeFromEnv({ ...process.env, ...(def.env || {}), ...configuredEnv }, amrProfileResolver)
351
+ ? amrModelScopeFromEnv({ ...(options.env ?? process.env), ...(def.env || {}), ...configuredEnv }, amrProfileResolver)
313
352
  : null;
314
- rememberLiveModels({ agentId: def.id, models: models.filter((row) => row.identityKind !== 'routing-mode') }, { scope: scope });
353
+ rememberLiveModels({ agentId: def.id, models: models.filter((row) => row.identityKind !== 'routing-mode') }, { scope: scope, ...(options.state ? { state: options.state } : {}) });
315
354
  }
316
- export async function detectAgents({}, { configuredEnvByAgent = {}, amrProfileResolver = noopAmrProfileResolver, modelContextByAgent = {}, modelDiscoveryDeps, force = false } = {}) {
317
- const results = await Promise.all(AGENT_DEFS.map((def) => safeProbe(def, configuredEnvByAgent?.[def.id] ?? {}, amrProfileResolver, { ...(modelContextByAgent[def.id] ? { context: modelContextByAgent[def.id] } : {}), ...(modelDiscoveryDeps ? { deps: modelDiscoveryDeps } : {}), force })));
355
+ export async function detectAgents({}, { configuredEnvByAgent = {}, amrProfileResolver = noopAmrProfileResolver, modelContextByAgent = {}, modelDiscoveryDeps, force = false, state = defaultRuntimeState, env = process.env, probeAgent } = {}) {
356
+ const results = await Promise.all(AGENT_DEFS.map((def) => safeProbe(def, configuredEnvByAgent?.[def.id] ?? {}, amrProfileResolver, { ...(modelContextByAgent[def.id] ? { context: modelContextByAgent[def.id] } : {}), deps: modelDiscoveryDeps ?? getRuntimeModelDiscoveryDeps({ state }), force, state, env, ...(probeAgent ? { probeAgent } : {}) })));
318
357
  // Refresh the validation cache from whatever we just surfaced to the UI
319
358
  // so a chat endpoint can accept any model the user could have just
320
359
  // picked, including ones that only showed up after a CLI re-auth.
@@ -323,7 +362,7 @@ export async function detectAgents({}, { configuredEnvByAgent = {}, amrProfileRe
323
362
  // is in-bounds for `AGENT_DEFS` too — the assertion just satisfies
324
363
  // `noUncheckedIndexedAccess`.
325
364
  const def = AGENT_DEFS[index];
326
- rememberDetectedLiveModels(def, configuredEnvByAgent?.[def.id] ?? {}, agent, amrProfileResolver);
365
+ rememberDetectedLiveModels(def, configuredEnvByAgent?.[def.id] ?? {}, agent, amrProfileResolver, { state, env });
327
366
  }
328
367
  return results;
329
368
  }
@@ -333,9 +372,9 @@ export async function detectAgents({}, { configuredEnvByAgent = {}, amrProfileRe
333
372
  // validation cache is refreshed per-agent (same effect as the batch path,
334
373
  // just incrementally). `detectAgents` keeps the array contract for callers
335
374
  // that don't care about incremental delivery.
336
- export async function* detectAgentsStream({}, { configuredEnvByAgent = {}, amrProfileResolver = noopAmrProfileResolver, modelContextByAgent = {}, modelDiscoveryDeps, force = false } = {}) {
337
- const tagged = AGENT_DEFS.map((def, index) => safeProbe(def, configuredEnvByAgent?.[def.id] ?? {}, amrProfileResolver, { ...(modelContextByAgent[def.id] ? { context: modelContextByAgent[def.id] } : {}), ...(modelDiscoveryDeps ? { deps: modelDiscoveryDeps } : {}), force }).then((agent) => {
338
- rememberDetectedLiveModels(def, configuredEnvByAgent?.[def.id] ?? {}, agent, amrProfileResolver);
375
+ export async function* detectAgentsStream({}, { configuredEnvByAgent = {}, amrProfileResolver = noopAmrProfileResolver, modelContextByAgent = {}, modelDiscoveryDeps, force = false, state = defaultRuntimeState, env = process.env, probeAgent } = {}) {
376
+ const tagged = AGENT_DEFS.map((def, index) => safeProbe(def, configuredEnvByAgent?.[def.id] ?? {}, amrProfileResolver, { ...(modelContextByAgent[def.id] ? { context: modelContextByAgent[def.id] } : {}), deps: modelDiscoveryDeps ?? getRuntimeModelDiscoveryDeps({ state }), force, state, env, ...(probeAgent ? { probeAgent } : {}) }).then((agent) => {
377
+ rememberDetectedLiveModels(def, configuredEnvByAgent?.[def.id] ?? {}, agent, amrProfileResolver, { state, env });
339
378
  return { index, agent };
340
379
  }));
341
380
  const pending = new Set(tagged.keys());
@@ -1,9 +1,14 @@
1
+ /**
2
+ * @module diagnostics
3
+ * Explain three detection failures: unresolved executable, uninvocable executable or missing authentication.
4
+ */
5
+ import { type ExecutableResolutionOptions } from './executables.js';
1
6
  import type { AgentLaunchResolution } from './launch.js';
2
7
  import type { AgentAuthProbeResult } from './auth.js';
3
8
  import type { AgentDiagnostic, RuntimeAgentDef } from './types.js';
4
9
  export declare function buildExecutableDiagnostic({ def }: {
5
10
  def: Pick<RuntimeAgentDef, 'id' | 'name' | 'bin'>;
6
- }, { configuredEnv }?: {
11
+ }, { configuredEnv, ...options }?: ExecutableResolutionOptions & {
7
12
  configuredEnv?: Record<string, string>;
8
13
  }): AgentDiagnostic;
9
14
  export type NotInvocableCause = 'not-executable' | 'missing-target';
@@ -16,7 +16,7 @@ function setEnvIntent(agentId) {
16
16
  // user-supplied `*_BIN` override points at a missing/invalid file. The two
17
17
  // cases get different copy + fix affordances because the remedy differs
18
18
  // (install / point us at the binary vs. fix or clear the override).
19
- export function buildExecutableDiagnostic({ def }, { configuredEnv = {} } = {}) {
19
+ export function buildExecutableDiagnostic({ def }, { configuredEnv = {}, ...options } = {}) {
20
20
  const envKey = agentBinEnvKey({ agentId: def.id });
21
21
  const overrideRaw = envKey ? configuredEnv?.[envKey]?.trim() : '';
22
22
  if (envKey && overrideRaw) {
@@ -32,7 +32,7 @@ export function buildExecutableDiagnostic({ def }, { configuredEnv = {} } = {})
32
32
  reason: 'not-on-path',
33
33
  severity: 'error',
34
34
  message: `${def.name} (\`${def.bin}\`) was not found on your PATH.`,
35
- searchedDirs: agentSearchDirs().slice(0, MAX_SEARCHED_DIRS),
35
+ searchedDirs: agentSearchDirs({}, options).slice(0, MAX_SEARCHED_DIRS),
36
36
  fixActions: [{ kind: 'openInstall' }, ...setEnvIntent(def.id), { kind: 'rescan' }],
37
37
  };
38
38
  }
package/dist/env.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * sandboxOverlay. The engine must not import a product's config/sandbox subsystems or env names.
8
8
  */
9
9
  import os from 'node:os';
10
- import { mergeProxyAwareEnv, resolveSystemProxyEnv } from '@jini-ai/platform';
10
+ import { mergeProxyAwareEnv, resolveSystemProxyEnv } from '@jini-ai/platform/proxy-env';
11
11
  import { expandConfiguredEnv } from './paths.js';
12
12
  import { resolveAmrOpenCodeExecutable } from './executables.js';
13
13
  function stripKeysCaseInsensitive(env, keysToStrip) {
@@ -1,4 +1,10 @@
1
+ import { type AgentRuntimeState } from './runtime-state.js';
1
2
  import type { RuntimeAgentDef } from './types.js';
3
+ /** Host environment and runtime state used by executable resolution. */
4
+ export interface ExecutableResolutionOptions {
5
+ env?: NodeJS.ProcessEnv;
6
+ state?: AgentRuntimeState;
7
+ }
2
8
  /**
3
9
  * Override the env var names `resolveDetectionHome` / the packaged-binary
4
10
  * lookup read. The host application (e.g. the OD adapter) calls this once
@@ -10,27 +16,27 @@ export declare function configureExecutableResolutionEnv({ overrides }: {
10
16
  agentHomeEnvVar?: string;
11
17
  resourceRootEnvVar?: string;
12
18
  };
19
+ }, { state }?: {
20
+ state?: AgentRuntimeState;
13
21
  }): void;
14
- export declare function userToolchainBinDirs(): string[];
15
- export declare function agentSearchDirs(): string[];
22
+ export declare function userToolchainBinDirs(_required?: Record<string, never>, options?: ExecutableResolutionOptions): string[];
23
+ export declare function agentSearchDirs(_required?: Record<string, never>, options?: ExecutableResolutionOptions): string[];
16
24
  export declare function agentBinEnvKey({ agentId }: {
17
25
  agentId: string | undefined;
18
26
  }): string | null;
19
27
  export declare function resolveOnPath({ bin }: {
20
28
  bin: string;
21
- }): string | null;
22
- export declare function resolveAmrOpenCodeExecutable({}: {}, { env }?: {
23
- env?: Record<string, string | undefined>;
24
- }): string | null;
25
- export declare function codexAppBundleCandidates(): string[];
29
+ }, options?: ExecutableResolutionOptions): string | null;
30
+ export declare function resolveAmrOpenCodeExecutable({}: {}, { env, state }?: ExecutableResolutionOptions): string | null;
31
+ export declare function codexAppBundleCandidates(_required?: Record<string, never>, options?: ExecutableResolutionOptions): string[];
26
32
  export declare function resolveAgentExecutable({ def }: {
27
33
  def: RuntimeAgentDef;
28
- }, { configuredEnv }?: {
34
+ }, options?: ExecutableResolutionOptions & {
29
35
  configuredEnv?: Record<string, string>;
30
36
  }): string | null;
31
37
  export declare function inspectAgentExecutableResolution({ def }: {
32
38
  def: RuntimeAgentDef;
33
- }, { configuredEnv }?: {
39
+ }, options?: ExecutableResolutionOptions & {
34
40
  configuredEnv?: Record<string, string>;
35
41
  }): {
36
42
  configuredOverridePath: string | null;