@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,3 +1,4 @@
1
+ import { defaultRuntimeState } from './runtime-state.js';
1
2
  /**
2
3
  * @module executables
3
4
  *
@@ -23,7 +24,7 @@ import { accessSync, constants, existsSync, statSync } from 'node:fs';
23
24
  import { delimiter } from 'node:path';
24
25
  import path from 'node:path';
25
26
  import { homedir } from 'node:os';
26
- import { wellKnownUserToolchainBins } from '@jini-ai/platform';
27
+ import { wellKnownUserToolchainBins } from '@jini-ai/platform/toolchain';
27
28
  import { expandHomePath } from './paths.js';
28
29
  const AGENT_BIN_ENV_KEYS = new Map([
29
30
  ['amr', 'VELA_BIN'],
@@ -53,59 +54,58 @@ const AGENT_BIN_ENV_KEYS = new Map([
53
54
  ['trae-cli', 'TRAE_CLI_BIN'],
54
55
  ['vibe', 'VIBE_BIN'],
55
56
  ]);
56
- /** Env var names consulted by detection-home/resource-root resolution. Overridable via `configureExecutableResolutionEnv`. */
57
- const executableResolutionEnv = {
58
- agentHomeEnvVar: 'AGENT_RUNTIME_HOME',
59
- resourceRootEnvVar: 'AGENT_RUNTIME_RESOURCE_ROOT',
60
- };
61
57
  /**
62
58
  * Override the env var names `resolveDetectionHome` / the packaged-binary
63
59
  * lookup read. The host application (e.g. the OD adapter) calls this once
64
60
  * at startup if it wants its own namespaced env vars instead of the
65
61
  * generic defaults.
66
62
  */
67
- export function configureExecutableResolutionEnv({ overrides }) {
63
+ export function configureExecutableResolutionEnv({ overrides }, { state = defaultRuntimeState } = {}) {
64
+ const executableResolutionEnv = state.executable.envNames;
68
65
  if (overrides.agentHomeEnvVar)
69
66
  executableResolutionEnv.agentHomeEnvVar = overrides.agentHomeEnvVar;
70
67
  if (overrides.resourceRootEnvVar)
71
68
  executableResolutionEnv.resourceRootEnvVar = overrides.resourceRootEnvVar;
72
69
  }
73
70
  const TOOLCHAIN_DIR_CACHE_TTL_MS = 5000;
74
- let cachedToolchainHome = null;
75
- let cachedToolchainDirs = null;
76
- let cachedToolchainDirsAt = 0;
71
+ // Toolchain cache and env names live with the injected host state.
77
72
  // Resolve the home directory detection should search, honoring the
78
73
  // configured agent-home override env var. `hasOverride` lets callers scope
79
74
  // strictly to the override home (skipping real-machine system locations) so
80
75
  // sandboxed detection runs and tests stay deterministic instead of
81
76
  // reaching the host.
82
- function resolveDetectionHome() {
83
- const homeOverride = process.env[executableResolutionEnv.agentHomeEnvVar];
84
- return { home: homeOverride || homedir(), hasOverride: Boolean(homeOverride) };
77
+ function resolveDetectionHome(options = {}) {
78
+ const env = options.env ?? process.env;
79
+ const homeOverride = env[(options.state ?? defaultRuntimeState).executable.envNames.agentHomeEnvVar];
80
+ return { home: homeOverride || env.HOME || env.USERPROFILE || homedir(), hasOverride: Boolean(homeOverride) };
85
81
  }
86
- function userToolchainDirs() {
87
- const { home, hasOverride } = resolveDetectionHome();
82
+ function userToolchainDirs(options = {}) {
83
+ const env = options.env ?? process.env;
84
+ const cache = (options.state ?? defaultRuntimeState).executable;
85
+ const { home, hasOverride } = resolveDetectionHome(options);
86
+ // Every environment field the existing toolchain adapter consults participates in the cache key.
87
+ const key = JSON.stringify([home, hasOverride, ...['VP_HOME', 'NPM_CONFIG_PREFIX', 'npm_config_prefix', 'APPDATA', 'LOCALAPPDATA', 'MISE_DATA_DIR', 'FNM_DIR'].map(name => env[name])]);
88
88
  const homeOverride = hasOverride ? home : undefined;
89
89
  const now = Date.now();
90
- if (cachedToolchainHome === home &&
91
- cachedToolchainDirs &&
92
- now - cachedToolchainDirsAt < TOOLCHAIN_DIR_CACHE_TTL_MS) {
93
- return cachedToolchainDirs;
90
+ if (cache.toolchainKey === key &&
91
+ cache.toolchainDirs &&
92
+ now - cache.toolchainDirsAt < TOOLCHAIN_DIR_CACHE_TTL_MS) {
93
+ return cache.toolchainDirs;
94
94
  }
95
- cachedToolchainHome = home;
96
- cachedToolchainDirsAt = now;
95
+ cache.toolchainKey = key;
96
+ cache.toolchainDirsAt = now;
97
97
  // When the agent-home override is set, scope the search strictly to the
98
98
  // override home: skip Homebrew / /usr/local *and* pass an empty env so
99
99
  // that a developer or CI runner with NPM_CONFIG_PREFIX /
100
100
  // npm_config_prefix exported can't leak the real machine's <prefix>/bin
101
101
  // into a sandboxed detection run. Without this, tests that build a tmp
102
102
  // home would be machine-environment-dependent.
103
- cachedToolchainDirs = wellKnownUserToolchainBins({
103
+ cache.toolchainDirs = wellKnownUserToolchainBins({
104
104
  home,
105
105
  includeSystemBins: process.platform !== 'win32' && !homeOverride,
106
- env: homeOverride ? {} : process.env,
106
+ env: homeOverride ? {} : env,
107
107
  });
108
- return cachedToolchainDirs;
108
+ return cache.toolchainDirs;
109
109
  }
110
110
  // The user-level toolchain bin directories (Homebrew, ~/.local/bin, ~/.bun/bin,
111
111
  // version-manager node dirs, npm prefixes, …) that binary *resolution* searches
@@ -113,18 +113,19 @@ function userToolchainDirs() {
113
113
  // a binary can resolve here yet fail to *execute* if its shebang interpreter
114
114
  // (e.g. `#!/usr/bin/env bun`) lives in one of these dirs and the spawn PATH
115
115
  // doesn't include it. Keeping resolution and spawn PATH symmetric fixes that.
116
- export function userToolchainBinDirs() {
117
- return userToolchainDirs();
116
+ export function userToolchainBinDirs(_required = {}, options = {}) {
117
+ return userToolchainDirs(options);
118
118
  }
119
- function resolvePathDirs() {
119
+ function resolvePathDirs(options = {}) {
120
+ const env = options.env ?? process.env;
120
121
  const seen = new Set();
121
122
  const dirs = [
122
- ...(process.env.PATH || '').split(delimiter),
123
+ ...(env.PATH || '').split(delimiter),
123
124
  // GUI launchers (macOS .app bundles, Linux .desktop files) often start
124
125
  // with a minimal PATH. Include common user-level CLI install locations
125
126
  // so agent detection matches the user's shell-installed tools,
126
127
  // especially Node version managers.
127
- ...userToolchainDirs(),
128
+ ...userToolchainDirs(options),
128
129
  ];
129
130
  return dirs.filter((dir) => {
130
131
  if (!dir || seen.has(dir))
@@ -137,8 +138,8 @@ function resolvePathDirs() {
137
138
  // detection can attach it to a `not-on-path` diagnostic verbatim — the UI
138
139
  // shows the user where we actually looked before asking them to set an
139
140
  // explicit binary path, instead of recomputing PATH client-side.
140
- export function agentSearchDirs() {
141
- return resolvePathDirs();
141
+ export function agentSearchDirs(_required = {}, options = {}) {
142
+ return resolvePathDirs(options);
142
143
  }
143
144
  // The `*_BIN` environment variable that overrides PATH detection for a given
144
145
  // agent id (e.g. `cursor-agent` → `CURSOR_AGENT_BIN`), or null when the agent
@@ -148,11 +149,12 @@ export function agentBinEnvKey({ agentId }) {
148
149
  return null;
149
150
  return AGENT_BIN_ENV_KEYS.get(agentId) ?? null;
150
151
  }
151
- export function resolveOnPath({ bin }) {
152
+ export function resolveOnPath({ bin }, options = {}) {
153
+ const env = options.env ?? process.env;
152
154
  const exts = process.platform === 'win32'
153
- ? (process.env.PATHEXT || '.EXE;.CMD;.BAT').split(';')
155
+ ? (env.PATHEXT || '.EXE;.CMD;.BAT').split(';')
154
156
  : [''];
155
- const dirs = resolvePathDirs();
157
+ const dirs = resolvePathDirs(options);
156
158
  for (const dir of dirs) {
157
159
  for (const ext of exts) {
158
160
  const full = path.join(dir, bin + ext);
@@ -162,17 +164,18 @@ export function resolveOnPath({ bin }) {
162
164
  }
163
165
  return null;
164
166
  }
165
- function looksExecutableOnWindows(filePath) {
167
+ function looksExecutableOnWindows(filePath, options = {}) {
168
+ const env = options.env ?? process.env;
166
169
  const ext = path.extname(filePath).trim().toUpperCase();
167
170
  if (!ext)
168
171
  return false;
169
- const executableExts = (process.env.PATHEXT || '.EXE;.CMD;.BAT')
172
+ const executableExts = (env.PATHEXT || '.EXE;.CMD;.BAT')
170
173
  .split(';')
171
174
  .map((value) => value.trim().toUpperCase())
172
175
  .filter(Boolean);
173
176
  return executableExts.includes(ext);
174
177
  }
175
- function executableFilePath(raw) {
178
+ function executableFilePath(raw, options = {}) {
176
179
  if (typeof raw !== 'string' || raw.trim().length === 0)
177
180
  return null;
178
181
  const expanded = expandHomePath({ value: raw.trim() });
@@ -182,7 +185,7 @@ function executableFilePath(raw) {
182
185
  if (!statSync(expanded).isFile())
183
186
  return null;
184
187
  if (process.platform === 'win32') {
185
- if (!looksExecutableOnWindows(expanded))
188
+ if (!looksExecutableOnWindows(expanded, options))
186
189
  return null;
187
190
  }
188
191
  else {
@@ -198,30 +201,32 @@ function executableFilePath(raw) {
198
201
  // `def.bin` first, then walks `def.fallbackBins` in order. Used for agents
199
202
  // whose forks ship under a different binary name but speak the exact same
200
203
  // CLI. Returns null when no candidate is on PATH.
201
- function configuredExecutableOverride(def, configuredEnv = {}) {
204
+ function configuredExecutableOverride(def, configuredEnv = {}, options = {}) {
202
205
  const envKey = AGENT_BIN_ENV_KEYS.get(def?.id);
203
206
  if (!envKey)
204
207
  return null;
205
- return executableFilePath(configuredEnv?.[envKey]);
208
+ return executableFilePath(configuredEnv?.[envKey], options);
206
209
  }
207
- export function resolveAmrOpenCodeExecutable({}, { env = process.env } = {}) {
208
- const configured = executableFilePath(env.VELA_OPENCODE_BIN);
210
+ export function resolveAmrOpenCodeExecutable({}, { env = process.env, state = defaultRuntimeState } = {}) {
211
+ const options = { env, state };
212
+ const executableResolutionEnv = state.executable.envNames;
213
+ const configured = executableFilePath(env.VELA_OPENCODE_BIN, options);
209
214
  if (configured)
210
215
  return configured;
211
216
  // In packaged builds prefer the bundled companion under
212
217
  // `<resourceRoot>/bin/libexec/opencode/opencode` so a stale global
213
218
  // `opencode` on the user's PATH can't override the known-good build that
214
219
  // shipped with this app. PATH is only consulted as a last resort.
215
- const resourceRoot = (env[executableResolutionEnv.resourceRootEnvVar] ?? process.env[executableResolutionEnv.resourceRootEnvVar])?.trim();
220
+ const resourceRoot = (env[executableResolutionEnv.resourceRootEnvVar])?.trim();
216
221
  if (resourceRoot) {
217
- const bundledDir = packagedVelaOpenCodeCompanionTree(resourceRoot);
222
+ const bundledDir = packagedVelaOpenCodeCompanionTree(resourceRoot, options);
218
223
  if (bundledDir) {
219
- const bundled = executableFilePath(path.join(bundledDir, process.platform === 'win32' ? 'opencode.exe' : 'opencode'));
224
+ const bundled = executableFilePath(path.join(bundledDir, process.platform === 'win32' ? 'opencode.exe' : 'opencode'), options);
220
225
  if (bundled)
221
226
  return bundled;
222
227
  }
223
228
  }
224
- return resolveOnPath({ bin: 'opencode-cli' }) ?? resolveOnPath({ bin: 'opencode' });
229
+ return resolveOnPath({ bin: 'opencode-cli' }, options) ?? resolveOnPath({ bin: 'opencode' }, options);
225
230
  }
226
231
  // The AMR (vela) OpenCode companion ships as a
227
232
  // `<resourceRoot>/bin/libexec/opencode/opencode` *executable file*, not just
@@ -229,7 +234,7 @@ export function resolveAmrOpenCodeExecutable({}, { env = process.env } = {}) {
229
234
  // a false-positive availability path: `detectAgents()` would surface AMR as
230
235
  // available even though the first real run can't launch. Verify the inner
231
236
  // executable too.
232
- function packagedVelaOpenCodeCompanionTree(resourceRoot) {
237
+ function packagedVelaOpenCodeCompanionTree(resourceRoot, options = {}) {
233
238
  const candidate = path.join(resourceRoot, 'bin', 'libexec', 'opencode');
234
239
  const exe = path.join(candidate, process.platform === 'win32' ? 'opencode.exe' : 'opencode');
235
240
  try {
@@ -238,7 +243,7 @@ function packagedVelaOpenCodeCompanionTree(resourceRoot) {
238
243
  if (!statSync(exe).isFile())
239
244
  return null;
240
245
  if (process.platform === 'win32') {
241
- if (!looksExecutableOnWindows(exe))
246
+ if (!looksExecutableOnWindows(exe, options))
242
247
  return null;
243
248
  }
244
249
  else {
@@ -250,14 +255,16 @@ function packagedVelaOpenCodeCompanionTree(resourceRoot) {
250
255
  return null;
251
256
  }
252
257
  }
253
- function packagedBuiltInExecutable(def, configuredEnv = {}) {
258
+ function packagedBuiltInExecutable(def, configuredEnv = {}, options = {}) {
254
259
  if (def.id !== 'amr')
255
260
  return null;
256
- const resourceRoot = process.env[executableResolutionEnv.resourceRootEnvVar]?.trim();
261
+ const env = options.env ?? process.env;
262
+ const executableResolutionEnv = (options.state ?? defaultRuntimeState).executable.envNames;
263
+ const resourceRoot = env[executableResolutionEnv.resourceRootEnvVar]?.trim();
257
264
  if (!resourceRoot)
258
265
  return null;
259
- if (!resolveAmrOpenCodeExecutable({}, { env: { ...process.env, ...configuredEnv } }) &&
260
- !packagedVelaOpenCodeCompanionTree(resourceRoot)) {
266
+ if (!resolveAmrOpenCodeExecutable({}, { ...options, env: { ...env, ...configuredEnv } }) &&
267
+ !packagedVelaOpenCodeCompanionTree(resourceRoot, options)) {
261
268
  return null;
262
269
  }
263
270
  const candidate = path.join(resourceRoot, 'bin', process.platform === 'win32' ? 'vela.exe' : 'vela');
@@ -265,7 +272,7 @@ function packagedBuiltInExecutable(def, configuredEnv = {}) {
265
272
  if (!statSync(candidate).isFile())
266
273
  return null;
267
274
  if (process.platform === 'win32') {
268
- if (!looksExecutableOnWindows(candidate))
275
+ if (!looksExecutableOnWindows(candidate, options))
269
276
  return null;
270
277
  }
271
278
  else {
@@ -286,11 +293,11 @@ function packagedBuiltInExecutable(def, configuredEnv = {}) {
286
293
  // bundle. Probe the well-known bundle locations so app-only installs are
287
294
  // detected. This is a last-resort fallback that ranks below PATH, so an
288
295
  // explicit `npm i -g` / Homebrew / version-manager install always wins.
289
- function codexAppBundleExecutable(def) {
296
+ function codexAppBundleExecutable(def, options = {}) {
290
297
  if (def?.id !== 'codex')
291
298
  return null;
292
- for (const candidate of codexAppBundleCandidates()) {
293
- const resolved = executableFilePath(candidate);
299
+ for (const candidate of codexAppBundleCandidates({}, options)) {
300
+ const resolved = executableFilePath(candidate, options);
294
301
  if (resolved)
295
302
  return resolved;
296
303
  }
@@ -301,12 +308,12 @@ function codexAppBundleExecutable(def) {
301
308
  // depend on the host actually having `/Applications/Codex.app`), so tests
302
309
  // assert the built candidate list directly to catch a path typo or
303
310
  // ordering regression in the common real-world install case.
304
- export function codexAppBundleCandidates() {
311
+ export function codexAppBundleCandidates(_required = {}, options = {}) {
305
312
  // The Codex app bundle is a macOS-only concept; other platforms have no
306
313
  // analogous standalone install of the `codex` CLI to probe for here.
307
314
  if (process.platform !== 'darwin')
308
315
  return [];
309
- const { home, hasOverride } = resolveDetectionHome();
316
+ const { home, hasOverride } = resolveDetectionHome(options);
310
317
  const bundleSuffix = ['Codex.app', 'Contents', 'Resources', 'codex'];
311
318
  // User-scoped install (~/Applications). Honors the override home so
312
319
  // sandboxed detection runs and tests stay deterministic.
@@ -319,10 +326,10 @@ export function codexAppBundleCandidates() {
319
326
  }
320
327
  return candidates;
321
328
  }
322
- export function resolveAgentExecutable({ def }, { configuredEnv = {} } = {}) {
323
- return inspectAgentExecutableResolution({ def: def }, { configuredEnv: configuredEnv }).selectedPath;
329
+ export function resolveAgentExecutable({ def }, options = {}) {
330
+ return inspectAgentExecutableResolution({ def: def }, options).selectedPath;
324
331
  }
325
- export function inspectAgentExecutableResolution({ def }, { configuredEnv = {} } = {}) {
332
+ export function inspectAgentExecutableResolution({ def }, options = {}) {
326
333
  if (!def?.bin) {
327
334
  return {
328
335
  configuredOverridePath: null,
@@ -330,21 +337,22 @@ export function inspectAgentExecutableResolution({ def }, { configuredEnv = {} }
330
337
  selectedPath: null,
331
338
  };
332
339
  }
333
- const configuredOverridePath = configuredExecutableOverride(def, configuredEnv);
340
+ const configuredEnv = options.configuredEnv ?? {};
341
+ const configuredOverridePath = configuredExecutableOverride(def, configuredEnv, options);
334
342
  const candidates = [
335
343
  def.bin,
336
344
  ...(Array.isArray(def.fallbackBins) ? def.fallbackBins : []),
337
345
  ];
338
346
  let pathResolvedPath = null;
339
347
  for (const bin of candidates) {
340
- const resolved = resolveOnPath({ bin: bin });
348
+ const resolved = resolveOnPath({ bin: bin }, options);
341
349
  if (resolved) {
342
350
  pathResolvedPath = resolved;
343
351
  break;
344
352
  }
345
353
  }
346
- const builtInPath = packagedBuiltInExecutable(def, configuredEnv);
347
- const appBundlePath = codexAppBundleExecutable(def);
354
+ const builtInPath = packagedBuiltInExecutable(def, configuredEnv, options);
355
+ const appBundlePath = codexAppBundleExecutable(def, options);
348
356
  return {
349
357
  configuredOverridePath,
350
358
  pathResolvedPath,
package/dist/index.d.ts CHANGED
@@ -47,6 +47,7 @@ export * from './telemetry-sink.js';
47
47
  export * from './providers/index.js';
48
48
  /** ACP + pi-rpc protocol adapters over the shared JSON-line-stream core. See aliases above. */
49
49
  export { createJsonLineStream, type AcpMcpServerInput, type AcpPermissionDecision, type AcpPermissionHandler, type AcpPermissionOption, type AcpPermissionRequest, type AcpSessionController, type ModelOption, type AttachAcpSessionOptions, type AccountFailure, type AccountFailureClassifier, buildAcpSessionNewParams, normalizeModels, detectAcpModels, attachAcpSession, noopAccountFailureClassifier, mapPiRpcEvent, attachPiRpcSession, parsePiModels as parsePiRpcModels, type PiRpcSession, type PiRpcSessionOptions, } from './agent-protocol/index.js';
50
- export * from './model-discovery.js';
51
- export { defaultModelDiscoveryDeps, parseClaudeInitializeMetadata, parsePiRpcMetadata } from './model-discovery-adapters.js';
50
+ export * from './model-discovery-native.js';
51
+ export { getRuntimeModelDiscoveryDeps, defaultModelDiscoveryDeps, parseClaudeInitializeMetadata, parsePiRpcMetadata } from './model-discovery-adapters.js';
52
+ export * from './runtime-state.js';
52
53
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -70,6 +70,7 @@ export * from './telemetry-sink.js';
70
70
  export * from './providers/index.js';
71
71
  /** ACP + pi-rpc protocol adapters over the shared JSON-line-stream core. See aliases above. */
72
72
  export { createJsonLineStream, buildAcpSessionNewParams, normalizeModels, detectAcpModels, attachAcpSession, noopAccountFailureClassifier, mapPiRpcEvent, attachPiRpcSession, parsePiModels as parsePiRpcModels, } from './agent-protocol/index.js';
73
- export * from './model-discovery.js';
74
- export { defaultModelDiscoveryDeps, parseClaudeInitializeMetadata, parsePiRpcMetadata } from './model-discovery-adapters.js';
73
+ export * from './model-discovery-native.js';
74
+ export { getRuntimeModelDiscoveryDeps, defaultModelDiscoveryDeps, parseClaudeInitializeMetadata, parsePiRpcMetadata } from './model-discovery-adapters.js';
75
+ export * from './runtime-state.js';
75
76
  //# sourceMappingURL=index.js.map
@@ -1,11 +1,33 @@
1
+ /**
2
+ * @module invocation
3
+ *
4
+ * Short, read-only metadata probes (model-list / version / help /
5
+ * auth-status) for an agent CLI. Ported from OD's
6
+ * `apps/daemon/src/runtimes/invocation.ts` with one dependency swap:
7
+ * `createCommandInvocation` now comes from `@jini-ai/platform` instead of the
8
+ * OD workspace package it originally shipped from — the two are the same
9
+ * function (platform was already verbatim-lifted into `@jini-ai/platform`,
10
+ * see its `archived provenance ledger`).
11
+ */
12
+ import { execFile } from 'node:child_process';
1
13
  import type { RuntimeExecOptions } from './types.js';
14
+ declare const execFileP: typeof execFile.__promisify__;
15
+ type ProbeExecPort = (input: {
16
+ command: string;
17
+ args: string[];
18
+ options: RuntimeExecOptions & {
19
+ windowsVerbatimArguments?: boolean | undefined;
20
+ };
21
+ }, optional: Record<string, never>) => ReturnType<typeof execFileP>;
2
22
  export declare function execAgentFile({ command, args }: {
3
23
  command: string;
4
24
  args: string[];
5
- }, { options }?: {
25
+ }, { options, execute }?: {
6
26
  options?: RuntimeExecOptions;
7
- }): import("child_process").PromiseWithChild<{
27
+ execute?: ProbeExecPort;
28
+ }): Promise<{
8
29
  stdout: string | NonSharedBuffer;
9
30
  stderr: string | NonSharedBuffer;
10
31
  }>;
32
+ export {};
11
33
  //# sourceMappingURL=invocation.d.ts.map
@@ -10,9 +10,10 @@
10
10
  * see its `archived provenance ledger`).
11
11
  */
12
12
  import { execFile } from 'node:child_process';
13
+ import { scheduleProbeProcess } from './probe-scheduler.js';
13
14
  import os from 'node:os';
14
15
  import { promisify } from 'node:util';
15
- import { createCommandInvocation } from '@jini-ai/platform';
16
+ import { createCommandInvocation } from '@jini-ai/platform/command';
16
17
  const execFileP = promisify(execFile);
17
18
  // Agent probes (model-list / version / help / auth-status) are short
18
19
  // read-only metadata calls that never need the caller's project files.
@@ -27,7 +28,7 @@ const execFileP = promisify(execFile);
27
28
  // running dev server. A probe writing a stray lockfile under the OS temp
28
29
  // dir is harmless. Actual agent runs spawn elsewhere with an explicit
29
30
  // project cwd and are unaffected.
30
- export function execAgentFile({ command, args }, { options = {} } = {}) {
31
+ export function execAgentFile({ command, args }, { options = {}, execute = (input) => execFileP(input.command, input.args, input.options) } = {}) {
31
32
  const invocation = createCommandInvocation(options.env
32
33
  ? {
33
34
  command,
@@ -38,10 +39,21 @@ export function execAgentFile({ command, args }, { options = {} } = {}) {
38
39
  command,
39
40
  args,
40
41
  });
41
- return execFileP(invocation.command, invocation.args, {
42
- ...options,
43
- cwd: options.cwd ?? os.tmpdir(),
44
- windowsVerbatimArguments: invocation.windowsVerbatimArguments,
45
- });
42
+ return scheduleProbeProcess({ work: async () => {
43
+ const pending = execute({ command: invocation.command, args: invocation.args, options: {
44
+ killSignal: 'SIGKILL', ...options, cwd: options.cwd ?? os.tmpdir(),
45
+ windowsVerbatimArguments: invocation.windowsVerbatimArguments,
46
+ } }, {});
47
+ try {
48
+ return await pending;
49
+ }
50
+ finally {
51
+ // AbortError can precede close. Hold the shared slot until the child actually exits.
52
+ const child = pending.child;
53
+ if (child?.pid && child.exitCode === null && child.signalCode === null) {
54
+ await new Promise(resolve => { child.once('close', () => resolve()); });
55
+ }
56
+ }
57
+ } }, { priority: args.includes('--version') ? 'version' : 'metadata', signal: options.signal });
46
58
  }
47
59
  //# sourceMappingURL=invocation.js.map
package/dist/launch.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { ExecutableResolutionOptions } from './executables.js';
1
2
  import { inspectAgentExecutableResolution } from './executables.js';
2
3
  import type { RuntimeAgentDef } from './types.js';
3
4
  export type AgentLaunchKind = 'selected' | 'codex-native';
@@ -9,7 +10,7 @@ export type AgentLaunchResolution = ReturnType<typeof inspectAgentExecutableReso
9
10
  };
10
11
  export declare function resolveAgentLaunch({ def }: {
11
12
  def: RuntimeAgentDef;
12
- }, { configuredEnv }?: {
13
+ }, options?: ExecutableResolutionOptions & {
13
14
  configuredEnv?: Record<string, string>;
14
15
  }): AgentLaunchResolution;
15
16
  export declare function applyAgentLaunchEnv({ env, launch }: {
package/dist/launch.js CHANGED
@@ -5,8 +5,8 @@
5
5
  import { accessSync, closeSync, constants, openSync, readdirSync, readSync, realpathSync, statSync } from 'node:fs';
6
6
  import path, { delimiter } from 'node:path';
7
7
  import { inspectAgentExecutableResolution, userToolchainBinDirs } from './executables.js';
8
- export function resolveAgentLaunch({ def }, { configuredEnv = {} } = {}) {
9
- const resolution = inspectAgentExecutableResolution({ def: def }, { configuredEnv: configuredEnv });
8
+ export function resolveAgentLaunch({ def }, options = {}) {
9
+ const resolution = inspectAgentExecutableResolution({ def: def }, options);
10
10
  if (!resolution.selectedPath) {
11
11
  return { ...resolution, launchPath: null, launchKind: 'selected', childPathPrepend: [], diagnostic: null };
12
12
  }
@@ -1,5 +1,5 @@
1
- import { type AgentDiscoveryConfiguration } from './model-discovery.js';
2
- import type { ModelDiscoveryContext, ModelDiscoveryDeps, ModelProbeResult } from './model-discovery-types.js';
1
+ import { type AgentRuntimeState } from './runtime-state.js';
2
+ import type { AgentDiscoveryConfiguration, ModelDiscoveryContext, ModelDiscoveryDeps, ModelProbeResult } from './model-discovery-types.js';
3
3
  export declare function modelProbeTimeoutMs(agentId: string): number;
4
4
  /** Shared CLI-subcommand adapter. Output parsers are pure def-owned functions, not vendor tools. */
5
5
  export declare function probeCliSubcommand(context: ModelDiscoveryContext, config: AgentDiscoveryConfiguration, deps: ModelDiscoveryDeps, signal: AbortSignal): Promise<ModelProbeResult>;
@@ -19,4 +19,8 @@ export declare function probeAgentCatalog(agentId: string, context: ModelDiscove
19
19
  export declare const defaultModelDiscoveryDeps: ModelDiscoveryDeps;
20
20
  /** Pi's native RPC supplies catalog AND current provider/id without making an inference call. */
21
21
  export declare function parsePiRpcMetadata(stdout: string): ModelProbeResult;
22
+ /** Stable dependency identity preserves the existing per-host single-probe budget as well as its cache. */
23
+ export declare function getRuntimeModelDiscoveryDeps({ state }: {
24
+ state: AgentRuntimeState;
25
+ }, _optional?: Record<string, never>): ModelDiscoveryDeps;
22
26
  //# sourceMappingURL=model-discovery-adapters.d.ts.map