@gobing-ai/ts-ai-runner 0.4.67 → 0.4.68

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.
package/README.md CHANGED
@@ -314,12 +314,15 @@ interactive session (ADR-047).
314
314
  3. else → fresh open.
315
315
 
316
316
  **Capability query (R2):** consult `getAgentSessionCapability(agent)` rather than inventing per-agent
317
- argv. It reports `supportsResumeById` and `supportsSessionDir`.
317
+ argv. It reports `supportsResumeById`, `supportsSessionDir`, `supportsPersistentStdin`,
318
+ `supportsStructuredOutput`, the `verifiedAgainst` CLI version the row was probed on, and a `note`
319
+ naming the reason for any `false` (including CLI flags that exist but are not shim-wired yet).
320
+ Uninstalled CLIs carry `verifiedAgainst: 'unverified …'` — never a fabricated version.
318
321
 
319
322
  ```ts
320
323
  import { getAgentSessionCapability, getAgentShim } from '@gobing-ai/ts-ai-runner';
321
324
 
322
- const cap = getAgentSessionCapability('omp'); // { supportsResumeById: true, supportsSessionDir: true }
325
+ const cap = getAgentSessionCapability('omp'); // { supportsResumeById: true, supportsSessionDir: true, supportsPersistentStdin: true, … }
323
326
  const { args } = getAgentShim('omp').getPromptCommand({
324
327
  input: '',
325
328
  sessionDir: '.spur/run/r1/agent-sessions/omp',
@@ -328,21 +331,31 @@ const { args } = getAgentShim('omp').getPromptCommand({
328
331
  // args: ['-p', '', '--session-dir', '.spur/run/r1/...', '-r', 'abc123', '--mode', 'text']
329
332
  ```
330
333
 
331
- **Shim / capability matrix:**
332
-
333
- | Agent | `supportsResumeById` | `supportsSessionDir` | sessionDir flag | sessionId resume flag | Legacy continue |
334
- | ----- | -------------------- | -------------------- | --------------- | --------------------- | --------------- |
335
- | omp | ✓ | ✓ | `--session-dir <dir>` | `-r <id>` | `-c` |
336
- | pi | ✓ | ✓ | `--session-dir <dir>` | `-r <id>` | `-c` |
337
- | claude | ✓ | ✗ (ignored) | — | `--resume <id>` | `--continue` |
338
- | codex | ✗ | ✗ (ignored) | — | degrade → fresh `exec` | `exec resume --last` |
339
- | agy | ✓ | ✗ (ignored) | — | `--conversation <id>` | `--continue` |
340
- | grok | ✓ | ✗ (ignored) | — | `--resume <id>` | `-c` |
341
- | deepseek | ✗ | ✗ (ignored) | — | degrade → fresh headless one-shot | degrade → fresh headless one-shot |
334
+ **Shim / capability matrix** (probed 2026-09-18; `verifiedAgainst` per row in `getAgentSessionCapability`):
335
+
336
+ | Agent | resumeById | sessionDir | sessionId resume flag | persistentStdin | structuredOutput |
337
+ | ----- | ---------- | ---------- | --------------------- | --------------- | ---------------- |
338
+ | omp | ✓ | ✓ | `-r <id>` | ✓ (`--mode rpc`) | ✓ (`--mode json`) |
339
+ | pi | ✓ | ✓ | `-r <id>` | ✓ (`--mode rpc`) | ✓ (`--mode json`) |
340
+ | claude | ✓ | ✗ (ignored) | `--resume <id>` | ✓ (`--input-format stream-json`; not shim-wired) | ✓ (`--output-format json`) |
341
+ | codex | ✓ (0.154.0) | ✗ (ignored) | `exec resume <id> <prompt>` | ✗ | ✓ (`--json`) |
342
+ | agy | ✓ | ✗ (ignored) | `--conversation <id>` | ✗ | ✗ (unverified) |
343
+ | grok | ✓ | ✗ (ignored) | `--resume <id>` | ✗ | ✓ (`--output-format json`) |
344
+ | gemini | ✗ (`-r` = latest/index) | ✗ | degrade → fresh `-p` | ✗ | ✓ (`-o json`) |
345
+ | opencode | ✗ (CLI `-s <id>` unwired) | ✗ | degrade → fresh `run` | ✗ | ✓ (`--format json`) |
346
+ | openclaw | ✗ (CLI `--session-id` unwired) | ✗ | degrade → fresh `agent` | ✗ | ✓ (CLI `--json`; unwired) |
347
+ | hermes | ✗ (unverified) | ✗ | degrade → fresh `chat` | ✗ | ✗ (unverified) |
348
+ | deepseek | ✗ | ✗ | degrade → fresh headless one-shot | ✗ | ✗ |
349
+
350
+ **Claude session discovery:** claude has no session-dir flag and does not accept a caller-chosen
351
+ session id at open; the headless run's session id is discovered from the process output after the
352
+ run (the `discoverSessionId` contract consumers implement). The capability row encodes exactly
353
+ that: `supportsResumeById: true` (resume later via `--resume <id>`), `supportsSessionDir: false`.
342
354
 
343
355
  **Degrade rule:** when `sessionDir`/`sessionId` is set and the agent lacks resume-by-id or a
344
356
  session-store flag, the shim opens **fresh in isolation** (or plain fresh) — never bare global
345
- continue/last. codex, which has no one-shot resume-by-id, always degrades to a fresh `exec`.
357
+ continue/last. codex pins via non-interactive `exec resume <id> <prompt>` (verified 0.154.0); a
358
+ sessionDir-only request still degrades to a fresh `exec` (no session-dir flag).
346
359
 
347
360
  **Durable vs ephemeral (R4):** for omp/pi, setting `sessionDir` implies a durable session — the shim
348
361
  omits `--no-session` so a session file is written and discoverable. The legacy fresh path (no session
@@ -91,6 +91,14 @@ export interface AgentSessionCapability {
91
91
  readonly supportsResumeById: boolean;
92
92
  /** Can isolate session storage into a caller-supplied directory. */
93
93
  readonly supportsSessionDir: boolean;
94
+ /** Multi-turn conversation over stdin in one process (e.g. `--input-format stream-json`, `--mode rpc`). */
95
+ readonly supportsPersistentStdin: boolean;
96
+ /** Structured machine-readable output mode (json / stream-json). */
97
+ readonly supportsStructuredOutput: boolean;
98
+ /** Agent CLI version this row was last verified against; `'unverified …'` when the CLI was absent. */
99
+ readonly verifiedAgainst: string;
100
+ /** Recorded reason for any `false` — a CLI gap, or a CLI-supported flag the shim argv does not wire yet. */
101
+ readonly note?: string;
94
102
  }
95
103
  /** Query a bundled agent's session-affinity capability by canonical name. */
96
104
  export declare function getAgentSessionCapability(agent: AgentName): AgentSessionCapability;
@@ -1 +1 @@
1
- {"version":3,"file":"shims.d.ts","sourceRoot":"","sources":["../../src/agents/shims.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shims.d.ts","sourceRoot":"","sources":["../../src/agents/shims.ts"],"names":[],"mappings":"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"}
@@ -34,12 +34,19 @@ const codexShim = {
34
34
  getVersionCommand: () => ({ command: 'codex', args: ['--version'] }),
35
35
  getPromptCommand: (options) => {
36
36
  const hasSession = options.sessionId !== undefined || options.sessionDir !== undefined;
37
- // Session/pin path degrades to a fresh `exec` — codex has no reliable
38
- // one-shot resume-by-id, so never `exec resume --last` (no global last).
39
37
  if (options.continue === true && !hasSession && options.input !== undefined) {
40
38
  throw new Error('Codex resume mode does not accept a new prompt');
41
39
  }
42
- const args = options.continue === true && !hasSession ? ['exec', 'resume', '--last'] : ['exec', options.input ?? ''];
40
+ // Non-interactive resume-by-id (verified codex-cli 0.154.0): `exec resume
41
+ // <id> <prompt>` continues the recorded thread headlessly — the former
42
+ // interactive-picker-only gap is closed (feature B8 R3). sessionDir-only
43
+ // still degrades to fresh exec (no session-dir flag); bare `continue`
44
+ // stays `resume --last` (input omitted — resume-last rejects a prompt).
45
+ const args = hasSession && options.sessionId !== undefined
46
+ ? ['exec', 'resume', options.sessionId, options.input ?? '']
47
+ : options.continue === true && !hasSession
48
+ ? ['exec', 'resume', '--last']
49
+ : ['exec', options.input ?? ''];
43
50
  if (options.model !== undefined)
44
51
  args.push('-m', options.model);
45
52
  if ((options.mode ?? 'text') === 'json')
@@ -295,25 +302,108 @@ export const AGENT_SHIMS = {
295
302
  * `supportsResumeById` decides resume-by-id vs fresh-degrade, and
296
303
  * `supportsSessionDir` decides whether `sessionDir` is honored (ADR-047 R2).
297
304
  *
298
- * Agents not in the six-agent affinity matrix default to no resume-by-id and no
299
- * session-dir — they get the isolated-fresh / no-resume degrade.
305
+ * Each row records what the agent CLI verifiably supports at the version named
306
+ * in `verifiedAgainst` (probed via `<agent> --help` / documented flags; feature
307
+ * B8). Every `false` carries a `note` naming the gap — an unexplained `false`
308
+ * is a defect — and a CLI-supported flag the shim argv does not wire yet is
309
+ * called out in the note instead of being silently degraded or guessed `true`.
310
+ * CLIs that were absent on the probing host carry `verifiedAgainst:
311
+ * 'unverified …'`, never a fabricated version.
300
312
  */
301
313
  const AGENT_SESSION_CAPABILITY = {
302
- omp: { supportsResumeById: true, supportsSessionDir: true },
303
- pi: { supportsResumeById: true, supportsSessionDir: true },
304
- claude: { supportsResumeById: true, supportsSessionDir: false },
305
- // codex resume is interactive-only (`exec resume` picker); no one-shot
306
- // resume-by-id and no session-dir — degrades to fresh exec.
307
- codex: { supportsResumeById: false, supportsSessionDir: false },
308
- 'antigravity-cli': { supportsResumeById: true, supportsSessionDir: false },
309
- grok: { supportsResumeById: true, supportsSessionDir: false },
310
- // Non-matrix agents: conservative default (isolated-fresh / no-resume).
311
- gemini: { supportsResumeById: false, supportsSessionDir: false },
312
- opencode: { supportsResumeById: false, supportsSessionDir: false },
313
- openclaw: { supportsResumeById: false, supportsSessionDir: false },
314
- hermes: { supportsResumeById: false, supportsSessionDir: false },
314
+ omp: {
315
+ supportsResumeById: true,
316
+ supportsSessionDir: true,
317
+ supportsPersistentStdin: true,
318
+ supportsStructuredOutput: true,
319
+ verifiedAgainst: '18.2.3',
320
+ },
321
+ pi: {
322
+ supportsResumeById: true,
323
+ supportsSessionDir: true,
324
+ supportsPersistentStdin: true,
325
+ supportsStructuredOutput: true,
326
+ verifiedAgainst: '0.85.1',
327
+ },
328
+ claude: {
329
+ supportsResumeById: true,
330
+ supportsSessionDir: false,
331
+ supportsPersistentStdin: true,
332
+ supportsStructuredOutput: true,
333
+ verifiedAgainst: '2.1.274',
334
+ // Verified `--input-format stream-json` (realtime streaming input) at CLI
335
+ // 2.1.274, but the shim has no persistent-stdin dispatch mode yet.
336
+ note: 'no session-dir flag — sessionDir is ignored (best-effort isolate); persistent stdin (--input-format stream-json) is not yet shim-wired',
337
+ },
338
+ codex: {
339
+ supportsResumeById: true,
340
+ supportsSessionDir: false,
341
+ supportsPersistentStdin: false,
342
+ supportsStructuredOutput: true,
343
+ verifiedAgainst: '0.154.0',
344
+ // R3 branch 2: `exec resume <id> <prompt>` is the working non-interactive
345
+ // resume (verified 0.154.0) — wired in getPromptCommand below.
346
+ note: 'no session-dir flag — sessionDir is ignored; `exec` carries one prompt arg (stdin `-` is one-shot), so no multi-turn stdin',
347
+ },
348
+ 'antigravity-cli': {
349
+ supportsResumeById: true,
350
+ supportsSessionDir: false,
351
+ supportsPersistentStdin: false,
352
+ supportsStructuredOutput: false,
353
+ verifiedAgainst: 'unverified (CLI not installed)',
354
+ // resume-by-id is provenance from the shim's own `--conversation <id>`
355
+ // argv, not a guess; agy itself was absent at verification (2026-09-18).
356
+ note: 'resume-by-id per the shim’s documented --conversation <id> argv; agy absent at verification (2026-09-18) — no session-dir, stdin, or structured-output flags documented',
357
+ },
358
+ grok: {
359
+ supportsResumeById: true,
360
+ supportsSessionDir: false,
361
+ supportsPersistentStdin: false,
362
+ supportsStructuredOutput: true,
363
+ verifiedAgainst: '1.0.34',
364
+ note: 'no session-dir flag — sessionDir is ignored (best-effort isolate); no multi-turn stdin input mode',
365
+ },
366
+ gemini: {
367
+ supportsResumeById: false,
368
+ supportsSessionDir: false,
369
+ supportsPersistentStdin: false,
370
+ supportsStructuredOutput: true,
371
+ verifiedAgainst: '0.46.0',
372
+ note: '`-r/--resume` accepts `latest` or a `--list-sessions` index, not a session id; no session-dir flag; no stdin input mode',
373
+ },
374
+ opencode: {
375
+ supportsResumeById: false,
376
+ supportsSessionDir: false,
377
+ supportsPersistentStdin: false,
378
+ supportsStructuredOutput: true,
379
+ verifiedAgainst: '1.17.15',
380
+ note: 'CLI `run -s/--session <id>` supports resume-by-id but the shim argv does not wire it yet (follow-up before declaring true); no session-dir flag; no stdin input mode',
381
+ },
382
+ openclaw: {
383
+ supportsResumeById: false,
384
+ supportsSessionDir: false,
385
+ supportsPersistentStdin: false,
386
+ supportsStructuredOutput: true,
387
+ verifiedAgainst: '2026.6.11',
388
+ note: 'CLI `agent --session-id <id>` and `--json` are not yet shim-wired (follow-up before declaring resume-by-id true); gateway turns have no persistent stdin',
389
+ },
390
+ hermes: {
391
+ supportsResumeById: false,
392
+ supportsSessionDir: false,
393
+ supportsPersistentStdin: false,
394
+ supportsStructuredOutput: false,
395
+ verifiedAgainst: 'unverified (CLI not installed)',
396
+ note: 'CLI absent at verification (2026-09-18) — conservative defaults; `chat --continue` resumes the last session only, no session-dir/stdin/structured-output flags documented',
397
+ },
315
398
  // dsh headless has no resume/session flags — fresh-dispatch degrade.
316
- deepseek: { supportsResumeById: false, supportsSessionDir: false },
399
+ deepseek: {
400
+ supportsResumeById: false,
401
+ supportsSessionDir: false,
402
+ supportsPersistentStdin: false,
403
+ supportsStructuredOutput: false,
404
+ verifiedAgainst: '0.1.5-rc.1',
405
+ note: 'headless app takes only the task positional and -h at 0.1.5-rc.1 — no resume/session/mode flags; fresh-dispatch degrade',
406
+ },
317
407
  };
318
408
  /** Query a bundled agent's session-affinity capability by canonical name. */
319
409
  export function getAgentSessionCapability(agent) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-ai-runner",
3
- "version": "0.4.67",
3
+ "version": "0.4.68",
4
4
  "description": "@gobing-ai/ts-ai-runner — Coding-agent shims, detection, doctor checks, and prompt execution.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -47,12 +47,12 @@
47
47
  "release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-ai-runner-v<version> && git push --tags' && exit 1"
48
48
  },
49
49
  "dependencies": {
50
- "@gobing-ai/ts-infra": "^0.4.67",
51
- "@gobing-ai/ts-runtime": "^0.4.67"
50
+ "@gobing-ai/ts-infra": "^0.4.68",
51
+ "@gobing-ai/ts-runtime": "^0.4.68"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/bun": "1.3.14",
55
- "@gobing-ai/ts-db": "^0.4.67"
55
+ "@gobing-ai/ts-db": "^0.4.68"
56
56
  },
57
57
  "publishConfig": {
58
58
  "access": "public"
@@ -131,13 +131,20 @@ const codexShim: AgentShim = {
131
131
  getVersionCommand: () => ({ command: 'codex', args: ['--version'] }),
132
132
  getPromptCommand: (options) => {
133
133
  const hasSession = options.sessionId !== undefined || options.sessionDir !== undefined;
134
- // Session/pin path degrades to a fresh `exec` — codex has no reliable
135
- // one-shot resume-by-id, so never `exec resume --last` (no global last).
136
134
  if (options.continue === true && !hasSession && options.input !== undefined) {
137
135
  throw new Error('Codex resume mode does not accept a new prompt');
138
136
  }
137
+ // Non-interactive resume-by-id (verified codex-cli 0.154.0): `exec resume
138
+ // <id> <prompt>` continues the recorded thread headlessly — the former
139
+ // interactive-picker-only gap is closed (feature B8 R3). sessionDir-only
140
+ // still degrades to fresh exec (no session-dir flag); bare `continue`
141
+ // stays `resume --last` (input omitted — resume-last rejects a prompt).
139
142
  const args =
140
- options.continue === true && !hasSession ? ['exec', 'resume', '--last'] : ['exec', options.input ?? ''];
143
+ hasSession && options.sessionId !== undefined
144
+ ? ['exec', 'resume', options.sessionId, options.input ?? '']
145
+ : options.continue === true && !hasSession
146
+ ? ['exec', 'resume', '--last']
147
+ : ['exec', options.input ?? ''];
141
148
  if (options.model !== undefined) args.push('-m', options.model);
142
149
  if ((options.mode ?? 'text') === 'json') args.push('--json');
143
150
  return { command: 'codex', args };
@@ -377,6 +384,14 @@ export interface AgentSessionCapability {
377
384
  readonly supportsResumeById: boolean;
378
385
  /** Can isolate session storage into a caller-supplied directory. */
379
386
  readonly supportsSessionDir: boolean;
387
+ /** Multi-turn conversation over stdin in one process (e.g. `--input-format stream-json`, `--mode rpc`). */
388
+ readonly supportsPersistentStdin: boolean;
389
+ /** Structured machine-readable output mode (json / stream-json). */
390
+ readonly supportsStructuredOutput: boolean;
391
+ /** Agent CLI version this row was last verified against; `'unverified …'` when the CLI was absent. */
392
+ readonly verifiedAgainst: string;
393
+ /** Recorded reason for any `false` — a CLI gap, or a CLI-supported flag the shim argv does not wire yet. */
394
+ readonly note?: string;
380
395
  }
381
396
 
382
397
  /**
@@ -385,25 +400,108 @@ export interface AgentSessionCapability {
385
400
  * `supportsResumeById` decides resume-by-id vs fresh-degrade, and
386
401
  * `supportsSessionDir` decides whether `sessionDir` is honored (ADR-047 R2).
387
402
  *
388
- * Agents not in the six-agent affinity matrix default to no resume-by-id and no
389
- * session-dir — they get the isolated-fresh / no-resume degrade.
403
+ * Each row records what the agent CLI verifiably supports at the version named
404
+ * in `verifiedAgainst` (probed via `<agent> --help` / documented flags; feature
405
+ * B8). Every `false` carries a `note` naming the gap — an unexplained `false`
406
+ * is a defect — and a CLI-supported flag the shim argv does not wire yet is
407
+ * called out in the note instead of being silently degraded or guessed `true`.
408
+ * CLIs that were absent on the probing host carry `verifiedAgainst:
409
+ * 'unverified …'`, never a fabricated version.
390
410
  */
391
411
  const AGENT_SESSION_CAPABILITY: Readonly<Record<AgentName, AgentSessionCapability>> = {
392
- omp: { supportsResumeById: true, supportsSessionDir: true },
393
- pi: { supportsResumeById: true, supportsSessionDir: true },
394
- claude: { supportsResumeById: true, supportsSessionDir: false },
395
- // codex resume is interactive-only (`exec resume` picker); no one-shot
396
- // resume-by-id and no session-dir — degrades to fresh exec.
397
- codex: { supportsResumeById: false, supportsSessionDir: false },
398
- 'antigravity-cli': { supportsResumeById: true, supportsSessionDir: false },
399
- grok: { supportsResumeById: true, supportsSessionDir: false },
400
- // Non-matrix agents: conservative default (isolated-fresh / no-resume).
401
- gemini: { supportsResumeById: false, supportsSessionDir: false },
402
- opencode: { supportsResumeById: false, supportsSessionDir: false },
403
- openclaw: { supportsResumeById: false, supportsSessionDir: false },
404
- hermes: { supportsResumeById: false, supportsSessionDir: false },
412
+ omp: {
413
+ supportsResumeById: true,
414
+ supportsSessionDir: true,
415
+ supportsPersistentStdin: true,
416
+ supportsStructuredOutput: true,
417
+ verifiedAgainst: '18.2.3',
418
+ },
419
+ pi: {
420
+ supportsResumeById: true,
421
+ supportsSessionDir: true,
422
+ supportsPersistentStdin: true,
423
+ supportsStructuredOutput: true,
424
+ verifiedAgainst: '0.85.1',
425
+ },
426
+ claude: {
427
+ supportsResumeById: true,
428
+ supportsSessionDir: false,
429
+ supportsPersistentStdin: true,
430
+ supportsStructuredOutput: true,
431
+ verifiedAgainst: '2.1.274',
432
+ // Verified `--input-format stream-json` (realtime streaming input) at CLI
433
+ // 2.1.274, but the shim has no persistent-stdin dispatch mode yet.
434
+ note: 'no session-dir flag — sessionDir is ignored (best-effort isolate); persistent stdin (--input-format stream-json) is not yet shim-wired',
435
+ },
436
+ codex: {
437
+ supportsResumeById: true,
438
+ supportsSessionDir: false,
439
+ supportsPersistentStdin: false,
440
+ supportsStructuredOutput: true,
441
+ verifiedAgainst: '0.154.0',
442
+ // R3 branch 2: `exec resume <id> <prompt>` is the working non-interactive
443
+ // resume (verified 0.154.0) — wired in getPromptCommand below.
444
+ note: 'no session-dir flag — sessionDir is ignored; `exec` carries one prompt arg (stdin `-` is one-shot), so no multi-turn stdin',
445
+ },
446
+ 'antigravity-cli': {
447
+ supportsResumeById: true,
448
+ supportsSessionDir: false,
449
+ supportsPersistentStdin: false,
450
+ supportsStructuredOutput: false,
451
+ verifiedAgainst: 'unverified (CLI not installed)',
452
+ // resume-by-id is provenance from the shim's own `--conversation <id>`
453
+ // argv, not a guess; agy itself was absent at verification (2026-09-18).
454
+ note: 'resume-by-id per the shim’s documented --conversation <id> argv; agy absent at verification (2026-09-18) — no session-dir, stdin, or structured-output flags documented',
455
+ },
456
+ grok: {
457
+ supportsResumeById: true,
458
+ supportsSessionDir: false,
459
+ supportsPersistentStdin: false,
460
+ supportsStructuredOutput: true,
461
+ verifiedAgainst: '1.0.34',
462
+ note: 'no session-dir flag — sessionDir is ignored (best-effort isolate); no multi-turn stdin input mode',
463
+ },
464
+ gemini: {
465
+ supportsResumeById: false,
466
+ supportsSessionDir: false,
467
+ supportsPersistentStdin: false,
468
+ supportsStructuredOutput: true,
469
+ verifiedAgainst: '0.46.0',
470
+ note: '`-r/--resume` accepts `latest` or a `--list-sessions` index, not a session id; no session-dir flag; no stdin input mode',
471
+ },
472
+ opencode: {
473
+ supportsResumeById: false,
474
+ supportsSessionDir: false,
475
+ supportsPersistentStdin: false,
476
+ supportsStructuredOutput: true,
477
+ verifiedAgainst: '1.17.15',
478
+ note: 'CLI `run -s/--session <id>` supports resume-by-id but the shim argv does not wire it yet (follow-up before declaring true); no session-dir flag; no stdin input mode',
479
+ },
480
+ openclaw: {
481
+ supportsResumeById: false,
482
+ supportsSessionDir: false,
483
+ supportsPersistentStdin: false,
484
+ supportsStructuredOutput: true,
485
+ verifiedAgainst: '2026.6.11',
486
+ note: 'CLI `agent --session-id <id>` and `--json` are not yet shim-wired (follow-up before declaring resume-by-id true); gateway turns have no persistent stdin',
487
+ },
488
+ hermes: {
489
+ supportsResumeById: false,
490
+ supportsSessionDir: false,
491
+ supportsPersistentStdin: false,
492
+ supportsStructuredOutput: false,
493
+ verifiedAgainst: 'unverified (CLI not installed)',
494
+ note: 'CLI absent at verification (2026-09-18) — conservative defaults; `chat --continue` resumes the last session only, no session-dir/stdin/structured-output flags documented',
495
+ },
405
496
  // dsh headless has no resume/session flags — fresh-dispatch degrade.
406
- deepseek: { supportsResumeById: false, supportsSessionDir: false },
497
+ deepseek: {
498
+ supportsResumeById: false,
499
+ supportsSessionDir: false,
500
+ supportsPersistentStdin: false,
501
+ supportsStructuredOutput: false,
502
+ verifiedAgainst: '0.1.5-rc.1',
503
+ note: 'headless app takes only the task positional and -h at 0.1.5-rc.1 — no resume/session/mode flags; fresh-dispatch degrade',
504
+ },
407
505
  };
408
506
 
409
507
  /** Query a bundled agent's session-affinity capability by canonical name. */