@jini-ai/agent-runtime 0.4.1 → 0.4.2

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 (127) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/acp-model-probe.d.ts +6 -27
  3. package/dist/acp-model-probe.js +6 -4
  4. package/dist/agent-protocol/acp/models.d.ts +8 -0
  5. package/dist/agent-protocol/acp/models.js +38 -6
  6. package/dist/agent-protocol/acp/session.js +15 -2
  7. package/dist/agent-protocol/pi-rpc/events.js +4 -0
  8. package/dist/amr-model-cache.d.ts +2 -12
  9. package/dist/amr-profile-resolver.d.ts +3 -14
  10. package/dist/anthropic-live-models.d.ts +1 -1
  11. package/dist/anthropic-live-models.js +0 -0
  12. package/dist/artifact-taxonomy.d.ts +3 -17
  13. package/dist/auth.d.ts +0 -6
  14. package/dist/auth.js +1 -30
  15. package/dist/claude-code-models.d.ts +3 -4
  16. package/dist/claude-code-models.js +5 -7
  17. package/dist/claude-stream.d.ts +0 -4
  18. package/dist/claude-stream.js +4 -4
  19. package/dist/copilot-stream.d.ts +0 -5
  20. package/dist/copilot-stream.js +0 -5
  21. package/dist/defs/aider.d.ts +15 -0
  22. package/dist/defs/aider.js +4 -2
  23. package/dist/defs/amp.d.ts +16 -0
  24. package/dist/defs/amp.js +5 -1
  25. package/dist/defs/amr.d.ts +15 -0
  26. package/dist/defs/amr.js +6 -15
  27. package/dist/defs/antigravity.d.ts +16 -1
  28. package/dist/defs/antigravity.js +3 -2
  29. package/dist/defs/auggie.d.ts +15 -0
  30. package/dist/defs/auggie.js +3 -0
  31. package/dist/defs/claude.d.ts +15 -0
  32. package/dist/defs/claude.js +26 -36
  33. package/dist/defs/cline.d.ts +15 -0
  34. package/dist/defs/cline.js +3 -0
  35. package/dist/defs/codebuddy.d.ts +15 -0
  36. package/dist/defs/codebuddy.js +3 -1
  37. package/dist/defs/codex.d.ts +15 -0
  38. package/dist/defs/codex.js +11 -48
  39. package/dist/defs/copilot.d.ts +15 -0
  40. package/dist/defs/copilot.js +5 -14
  41. package/dist/defs/crush.d.ts +15 -0
  42. package/dist/defs/crush.js +3 -0
  43. package/dist/defs/cursor-agent.d.ts +15 -0
  44. package/dist/defs/cursor-agent.js +3 -1
  45. package/dist/defs/deepseek.d.ts +15 -0
  46. package/dist/defs/deepseek.js +3 -1
  47. package/dist/defs/devin.d.ts +15 -0
  48. package/dist/defs/devin.js +3 -1
  49. package/dist/defs/droid.d.ts +15 -0
  50. package/dist/defs/droid.js +3 -0
  51. package/dist/defs/goose.d.ts +15 -0
  52. package/dist/defs/goose.js +3 -0
  53. package/dist/defs/grok-build.d.ts +15 -0
  54. package/dist/defs/grok-build.js +5 -9
  55. package/dist/defs/hermes.d.ts +15 -0
  56. package/dist/defs/hermes.js +3 -1
  57. package/dist/defs/index.d.ts +1 -6
  58. package/dist/defs/index.js +1 -6
  59. package/dist/defs/kilo.d.ts +15 -0
  60. package/dist/defs/kilo.js +3 -1
  61. package/dist/defs/kimi.d.ts +15 -0
  62. package/dist/defs/kimi.js +3 -1
  63. package/dist/defs/kiro.d.ts +15 -0
  64. package/dist/defs/kiro.js +3 -1
  65. package/dist/defs/mimo.d.ts +15 -0
  66. package/dist/defs/mimo.js +3 -1
  67. package/dist/defs/opencode.d.ts +15 -1
  68. package/dist/defs/opencode.js +2 -1
  69. package/dist/defs/pi.d.ts +15 -0
  70. package/dist/defs/pi.js +6 -5
  71. package/dist/defs/qoder.d.ts +15 -0
  72. package/dist/defs/qoder.js +3 -1
  73. package/dist/defs/qwen.d.ts +15 -0
  74. package/dist/defs/qwen.js +3 -1
  75. package/dist/defs/reasonix.d.ts +15 -0
  76. package/dist/defs/reasonix.js +7 -23
  77. package/dist/defs/shared.d.ts +3 -8
  78. package/dist/defs/shared.js +3 -8
  79. package/dist/defs/trae-cli.d.ts +15 -0
  80. package/dist/defs/trae-cli.js +3 -1
  81. package/dist/defs/vibe.d.ts +15 -0
  82. package/dist/defs/vibe.js +3 -1
  83. package/dist/detection.d.ts +23 -4
  84. package/dist/detection.js +41 -59
  85. package/dist/diagnostics.js +1 -9
  86. package/dist/env.js +5 -31
  87. package/dist/executables.js +0 -1
  88. package/dist/index.d.ts +10 -257
  89. package/dist/index.js +20 -278
  90. package/dist/json-event-stream.js +23 -29
  91. package/dist/launch.js +1 -5
  92. package/dist/log-file.js +0 -5
  93. package/dist/metadata.js +0 -4
  94. package/dist/mmd-routes.js +2 -6
  95. package/dist/model-discovery-adapters.d.ts +19 -0
  96. package/dist/model-discovery-adapters.js +387 -0
  97. package/dist/model-discovery-config.d.ts +17 -0
  98. package/dist/model-discovery-config.js +137 -0
  99. package/dist/model-discovery-sdk.d.ts +18 -0
  100. package/dist/model-discovery-sdk.js +56 -0
  101. package/dist/model-discovery-types.d.ts +139 -0
  102. package/dist/model-discovery-types.js +2 -0
  103. package/dist/model-discovery.d.ts +46 -0
  104. package/dist/model-discovery.js +258 -0
  105. package/dist/model-registry.d.ts +5 -36
  106. package/dist/models.d.ts +1 -4
  107. package/dist/models.js +3 -1
  108. package/dist/opencode-log.js +1 -3
  109. package/dist/paths.js +1 -4
  110. package/dist/pi-models.d.ts +1 -7
  111. package/dist/pi-models.js +1 -3
  112. package/dist/prompt-augmenter.d.ts +3 -17
  113. package/dist/prompt-augmenter.js +3 -17
  114. package/dist/providers/model-catalog.d.ts +2 -0
  115. package/dist/providers/model-catalog.js +76 -55
  116. package/dist/providers/sse-decode.d.ts +4 -48
  117. package/dist/providers/sse-decode.js +3 -108
  118. package/dist/qoder-stream.d.ts +0 -3
  119. package/dist/qoder-stream.js +0 -3
  120. package/dist/registry.d.ts +1 -1
  121. package/dist/registry.js +3 -3
  122. package/dist/resolution.js +1 -4
  123. package/dist/role-marker-guard.d.ts +0 -5
  124. package/dist/role-marker-guard.js +0 -5
  125. package/dist/telemetry-sink.d.ts +3 -14
  126. package/dist/types.d.ts +9 -2
  127. package/package.json +27 -6
@@ -321,66 +321,87 @@ export async function listProviderModels(requiredArgs, optionalArgs = {}) {
321
321
  // assigns unconditionally.
322
322
  let result;
323
323
  try {
324
- const response = await fetch(url, {
325
- method: 'GET',
326
- headers: providerModelsHeaders(input.protocol, input.apiKey),
327
- ...input.requestInit,
328
- signal: controller.signal,
329
- redirect: 'error',
330
- });
331
- const latencyMs = Date.now() - start;
332
- const rawText = await response.text();
333
- let data = {};
334
- let parseError;
335
- try {
336
- data = rawText ? JSON.parse(rawText) : {};
337
- }
338
- catch (err) {
339
- // JSON.parse only ever throws a SyntaxError (an Error); the cast
340
- // documents that rather than adding an untestable non-Error branch.
341
- parseError = err.message;
342
- }
343
- if (!response.ok) {
344
- const detail = parseError
345
- ? rawText.trim().slice(0, 240) || parseError
346
- : extractProviderErrorDetail(data, rawText);
347
- return {
348
- ok: false,
349
- kind: statusToKind(response.status),
350
- latencyMs,
351
- status: response.status,
352
- detail: redactSecrets({ input: detail }, { exactSecrets: [input.apiKey] }),
353
- };
354
- }
355
- if (parseError) {
356
- return {
357
- ok: false,
358
- kind: 'unknown',
359
- latencyMs,
360
- status: response.status,
361
- detail: redactSecrets({ input: parseError }, { exactSecrets: [input.apiKey] }),
362
- };
363
- }
364
- // Safe: `url` above only resolves for the five protocols
365
- // `ModelListProtocol` covers (providerModelsUrl throws for every other
366
- // ConnectionTestProtocol, and azure/bedrock already returned above).
367
- const models = extractModels(input.protocol, data);
368
- if (models.length === 0) {
324
+ const collected = [];
325
+ const cursors = new Set();
326
+ for (let page = 0; page < 100; page += 1) {
327
+ const response = await (input.fetchFn ?? fetch)(url, {
328
+ method: 'GET',
329
+ headers: providerModelsHeaders(input.protocol, input.apiKey),
330
+ ...input.requestInit,
331
+ signal: controller.signal,
332
+ redirect: 'error',
333
+ });
334
+ const latencyMs = Date.now() - start;
335
+ const rawText = await response.text();
336
+ let data = {};
337
+ let parseError;
338
+ try {
339
+ data = rawText ? JSON.parse(rawText) : {};
340
+ }
341
+ catch (err) {
342
+ // JSON.parse only ever throws a SyntaxError (an Error); the cast
343
+ // documents that rather than adding an untestable non-Error branch.
344
+ parseError = err.message;
345
+ }
346
+ if (!response.ok) {
347
+ const detail = parseError
348
+ ? rawText.trim().slice(0, 240) || parseError
349
+ : extractProviderErrorDetail(data, rawText);
350
+ return {
351
+ ok: false,
352
+ kind: statusToKind(response.status),
353
+ latencyMs,
354
+ status: response.status,
355
+ detail: redactSecrets({ input: detail }, { exactSecrets: [input.apiKey] }),
356
+ };
357
+ }
358
+ if (parseError) {
359
+ return {
360
+ ok: false,
361
+ kind: 'unknown',
362
+ latencyMs,
363
+ status: response.status,
364
+ detail: redactSecrets({ input: parseError }, { exactSecrets: [input.apiKey] }),
365
+ };
366
+ }
367
+ // Safe: `url` above only resolves for the five protocols
368
+ // `ModelListProtocol` covers (providerModelsUrl throws for every other
369
+ // ConnectionTestProtocol, and azure/bedrock already returned above).
370
+ collected.push(...extractModels(input.protocol, data));
371
+ const envelope = data && typeof data === 'object' ? data : {};
372
+ const cursor = input.protocol === 'google' && typeof envelope.nextPageToken === 'string' ? envelope.nextPageToken
373
+ : envelope.has_more === true && typeof envelope.last_id === 'string' ? envelope.last_id
374
+ : typeof envelope.next_cursor === 'string' ? envelope.next_cursor : undefined;
375
+ if (envelope.has_more === true && !cursor)
376
+ return { ok: false, kind: 'unknown', latencyMs, detail: 'Provider omitted its pagination cursor.' };
377
+ if (cursor) {
378
+ if (cursors.has(cursor))
379
+ return { ok: false, kind: 'unknown', latencyMs, detail: 'Provider repeated a pagination cursor.' };
380
+ cursors.add(cursor);
381
+ const next = new URL(url);
382
+ next.searchParams.set(input.protocol === 'google' ? 'pageToken' : input.protocol === 'anthropic' ? 'after_id' : 'cursor', cursor);
383
+ url = next.toString();
384
+ continue;
385
+ }
386
+ const models = uniqueModels(collected);
387
+ if (models.length === 0) {
388
+ return {
389
+ ok: false,
390
+ kind: 'no_models',
391
+ latencyMs,
392
+ status: response.status,
393
+ detail: 'Provider returned no usable text-generation models.',
394
+ };
395
+ }
369
396
  return {
370
- ok: false,
371
- kind: 'no_models',
397
+ ok: true,
398
+ kind: 'success',
372
399
  latencyMs,
373
400
  status: response.status,
374
- detail: 'Provider returned no usable text-generation models.',
401
+ models,
375
402
  };
376
403
  }
377
- return {
378
- ok: true,
379
- kind: 'success',
380
- latencyMs,
381
- status: response.status,
382
- models,
383
- };
404
+ return { ok: false, kind: 'unknown', latencyMs: Date.now() - start, detail: 'Provider pagination exceeded its safety limit.' };
384
405
  }
385
406
  catch (err) {
386
407
  const latencyMs = Date.now() - start;
@@ -1,49 +1,5 @@
1
- /**
2
- * @module providers/sse-decode
3
- *
4
- * Minimal, tolerant Server-Sent-Events *frame decoder* for an INBOUND
5
- * provider stream — the response body a call to the Anthropic Messages API
6
- * or the OpenAI Chat Completions API sends back when `stream: true` is
7
- * requested. This is deliberately not `@jini-ai/http-kit`'s `sse.ts`: that module
8
- * is the OUTBOUND channel a route uses to push events to a browser client
9
- * (bounded queue, backpressure, `Last-Event-ID` replay). This module has
10
- * none of that — it only turns a raw byte/text stream into `{event, data}`
11
- * frames per the SSE wire format (`text/event-stream`, RFC-ish: fields
12
- * separated by `\n`, records separated by a blank line), so a provider-
13
- * specific turn-runner (`anthropic-messages.ts`, `openai-chat.ts`) can
14
- * `JSON.parse` each frame's `data` field without re-deriving line/record
15
- * framing itself.
16
- *
17
- * New file, not a port — chat.ts's origin used the Node `fetch` global's
18
- * response body directly with ad hoc inline buffering per call site; this
19
- * factors that concern into one shared, independently-tested primitive so
20
- * both provider turn-runners consume the same framing logic (the same
21
- * "shared mechanism, one implementation" discipline `@jini-ai/http-kit`'s
22
- * `sse.ts`/`origin-validation.ts` already apply to their own domains).
23
- */
24
- /** One decoded SSE record. `event` is `null` when the record had no `event:` line (OpenAI's Chat Completions stream never sends one; every `data:` line is implicitly a plain message). `data` is the joined value of every `data:` line in the record (multi-line `data:` fields are joined with `\n`, per the SSE spec). */
25
- export interface DecodedSseEvent {
26
- readonly event: string | null;
27
- readonly data: string;
28
- }
29
- /**
30
- * Decodes an inbound byte/text stream into SSE records. Accepts either
31
- * `Uint8Array` chunks (a real `fetch` response body) or `string` chunks (test
32
- * fixtures, or any caller that already has decoded text) in the same
33
- * iterable — this keeps unit tests free of any need to construct a real web
34
- * `ReadableStream`, matching this package's existing `fetch`-mocking
35
- * convention (`vi.stubGlobal('fetch', ...)`, see `model-catalog.test.ts`).
36
- *
37
- * Tolerant per the SSE spec: accepts both `\n` and `\r\n` line endings,
38
- * ignores comment lines (leading `:`) and unrecognized field names (`id`/
39
- * `retry` are parsed by real SSE clients for reconnect bookkeeping, which is
40
- * meaningless for a single proxied request/response pair, so this decoder
41
- * intentionally drops them), and flushes a trailing unterminated record —
42
- * even one whose very last line has no trailing `\n` at all — once the
43
- * source iterable ends, so a provider closing the connection immediately
44
- * after its last byte never silently loses that last record.
45
- */
46
- export declare function decodeSseStream({ source }: {
47
- source: AsyncIterable<Uint8Array | string>;
48
- }): AsyncGenerator<DecodedSseEvent>;
1
+ /** Compatibility entry. Inbound SSE framing is shared with CLI consumers through core's
2
+ * dependency-free primitives; providers keep their JSON interpretation and select the exported EOF policy. */
3
+ export { decodeSseFrames, decodeSseStream, parseSseRecord } from '@jini-ai/core/primitives';
4
+ export type { DecodedSseEvent, DecodedSseFrame, SseFramingOptions } from '@jini-ai/core/primitives';
49
5
  //# sourceMappingURL=sse-decode.d.ts.map
@@ -1,109 +1,4 @@
1
- /**
2
- * @module providers/sse-decode
3
- *
4
- * Minimal, tolerant Server-Sent-Events *frame decoder* for an INBOUND
5
- * provider stream — the response body a call to the Anthropic Messages API
6
- * or the OpenAI Chat Completions API sends back when `stream: true` is
7
- * requested. This is deliberately not `@jini-ai/http-kit`'s `sse.ts`: that module
8
- * is the OUTBOUND channel a route uses to push events to a browser client
9
- * (bounded queue, backpressure, `Last-Event-ID` replay). This module has
10
- * none of that — it only turns a raw byte/text stream into `{event, data}`
11
- * frames per the SSE wire format (`text/event-stream`, RFC-ish: fields
12
- * separated by `\n`, records separated by a blank line), so a provider-
13
- * specific turn-runner (`anthropic-messages.ts`, `openai-chat.ts`) can
14
- * `JSON.parse` each frame's `data` field without re-deriving line/record
15
- * framing itself.
16
- *
17
- * New file, not a port — chat.ts's origin used the Node `fetch` global's
18
- * response body directly with ad hoc inline buffering per call site; this
19
- * factors that concern into one shared, independently-tested primitive so
20
- * both provider turn-runners consume the same framing logic (the same
21
- * "shared mechanism, one implementation" discipline `@jini-ai/http-kit`'s
22
- * `sse.ts`/`origin-validation.ts` already apply to their own domains).
23
- */
24
- /**
25
- * Decodes an inbound byte/text stream into SSE records. Accepts either
26
- * `Uint8Array` chunks (a real `fetch` response body) or `string` chunks (test
27
- * fixtures, or any caller that already has decoded text) in the same
28
- * iterable — this keeps unit tests free of any need to construct a real web
29
- * `ReadableStream`, matching this package's existing `fetch`-mocking
30
- * convention (`vi.stubGlobal('fetch', ...)`, see `model-catalog.test.ts`).
31
- *
32
- * Tolerant per the SSE spec: accepts both `\n` and `\r\n` line endings,
33
- * ignores comment lines (leading `:`) and unrecognized field names (`id`/
34
- * `retry` are parsed by real SSE clients for reconnect bookkeeping, which is
35
- * meaningless for a single proxied request/response pair, so this decoder
36
- * intentionally drops them), and flushes a trailing unterminated record —
37
- * even one whose very last line has no trailing `\n` at all — once the
38
- * source iterable ends, so a provider closing the connection immediately
39
- * after its last byte never silently loses that last record.
40
- */
41
- export async function* decodeSseStream({ source }) {
42
- const decoder = new TextDecoder();
43
- let buffer = '';
44
- let eventType = null;
45
- let dataLines = [];
46
- let sawAnyField = false;
47
- /** Processes one already-`\r`-stripped line, mutating the in-progress record. Returns the completed record when `line` is the blank line terminating a non-empty record; `null` otherwise (including a blank line with no preceding fields, which starts no record at all). */
48
- function handleLine(line) {
49
- if (line === '') {
50
- if (!sawAnyField)
51
- return null;
52
- const completed = { event: eventType, data: dataLines.join('\n') };
53
- eventType = null;
54
- dataLines = [];
55
- sawAnyField = false;
56
- return completed;
57
- }
58
- if (line.startsWith(':'))
59
- return null; // comment line, per spec
60
- const colonIndex = line.indexOf(':');
61
- const field = colonIndex === -1 ? line : line.slice(0, colonIndex);
62
- let value = colonIndex === -1 ? '' : line.slice(colonIndex + 1);
63
- if (value.startsWith(' '))
64
- value = value.slice(1);
65
- if (field === 'event') {
66
- eventType = value;
67
- sawAnyField = true;
68
- }
69
- else if (field === 'data') {
70
- dataLines.push(value);
71
- sawAnyField = true;
72
- }
73
- // Every other field name (id/retry/anything a future provider adds) is
74
- // intentionally ignored — see module doc.
75
- return null;
76
- }
77
- function* drainCompleteLines() {
78
- let newlineIndex;
79
- while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
80
- let line = buffer.slice(0, newlineIndex);
81
- buffer = buffer.slice(newlineIndex + 1);
82
- if (line.endsWith('\r'))
83
- line = line.slice(0, -1);
84
- const completed = handleLine(line);
85
- if (completed)
86
- yield completed;
87
- }
88
- }
89
- for await (const chunk of source) {
90
- buffer += typeof chunk === 'string' ? chunk : decoder.decode(chunk, { stream: true });
91
- yield* drainCompleteLines();
92
- }
93
- buffer += decoder.decode();
94
- yield* drainCompleteLines();
95
- // Flush whatever is left: either a trailing unterminated line (no final
96
- // `\n` at all — `buffer` still holds it, never having been handed to
97
- // `handleLine`), or a fully-parsed-but-not-blank-line-terminated record
98
- // (`buffer` is empty, but `sawAnyField` is true from lines already
99
- // processed above).
100
- if (buffer.length > 0) {
101
- const completed = handleLine(buffer.endsWith('\r') ? buffer.slice(0, -1) : buffer);
102
- if (completed)
103
- yield completed;
104
- }
105
- if (sawAnyField) {
106
- yield { event: eventType, data: dataLines.join('\n') };
107
- }
108
- }
1
+ /** Compatibility entry. Inbound SSE framing is shared with CLI consumers through core's
2
+ * dependency-free primitives; providers keep their JSON interpretation and select the exported EOF policy. */
3
+ export { decodeSseFrames, decodeSseStream, parseSseRecord } from '@jini-ai/core/primitives';
109
4
  //# sourceMappingURL=sse-decode.js.map
@@ -1,9 +1,6 @@
1
1
  /**
2
2
  * @module qoder-stream
3
3
  *
4
- * Ported verbatim from OD's `apps/daemon/src/runtimes/qoder-stream.ts`
5
- * (only `node:buffer` as a dependency). See `archived provenance ledger`.
6
- *
7
4
  * Parses Qoder CLI's `--output-format stream-json` JSONL stream into the
8
5
  * small event set consumed by the chat UI. Qoder's top-level records are
9
6
  * wrapper objects (`system`, `assistant`, `result`) with adapter-specific
@@ -1,9 +1,6 @@
1
1
  /**
2
2
  * @module qoder-stream
3
3
  *
4
- * Ported verbatim from OD's `apps/daemon/src/runtimes/qoder-stream.ts`
5
- * (only `node:buffer` as a dependency). See `archived provenance ledger`.
6
- *
7
4
  * Parses Qoder CLI's `--output-format stream-json` JSONL stream into the
8
5
  * small event set consumed by the chat UI. Qoder's top-level records are
9
6
  * wrapper objects (`system`, `assistant`, `result`) with adapter-specific
@@ -10,7 +10,7 @@ export declare function getAgentDef({ id }: {
10
10
  * def, per its own `externalMcpInjection` declaration (`types.ts`'s own doc names the six wired
11
11
  * strategies and the defs that leave the field `undefined` because the CLI has no mechanism to
12
12
  * receive one — `aider`, `amp`, `copilot`, `crush`, `cursor-agent`, `deepseek`, `droid`,
13
- * `gemini`, `grok-build`, `pi`, `qoder`, and `qwen` today, each documenting why in its own def file).
13
+ * `grok-build`, `pi`, `qoder`, and `qwen` today, each documenting why in its own def file).
14
14
  *
15
15
  * This is the single seam a tool-availability UI (e.g. the chat runtime picker's "No tools" badge)
16
16
  * should read instead of hardcoding a runtime-id list: that list goes stale the moment a def gains
package/dist/registry.js CHANGED
@@ -17,7 +17,7 @@
17
17
  * can reintroduce a de-branded, sandbox-free local-profile loader as an
18
18
  * injected port if a consumer needs it. See `archived provenance ledger`.
19
19
  */
20
- import { aiderAgentDef, ampAgentDef, amrAgentDef, antigravityAgentDef, auggieAgentDef, claudeAgentDef, clineAgentDef, codebuddyAgentDef, codexAgentDef, copilotAgentDef, crushAgentDef, cursorAgentDef, deepseekAgentDef, devinAgentDef, droidAgentDef, geminiAgentDef, gooseAgentDef, grokBuildAgentDef, hermesAgentDef, kiloAgentDef, kimiAgentDef, kiroAgentDef, mimoAgentDef, opencodeAgentDef, piAgentDef, qoderAgentDef, qwenAgentDef, reasonixAgentDef, traeCliAgentDef, vibeAgentDef, } from './defs/index.js';
20
+ import { aiderAgentDef, ampAgentDef, amrAgentDef, antigravityAgentDef, auggieAgentDef, claudeAgentDef, clineAgentDef, codebuddyAgentDef, codexAgentDef, copilotAgentDef, crushAgentDef, cursorAgentDef, deepseekAgentDef, devinAgentDef, droidAgentDef, gooseAgentDef, grokBuildAgentDef, hermesAgentDef, kiloAgentDef, kimiAgentDef, kiroAgentDef, mimoAgentDef, opencodeAgentDef, piAgentDef, qoderAgentDef, qwenAgentDef, reasonixAgentDef, traeCliAgentDef, vibeAgentDef, } from './defs/index.js';
21
21
  export const BASE_AGENT_DEFS = [
22
22
  amrAgentDef,
23
23
  claudeAgentDef,
@@ -30,7 +30,7 @@ export const BASE_AGENT_DEFS = [
30
30
  kimiAgentDef,
31
31
  cursorAgentDef,
32
32
  qwenAgentDef,
33
- geminiAgentDef,
33
+ // Gemini CLI retired 2026-10-06; Antigravity (agy) is the Google CLI path.
34
34
  qoderAgentDef,
35
35
  copilotAgentDef,
36
36
  ampAgentDef,
@@ -67,7 +67,7 @@ export function getAgentDef({ id }) {
67
67
  * def, per its own `externalMcpInjection` declaration (`types.ts`'s own doc names the six wired
68
68
  * strategies and the defs that leave the field `undefined` because the CLI has no mechanism to
69
69
  * receive one — `aider`, `amp`, `copilot`, `crush`, `cursor-agent`, `deepseek`, `droid`,
70
- * `gemini`, `grok-build`, `pi`, `qoder`, and `qwen` today, each documenting why in its own def file).
70
+ * `grok-build`, `pi`, `qoder`, and `qwen` today, each documenting why in its own def file).
71
71
  *
72
72
  * This is the single seam a tool-availability UI (e.g. the chat runtime picker's "No tools" badge)
73
73
  * should read instead of hardcoding a runtime-id list: that list goes stale the moment a def gains
@@ -1,9 +1,6 @@
1
1
  /**
2
2
  * @module resolution
3
- *
4
- * Resolve the absolute path of an agent's binary on the current PATH, by
5
- * agent id, from the registry. Ported verbatim from OD's
6
- * `apps/daemon/src/runtimes/resolution.ts`.
3
+ * Resolve an agent's absolute executable path from the current PATH and registry.
7
4
  */
8
5
  import { getAgentDef } from './registry.js';
9
6
  import { resolveAgentExecutable } from './executables.js';
@@ -9,11 +9,6 @@
9
9
  * stream handlers that can track message boundaries (Claude, Copilot,
10
10
  * Qoder, OpenCode/Codex, Pi, ACP). Returns `{ feedText, contaminated,
11
11
  * warningEvent }`.
12
- *
13
- * Ported verbatim from OD's `apps/daemon/src/role-marker-guard.ts` (a
14
- * top-level daemon file, not under `runtimes/`, but consumed by
15
- * `claude-stream.ts` and self-contained with no product coupling — see
16
- * `archived provenance ledger`).
17
12
  */
18
13
  export declare const FABRICATED_ROLE_MARKER_RE: RegExp;
19
14
  export type RoleMarkerWarningEvent = {
@@ -9,11 +9,6 @@
9
9
  * stream handlers that can track message boundaries (Claude, Copilot,
10
10
  * Qoder, OpenCode/Codex, Pi, ACP). Returns `{ feedText, contaminated,
11
11
  * warningEvent }`.
12
- *
13
- * Ported verbatim from OD's `apps/daemon/src/role-marker-guard.ts` (a
14
- * top-level daemon file, not under `runtimes/`, but consumed by
15
- * `claude-stream.ts` and self-contained with no product coupling — see
16
- * `archived provenance ledger`).
17
12
  */
18
13
  // Regex matching fabricated role-marker lines injected by the model into
19
14
  // its own output. Anchored to start-of-line via (?:^|\n) so we don't
@@ -1,19 +1,8 @@
1
1
  /**
2
2
  * @module telemetry-sink
3
- *
4
- * Port replacing the OD analytics naming baked into OD's
5
- * `runtimes/runs/run-artifacts.ts` (`run_finished.artifact_count`,
6
- * `runAskedUserQuestion` → `run_finished.asked_user_question`,
7
- * `deriveActivationMilestones`'s PostHog `$set_once` person-property
8
- * scheme) and `run-lifecycle-analytics.ts` (not present as a separate file
9
- * on this branch — its concerns are folded into `run-artifacts.ts` here).
10
- * None of that OD-schema logic is ported.
11
- *
12
- * Per r1b §1b: the engine emits a generic run-lifecycle event stream; the
13
- * host maps it to its own analytics schema. `data` is intentionally
14
- * `Record<string, unknown>` — artifactCount, askedUserQuestion, stopReason,
15
- * etc. are opaque to the engine, populated by whatever
16
- * `ArtifactTaxonomy`/`PromptAugmenter` implementation the host wires in.
3
+ * The engine emits generic lifecycle events; hosts map them to their own analytics schema.
4
+ * data is opaque Record<string, unknown>: metrics supplied by ArtifactTaxonomy/PromptAugmenter
5
+ * must not install a product's analytics identity or event schema in the engine.
17
6
  */
18
7
  export interface RunLifecycleEvent {
19
8
  type: 'run_started' | 'run_finished' | 'run_failed' | 'tool_use' | 'artifact_written';
package/dist/types.d.ts CHANGED
@@ -11,12 +11,15 @@
11
11
  * Ported from OD's `apps/daemon/src/runtimes/types.ts`. Product-neutral
12
12
  * as found — see `archived provenance ledger` for the full provenance table.
13
13
  */
14
+ import type { AgentModelDiscoveryPort, ModelCatalogSnapshot, DefaultModelResolution, ModelIdentityKind } from './model-discovery-types.js';
14
15
  import type { ExecFileOptions } from 'node:child_process';
15
16
  import type { AgentDiagnostic } from '@jini-ai/protocol';
16
17
  export type RuntimeEnv = NodeJS.ProcessEnv | Record<string, string>;
17
18
  export type RuntimeModelOption = {
18
19
  id: string;
19
20
  label: string;
21
+ identityKind?: ModelIdentityKind;
22
+ resolvedId?: string;
20
23
  /**
21
24
  * The reasoning-effort levels THIS model supports, when the runtime's own catalog reports them
22
25
  * per model rather than per agent.
@@ -258,12 +261,14 @@ export type RuntimeLockHold = {
258
261
  export type RuntimeLock = {
259
262
  readonly acquire: (context: RuntimeLockAcquireContext) => Promise<RuntimeLockHold>;
260
263
  };
261
- export type RuntimeAgentDef = {
264
+ export type RuntimeAgentDef = Partial<AgentModelDiscoveryPort> & {
262
265
  id: string;
263
266
  name: string;
264
267
  bin: string;
265
268
  versionArgs: string[];
266
269
  fallbackModels: RuntimeModelOption[];
270
+ /** False when the native CLI exposes only routing modes and cannot pin a concrete ID. */
271
+ supportsConcreteModelSelection?: boolean;
267
272
  /**
268
273
  * `YYYY-MM-DD` — when this def's hardcoded `fallbackModels` list was last checked against the
269
274
  * vendor's own current list.
@@ -397,9 +402,11 @@ export type RuntimeAgentDef = {
397
402
  stdoutPolicy?: RuntimeStdoutPolicy;
398
403
  runtimeLock?: RuntimeLock;
399
404
  };
400
- export type DetectedAgent = Omit<RuntimeAgentDef, 'buildArgs' | 'listModels' | 'fetchModels' | 'deriveReasoningOptions' | 'fallbackModels' | 'fallbackModelsAssertedAt' | 'helpArgs' | 'capabilityFlags' | 'fallbackBins' | 'versionProbeTimeoutMs' | 'maxPromptArgBytes' | 'env' | 'inactivityTimeoutMs' | 'authProbe' | 'needsAgentLogFile' | 'stdoutPolicy' | 'runtimeLock'> & {
405
+ export type DetectedAgent = Omit<RuntimeAgentDef, 'discoverModels' | 'resolveDefaultModel' | 'buildArgs' | 'listModels' | 'fetchModels' | 'deriveReasoningOptions' | 'fallbackModels' | 'fallbackModelsAssertedAt' | 'helpArgs' | 'capabilityFlags' | 'fallbackBins' | 'versionProbeTimeoutMs' | 'maxPromptArgBytes' | 'env' | 'inactivityTimeoutMs' | 'authProbe' | 'needsAgentLogFile' | 'stdoutPolicy' | 'runtimeLock'> & {
401
406
  models: RuntimeModelOption[];
402
407
  modelsSource: RuntimeModelSource;
408
+ modelCatalog?: ModelCatalogSnapshot;
409
+ defaultModelResolution?: DefaultModelResolution;
403
410
  available: boolean;
404
411
  authStatus?: 'ok' | 'missing' | 'unknown';
405
412
  authMessage?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jini-ai/agent-runtime",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Coding-agent runtime adapters: runtime registry, detection, launch, stream parsers, and the ACP/pi-rpc subprocess transport.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -74,14 +74,35 @@
74
74
  "access": "public",
75
75
  "registry": "https://registry.npmjs.org"
76
76
  },
77
- "dependencies": {
77
+ "devDependencies": {
78
+ "@vitest/coverage-v8": "^2.1.9",
78
79
  "@jini-ai/core": "^0.4.0",
79
- "@jini-ai/protocol": "^0.4.1",
80
+ "@jini-ai/oauth": "^0.2.1",
81
+ "@jini-ai/platform": "^0.5.2",
82
+ "@jini-ai/protocol": "^0.4.2"
83
+ },
84
+ "dependencies": {
85
+ "@jini-ai/core": "^0.4.2"
86
+ },
87
+ "peerDependencies": {
88
+ "@github/copilot-sdk": "*",
89
+ "@jini-ai/oauth": "^0.2.0",
80
90
  "@jini-ai/platform": "^0.5.1",
81
- "@jini-ai/oauth": "^0.2.0"
91
+ "@jini-ai/protocol": "^0.4.1"
82
92
  },
83
- "devDependencies": {
84
- "@vitest/coverage-v8": "^2.1.9"
93
+ "peerDependenciesMeta": {
94
+ "@github/copilot-sdk": {
95
+ "optional": true
96
+ },
97
+ "@jini-ai/oauth": {
98
+ "optional": true
99
+ },
100
+ "@jini-ai/platform": {
101
+ "optional": true
102
+ },
103
+ "@jini-ai/protocol": {
104
+ "optional": true
105
+ }
85
106
  },
86
107
  "scripts": {
87
108
  "build": "tsc -p tsconfig.json",