@deepstrike/sdk 0.2.10 → 0.2.12

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 (41) hide show
  1. package/README.md +38 -0
  2. package/dist/harness/harness.d.ts +5 -0
  3. package/dist/harness/harness.js +12 -1
  4. package/dist/index.d.ts +7 -4
  5. package/dist/index.js +6 -4
  6. package/dist/providers/anthropic.d.ts +7 -0
  7. package/dist/providers/anthropic.js +129 -10
  8. package/dist/providers/base.d.ts +38 -0
  9. package/dist/providers/base.js +0 -0
  10. package/dist/providers/catalog.js +20 -8
  11. package/dist/providers/deepseek.d.ts +12 -0
  12. package/dist/providers/deepseek.js +23 -4
  13. package/dist/providers/gemini.js +23 -4
  14. package/dist/providers/glm.d.ts +12 -0
  15. package/dist/providers/glm.js +19 -2
  16. package/dist/providers/kimi.d.ts +12 -0
  17. package/dist/providers/kimi.js +19 -2
  18. package/dist/providers/minimax.js +4 -2
  19. package/dist/providers/ollama.js +2 -2
  20. package/dist/providers/openai-chat.js +3 -2
  21. package/dist/providers/openai-responses.js +13 -0
  22. package/dist/providers/openai.d.ts +7 -0
  23. package/dist/providers/openai.js +15 -2
  24. package/dist/providers/profiles.d.ts +52 -28
  25. package/dist/providers/profiles.js +52 -28
  26. package/dist/providers/qwen.d.ts +12 -0
  27. package/dist/providers/qwen.js +23 -4
  28. package/dist/runtime/output-schema.d.ts +14 -0
  29. package/dist/runtime/output-schema.js +113 -0
  30. package/dist/runtime/reducers.d.ts +15 -0
  31. package/dist/runtime/reducers.js +57 -0
  32. package/dist/runtime/runner.d.ts +24 -1
  33. package/dist/runtime/runner.js +185 -15
  34. package/dist/runtime/session-log.d.ts +9 -1
  35. package/dist/runtime/session-repair.d.ts +14 -0
  36. package/dist/runtime/session-repair.js +15 -0
  37. package/dist/runtime/sub-agent-orchestrator.js +14 -1
  38. package/dist/types/agent.d.ts +43 -1
  39. package/dist/types/agent.js +105 -18
  40. package/dist/types.d.ts +42 -1
  41. package/package.json +2 -2
@@ -35,18 +35,36 @@ export const endpointProfiles = {
35
35
  protocol: "openai-chat",
36
36
  baseURL: "https://api.minimaxi.com/v1",
37
37
  },
38
+ "deepseek.anthropic": {
39
+ id: "deepseek.anthropic",
40
+ providerId: "deepseek",
41
+ protocol: "anthropic-messages",
42
+ baseURL: "https://api.deepseek.com/anthropic",
43
+ },
38
44
  "deepseek.openai": {
39
45
  id: "deepseek.openai",
40
46
  providerId: "deepseek",
41
47
  protocol: "openai-chat",
42
48
  baseURL: "https://api.deepseek.com",
43
49
  },
50
+ "kimi.anthropic": {
51
+ id: "kimi.anthropic",
52
+ providerId: "kimi",
53
+ protocol: "anthropic-messages",
54
+ baseURL: "https://api.moonshot.ai/anthropic",
55
+ },
44
56
  "kimi.openai": {
45
57
  id: "kimi.openai",
46
58
  providerId: "kimi",
47
59
  protocol: "openai-chat",
48
60
  baseURL: "https://api.moonshot.cn/v1",
49
61
  },
62
+ "qwen.anthropic": {
63
+ id: "qwen.anthropic",
64
+ providerId: "qwen",
65
+ protocol: "anthropic-messages",
66
+ baseURL: "https://dashscope-intl.aliyuncs.com/apps/anthropic",
67
+ },
50
68
  "qwen.dashscope": {
51
69
  id: "qwen.dashscope",
52
70
  providerId: "qwen",
@@ -77,6 +95,12 @@ export const endpointProfiles = {
77
95
  protocol: "gemini-embeddings",
78
96
  baseURL: "https://generativelanguage.googleapis.com",
79
97
  },
98
+ "glm.anthropic": {
99
+ id: "glm.anthropic",
100
+ providerId: "glm",
101
+ protocol: "anthropic-messages",
102
+ baseURL: "https://api.z.ai/api/anthropic",
103
+ },
80
104
  "glm.openai": {
81
105
  id: "glm.openai",
82
106
  providerId: "glm",
@@ -375,28 +399,28 @@ export const modelProfiles = {
375
399
  },
376
400
  // ── DeepSeek ───────────────────────────────────────────────────────────────
377
401
  "deepseek/deepseek-chat": {
378
- id: "deepseek/deepseek-chat", providerId: "deepseek", defaultEndpointId: "deepseek.openai",
402
+ id: "deepseek/deepseek-chat", providerId: "deepseek", defaultEndpointId: "deepseek.anthropic",
379
403
  contextWindow: 64_000,
380
404
  modalities: { input: ["text"], output: ["text"] },
381
405
  tools: { supported: true }, reasoning: { supported: false, preserveAcrossToolTurns: false },
382
406
  policy: { maxTurns: 25 },
383
407
  },
384
408
  "deepseek/deepseek-reasoner": {
385
- id: "deepseek/deepseek-reasoner", providerId: "deepseek", defaultEndpointId: "deepseek.openai",
409
+ id: "deepseek/deepseek-reasoner", providerId: "deepseek", defaultEndpointId: "deepseek.anthropic",
386
410
  contextWindow: 64_000,
387
411
  modalities: { input: ["text"], output: ["text"] },
388
412
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: true },
389
413
  policy: { maxTurns: 50 },
390
414
  },
391
415
  "deepseek/deepseek-v4-flash": {
392
- id: "deepseek/deepseek-v4-flash", providerId: "deepseek", defaultEndpointId: "deepseek.openai",
416
+ id: "deepseek/deepseek-v4-flash", providerId: "deepseek", defaultEndpointId: "deepseek.anthropic",
393
417
  contextWindow: 1_000_000,
394
418
  modalities: { input: ["text"], output: ["text"] },
395
419
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: true },
396
420
  policy: { maxTurns: 20 },
397
421
  },
398
422
  "deepseek/deepseek-v4-pro": {
399
- id: "deepseek/deepseek-v4-pro", providerId: "deepseek", defaultEndpointId: "deepseek.openai",
423
+ id: "deepseek/deepseek-v4-pro", providerId: "deepseek", defaultEndpointId: "deepseek.anthropic",
400
424
  contextWindow: 1_000_000,
401
425
  modalities: { input: ["text"], output: ["text"] },
402
426
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: true },
@@ -404,48 +428,48 @@ export const modelProfiles = {
404
428
  },
405
429
  // ── Kimi ───────────────────────────────────────────────────────────────────
406
430
  "kimi/moonshot-v1-8k": {
407
- id: "kimi/moonshot-v1-8k", providerId: "kimi", defaultEndpointId: "kimi.openai",
431
+ id: "kimi/moonshot-v1-8k", providerId: "kimi", defaultEndpointId: "kimi.anthropic",
408
432
  contextWindow: 8_000,
409
433
  modalities: { input: ["text"], output: ["text"] },
410
434
  tools: { supported: true }, reasoning: { supported: false, preserveAcrossToolTurns: false },
411
435
  policy: { maxTurns: 15 },
412
436
  },
413
437
  "kimi/moonshot-v1-32k": {
414
- id: "kimi/moonshot-v1-32k", providerId: "kimi", defaultEndpointId: "kimi.openai",
438
+ id: "kimi/moonshot-v1-32k", providerId: "kimi", defaultEndpointId: "kimi.anthropic",
415
439
  contextWindow: 32_000,
416
440
  modalities: { input: ["text"], output: ["text"] },
417
441
  tools: { supported: true }, reasoning: { supported: false, preserveAcrossToolTurns: false },
418
442
  policy: { maxTurns: 20 },
419
443
  },
420
444
  "kimi/moonshot-v1-128k": {
421
- id: "kimi/moonshot-v1-128k", providerId: "kimi", defaultEndpointId: "kimi.openai",
445
+ id: "kimi/moonshot-v1-128k", providerId: "kimi", defaultEndpointId: "kimi.anthropic",
422
446
  contextWindow: 128_000,
423
447
  modalities: { input: ["text", "image"], output: ["text"] },
424
448
  tools: { supported: true }, reasoning: { supported: false, preserveAcrossToolTurns: false },
425
449
  policy: { maxTurns: 30 },
426
450
  },
427
451
  "kimi/kimi-k2.5": {
428
- id: "kimi/kimi-k2.5", providerId: "kimi", defaultEndpointId: "kimi.openai",
452
+ id: "kimi/kimi-k2.5", providerId: "kimi", defaultEndpointId: "kimi.anthropic",
429
453
  contextWindow: 256_000,
430
454
  modalities: { input: ["text", "image"], output: ["text"] },
431
455
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
432
456
  policy: { maxTurns: 30 },
433
457
  },
434
458
  "kimi/kimi-k2.6": {
435
- id: "kimi/kimi-k2.6", providerId: "kimi", defaultEndpointId: "kimi.openai",
459
+ id: "kimi/kimi-k2.6", providerId: "kimi", defaultEndpointId: "kimi.anthropic",
436
460
  modalities: { input: ["text"], output: ["text"] },
437
461
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
438
462
  policy: { maxTurns: 35 },
439
463
  },
440
464
  "kimi/kimi-k2-thinking": {
441
- id: "kimi/kimi-k2-thinking", providerId: "kimi", defaultEndpointId: "kimi.openai",
465
+ id: "kimi/kimi-k2-thinking", providerId: "kimi", defaultEndpointId: "kimi.anthropic",
442
466
  contextWindow: 256_000,
443
467
  modalities: { input: ["text"], output: ["text"] },
444
468
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: true },
445
469
  policy: { maxTurns: 50 },
446
470
  },
447
471
  "kimi/kimi-k2-thinking-turbo": {
448
- id: "kimi/kimi-k2-thinking-turbo", providerId: "kimi", defaultEndpointId: "kimi.openai",
472
+ id: "kimi/kimi-k2-thinking-turbo", providerId: "kimi", defaultEndpointId: "kimi.anthropic",
449
473
  contextWindow: 256_000,
450
474
  modalities: { input: ["text"], output: ["text"] },
451
475
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: true },
@@ -453,91 +477,91 @@ export const modelProfiles = {
453
477
  },
454
478
  // ── Qwen ───────────────────────────────────────────────────────────────────
455
479
  "qwen/qwen3.7-max-preview": {
456
- id: "qwen/qwen3.7-max-preview", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
480
+ id: "qwen/qwen3.7-max-preview", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
457
481
  contextWindow: 256_000,
458
482
  modalities: { input: ["text"], output: ["text"] },
459
483
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
460
484
  policy: { maxTurns: 45 },
461
485
  },
462
486
  "qwen/qwen3.7-plus-preview": {
463
- id: "qwen/qwen3.7-plus-preview", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
487
+ id: "qwen/qwen3.7-plus-preview", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
464
488
  contextWindow: 1_000_000,
465
489
  modalities: { input: ["text", "image"], output: ["text"] },
466
490
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
467
491
  policy: { maxTurns: 40 },
468
492
  },
469
493
  "qwen/qwen3.6-max-preview": {
470
- id: "qwen/qwen3.6-max-preview", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
494
+ id: "qwen/qwen3.6-max-preview", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
471
495
  contextWindow: 256_000,
472
496
  modalities: { input: ["text"], output: ["text"] },
473
497
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
474
498
  policy: { maxTurns: 40 },
475
499
  },
476
500
  "qwen/qwen3.6-plus": {
477
- id: "qwen/qwen3.6-plus", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
501
+ id: "qwen/qwen3.6-plus", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
478
502
  contextWindow: 1_000_000,
479
503
  modalities: { input: ["text", "image"], output: ["text"] },
480
504
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
481
505
  policy: { maxTurns: 35 },
482
506
  },
483
507
  "qwen/qwen3.6-flash": {
484
- id: "qwen/qwen3.6-flash", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
508
+ id: "qwen/qwen3.6-flash", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
485
509
  contextWindow: 1_000_000,
486
510
  modalities: { input: ["text", "image"], output: ["text"] },
487
511
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
488
512
  policy: { maxTurns: 20 },
489
513
  },
490
514
  "qwen/qwen3.6-35b-a3b": {
491
- id: "qwen/qwen3.6-35b-a3b", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
515
+ id: "qwen/qwen3.6-35b-a3b", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
492
516
  contextWindow: 256_000,
493
517
  modalities: { input: ["text", "image"], output: ["text"] },
494
518
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
495
519
  policy: { maxTurns: 25 },
496
520
  },
497
521
  "qwen/qwen3.6-27b": {
498
- id: "qwen/qwen3.6-27b", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
522
+ id: "qwen/qwen3.6-27b", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
499
523
  contextWindow: 256_000,
500
524
  modalities: { input: ["text", "image"], output: ["text"] },
501
525
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
502
526
  policy: { maxTurns: 25 },
503
527
  },
504
528
  "qwen/qwen3.5-plus": {
505
- id: "qwen/qwen3.5-plus", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
529
+ id: "qwen/qwen3.5-plus", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
506
530
  contextWindow: 1_000_000,
507
531
  modalities: { input: ["text", "image"], output: ["text"] },
508
532
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
509
533
  policy: { maxTurns: 35 },
510
534
  },
511
535
  "qwen/qwen3.5-flash": {
512
- id: "qwen/qwen3.5-flash", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
536
+ id: "qwen/qwen3.5-flash", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
513
537
  contextWindow: 1_000_000,
514
538
  modalities: { input: ["text", "image"], output: ["text"] },
515
539
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
516
540
  policy: { maxTurns: 20 },
517
541
  },
518
542
  "qwen/qwen3.5-397b-a17b": {
519
- id: "qwen/qwen3.5-397b-a17b", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
543
+ id: "qwen/qwen3.5-397b-a17b", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
520
544
  contextWindow: 256_000,
521
545
  modalities: { input: ["text", "image"], output: ["text"] },
522
546
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
523
547
  policy: { maxTurns: 35 },
524
548
  },
525
549
  "qwen/qwen3.5-122b-a10b": {
526
- id: "qwen/qwen3.5-122b-a10b", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
550
+ id: "qwen/qwen3.5-122b-a10b", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
527
551
  contextWindow: 256_000,
528
552
  modalities: { input: ["text", "image"], output: ["text"] },
529
553
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
530
554
  policy: { maxTurns: 25 },
531
555
  },
532
556
  "qwen/qwen3.5-35b-a3b": {
533
- id: "qwen/qwen3.5-35b-a3b", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
557
+ id: "qwen/qwen3.5-35b-a3b", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
534
558
  contextWindow: 256_000,
535
559
  modalities: { input: ["text", "image"], output: ["text"] },
536
560
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
537
561
  policy: { maxTurns: 20 },
538
562
  },
539
563
  "qwen/qwen3.5-27b": {
540
- id: "qwen/qwen3.5-27b", providerId: "qwen", defaultEndpointId: "qwen.dashscope",
564
+ id: "qwen/qwen3.5-27b", providerId: "qwen", defaultEndpointId: "qwen.anthropic",
541
565
  contextWindow: 256_000,
542
566
  modalities: { input: ["text", "image"], output: ["text"] },
543
567
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: false },
@@ -645,28 +669,28 @@ export const modelProfiles = {
645
669
  },
646
670
  // ── GLM ────────────────────────────────────────────────────────────────────
647
671
  "glm/glm-5.1": {
648
- id: "glm/glm-5.1", providerId: "glm", defaultEndpointId: "glm.openai",
672
+ id: "glm/glm-5.1", providerId: "glm", defaultEndpointId: "glm.anthropic",
649
673
  contextWindow: 200_000,
650
674
  modalities: { input: ["text"], output: ["text"] },
651
675
  tools: { supported: true }, reasoning: { supported: true, preserveAcrossToolTurns: true },
652
676
  policy: { maxTurns: 50 },
653
677
  },
654
678
  "glm/glm-4-plus": {
655
- id: "glm/glm-4-plus", providerId: "glm", defaultEndpointId: "glm.openai",
679
+ id: "glm/glm-4-plus", providerId: "glm", defaultEndpointId: "glm.anthropic",
656
680
  contextWindow: 128_000,
657
681
  modalities: { input: ["text", "image"], output: ["text"] },
658
682
  tools: { supported: true }, reasoning: { supported: false, preserveAcrossToolTurns: false },
659
683
  policy: { maxTurns: 35 },
660
684
  },
661
685
  "glm/glm-4-flash": {
662
- id: "glm/glm-4-flash", providerId: "glm", defaultEndpointId: "glm.openai",
686
+ id: "glm/glm-4-flash", providerId: "glm", defaultEndpointId: "glm.anthropic",
663
687
  contextWindow: 128_000,
664
688
  modalities: { input: ["text"], output: ["text"] },
665
689
  tools: { supported: true }, reasoning: { supported: false, preserveAcrossToolTurns: false },
666
690
  policy: { maxTurns: 15 },
667
691
  },
668
692
  "glm/glm-4-air": {
669
- id: "glm/glm-4-air", providerId: "glm", defaultEndpointId: "glm.openai",
693
+ id: "glm/glm-4-air", providerId: "glm", defaultEndpointId: "glm.anthropic",
670
694
  contextWindow: 128_000,
671
695
  modalities: { input: ["text"], output: ["text"] },
672
696
  tools: { supported: true }, reasoning: { supported: false, preserveAcrossToolTurns: false },
@@ -1,7 +1,19 @@
1
1
  import OpenAI from "openai";
2
2
  import type { LLMProvider, Message, ProviderDescriptor, RenderedContext, StreamEvent, ToolSchema, RuntimePolicy, ProviderReplay } from "../types.js";
3
+ import { AnthropicProvider } from "./anthropic.js";
3
4
  import { CircuitBreaker } from "./base.js";
4
5
  import { OpenAIChatAdapter } from "./openai-chat.js";
6
+ /**
7
+ * Qwen over its Anthropic-compatible endpoint.
8
+ */
9
+ export declare class QwenAnthropicProvider extends AnthropicProvider {
10
+ constructor(apiKey: string, model?: string, retry?: {
11
+ maxRetries: number;
12
+ baseDelay: number;
13
+ }, baseURL?: string);
14
+ protected providerName(): string;
15
+ runtimePolicy(): RuntimePolicy;
16
+ }
5
17
  export declare class QwenProvider implements LLMProvider {
6
18
  protected readonly model: string;
7
19
  protected client: OpenAI;
@@ -1,9 +1,9 @@
1
1
  import OpenAI from "openai";
2
2
  import { withServerRuntimeGuard } from "../runtime/server.js";
3
- import { CircuitBreaker, omitExtensionKeys } from "./base.js";
3
+ import { AnthropicProvider } from "./anthropic.js";
4
+ import { CircuitBreaker, omitExtensionKeys, openAICachedPromptTokens } from "./base.js";
4
5
  import { OpenAIChatAdapter } from "./openai-chat.js";
5
6
  import { endpointProfiles } from "./profiles.js";
6
- const QWEN_BASE = endpointProfiles["qwen.dashscope"].baseURL;
7
7
  const QWEN_POLICIES = {
8
8
  "qwen3.7-max-preview": { maxTurns: 45 },
9
9
  "qwen3.7-plus-preview": { maxTurns: 40 },
@@ -19,6 +19,23 @@ const QWEN_POLICIES = {
19
19
  "qwen3.5-35b-a3b": { maxTurns: 20 },
20
20
  "qwen3.5-27b": { maxTurns: 20 },
21
21
  };
22
+ /**
23
+ * Qwen over its Anthropic-compatible endpoint.
24
+ */
25
+ export class QwenAnthropicProvider extends AnthropicProvider {
26
+ constructor(apiKey, model = "qwen3.6-plus", retry, baseURL = endpointProfiles["qwen.anthropic"].baseURL) {
27
+ super(apiKey, model, retry, {
28
+ baseURL,
29
+ authMode: "api-key",
30
+ });
31
+ }
32
+ providerName() {
33
+ return "qwen";
34
+ }
35
+ runtimePolicy() {
36
+ return QWEN_POLICIES[this.model] ?? {};
37
+ }
38
+ }
22
39
  export class QwenProvider {
23
40
  model;
24
41
  client;
@@ -26,7 +43,7 @@ export class QwenProvider {
26
43
  maxRetries;
27
44
  baseDelay;
28
45
  chat = new OpenAIChatAdapter();
29
- constructor(apiKey, model = "qwen3.6-plus", retry = { maxRetries: 3, baseDelay: 1000 }, baseURL = QWEN_BASE) {
46
+ constructor(apiKey, model = "qwen3.6-plus", retry = { maxRetries: 3, baseDelay: 1000 }, baseURL = endpointProfiles["qwen.dashscope"].baseURL) {
30
47
  this.model = model;
31
48
  this.client = withServerRuntimeGuard(() => new OpenAI({ apiKey, baseURL }));
32
49
  this.circuit = new CircuitBreaker();
@@ -113,11 +130,13 @@ export class QwenProvider {
113
130
  let totalTokens = 0;
114
131
  let inputTokens = 0;
115
132
  let outputTokens = 0;
133
+ let cacheReadTokens = 0;
116
134
  for await (const chunk of stream) {
117
135
  if (chunk.usage) {
118
136
  totalTokens = chunk.usage.total_tokens;
119
137
  inputTokens = chunk.usage.prompt_tokens ?? 0;
120
138
  outputTokens = chunk.usage.completion_tokens ?? 0;
139
+ cacheReadTokens = openAICachedPromptTokens(chunk.usage);
121
140
  continue;
122
141
  }
123
142
  const choice = chunk.choices[0];
@@ -184,7 +203,7 @@ export class QwenProvider {
184
203
  yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
185
204
  }
186
205
  if (totalTokens > 0)
187
- yield { type: "usage", totalTokens, inputTokens, outputTokens };
206
+ yield { type: "usage", totalTokens, inputTokens, outputTokens, ...(cacheReadTokens > 0 ? { cacheReadInputTokens: cacheReadTokens } : {}) };
188
207
  }
189
208
  thinkingExtraBody(extensions) {
190
209
  const enableThinking = Boolean(extensions?.enableThinking ?? extensions?.enable_thinking);
@@ -0,0 +1,14 @@
1
+ export interface SchemaValidation {
2
+ ok: boolean;
3
+ errors: string[];
4
+ }
5
+ type JsonSchema = Record<string, unknown>;
6
+ /** Validate `value` against `schema` (the supported subset). `path` is for error messages. */
7
+ export declare function validateAgainstSchema(value: unknown, schema: JsonSchema, path?: string): SchemaValidation;
8
+ /** The instruction appended to a node's goal so its agent produces schema-conforming JSON. */
9
+ export declare function schemaInstruction(schema: JsonSchema): string;
10
+ /** A stronger re-prompt for a retry after a validation failure. */
11
+ export declare function schemaRetryInstruction(schema: JsonSchema, errors: string[]): string;
12
+ /** Best-effort extraction of a JSON value from agent output (raw, fenced, or embedded). */
13
+ export declare function extractJsonValue(text: string): unknown;
14
+ export {};
@@ -0,0 +1,113 @@
1
+ // G3 structured output: a small, dependency-free JSON-Schema subset validator + helpers used by the
2
+ // workflow runner to enforce a node's `output_schema`. The kernel carries the schema verbatim (it is
3
+ // zero-I/O and never validates); enforcement lives here, SDK-side, where the agent output exists.
4
+ //
5
+ // Supported keywords (the common structured-output subset): `type` (object | array | string |
6
+ // number | integer | boolean | null), `required`, `properties` (recursive), `items` (recursive),
7
+ // `enum`. Unknown keywords are ignored rather than rejected — a permissive superset of these specs
8
+ // still validates, matching "instruct the model, then check the shape" rather than full JSON Schema.
9
+ function typeOfValue(v) {
10
+ if (v === null)
11
+ return "null";
12
+ if (Array.isArray(v))
13
+ return "array";
14
+ return typeof v; // "object" | "string" | "number" | "boolean"
15
+ }
16
+ function matchesType(v, t) {
17
+ switch (t) {
18
+ case "integer":
19
+ return typeof v === "number" && Number.isInteger(v);
20
+ case "number":
21
+ return typeof v === "number";
22
+ case "object":
23
+ return typeOfValue(v) === "object";
24
+ default:
25
+ return typeOfValue(v) === t;
26
+ }
27
+ }
28
+ /** Validate `value` against `schema` (the supported subset). `path` is for error messages. */
29
+ export function validateAgainstSchema(value, schema, path = "$") {
30
+ const errors = [];
31
+ const type = schema.type;
32
+ if (typeof type === "string" && !matchesType(value, type)) {
33
+ errors.push(`${path}: expected ${type}, got ${typeOfValue(value)}`);
34
+ return { ok: false, errors }; // type mismatch ⇒ stop; deeper checks are meaningless
35
+ }
36
+ if (Array.isArray(type) && !type.some(t => typeof t === "string" && matchesType(value, t))) {
37
+ errors.push(`${path}: expected one of [${type.join(", ")}], got ${typeOfValue(value)}`);
38
+ return { ok: false, errors };
39
+ }
40
+ if (Array.isArray(schema.enum) && !schema.enum.some(e => e === value)) {
41
+ errors.push(`${path}: value not in enum`);
42
+ }
43
+ if (typeOfValue(value) === "object") {
44
+ const obj = value;
45
+ const required = Array.isArray(schema.required) ? schema.required : [];
46
+ for (const key of required) {
47
+ if (!(key in obj))
48
+ errors.push(`${path}.${key}: required property missing`);
49
+ }
50
+ const properties = schema.properties ?? {};
51
+ for (const [key, sub] of Object.entries(properties)) {
52
+ if (key in obj) {
53
+ const r = validateAgainstSchema(obj[key], sub, `${path}.${key}`);
54
+ if (!r.ok)
55
+ errors.push(...r.errors);
56
+ }
57
+ }
58
+ }
59
+ if (typeOfValue(value) === "array" && schema.items && typeof schema.items === "object") {
60
+ const items = schema.items;
61
+ value.forEach((el, i) => {
62
+ const r = validateAgainstSchema(el, items, `${path}[${i}]`);
63
+ if (!r.ok)
64
+ errors.push(...r.errors);
65
+ });
66
+ }
67
+ return { ok: errors.length === 0, errors };
68
+ }
69
+ /** The instruction appended to a node's goal so its agent produces schema-conforming JSON. */
70
+ export function schemaInstruction(schema) {
71
+ return ("You MUST return ONLY a single JSON value that conforms to this JSON Schema, with no prose, " +
72
+ "no markdown, and no code fences:\n" +
73
+ JSON.stringify(schema));
74
+ }
75
+ /** A stronger re-prompt for a retry after a validation failure. */
76
+ export function schemaRetryInstruction(schema, errors) {
77
+ return (`${schemaInstruction(schema)}\n\nYour previous output did NOT conform: ${errors.join("; ")}. ` +
78
+ "Return ONLY the corrected JSON value.");
79
+ }
80
+ /** Best-effort extraction of a JSON value from agent output (raw, fenced, or embedded). */
81
+ export function extractJsonValue(text) {
82
+ const trimmed = (text ?? "").trim();
83
+ if (!trimmed)
84
+ return undefined;
85
+ const tryParse = (s) => {
86
+ try {
87
+ return JSON.parse(s);
88
+ }
89
+ catch {
90
+ return undefined;
91
+ }
92
+ };
93
+ const whole = tryParse(trimmed);
94
+ if (whole !== undefined)
95
+ return whole;
96
+ const fence = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
97
+ if (fence) {
98
+ const fenced = tryParse(fence[1].trim());
99
+ if (fenced !== undefined)
100
+ return fenced;
101
+ }
102
+ // Fall back to the first balanced {...} or [...] slice.
103
+ for (const [open, close] of [["{", "}"], ["[", "]"]]) {
104
+ const start = trimmed.indexOf(open);
105
+ const end = trimmed.lastIndexOf(close);
106
+ if (start !== -1 && end > start) {
107
+ const slice = tryParse(trimmed.slice(start, end + 1));
108
+ if (slice !== undefined)
109
+ return slice;
110
+ }
111
+ }
112
+ return undefined;
113
+ }
@@ -0,0 +1,15 @@
1
+ /** One dependency's contribution to a reduce: the producing node's agent id and its output text. */
2
+ export interface ReducerInput {
3
+ agentId: string;
4
+ output: string;
5
+ }
6
+ /** A pure function over a reduce node's dependency outputs → the reduce node's output string. */
7
+ export type Reducer = (inputs: ReducerInput[]) => string;
8
+ export type ReducerRegistry = Record<string, Reducer>;
9
+ /**
10
+ * Built-in reducers, available to every workflow without registration. A user-supplied registry is
11
+ * merged over these (so a custom reducer can shadow a built-in of the same name).
12
+ */
13
+ export declare const builtinReducers: ReducerRegistry;
14
+ /** Resolve a reducer by name from the built-ins overlaid with a user registry. */
15
+ export declare function resolveReducer(name: string, user?: ReducerRegistry): Reducer | undefined;
@@ -0,0 +1,57 @@
1
+ // G2 deterministic compute: the host-side reducer registry. A `NodeKind::Reduce` workflow node runs
2
+ // no LLM agent — the kernel hands the SDK a reducer name + its dependency outputs, and the SDK runs
3
+ // the named pure function here. This is the "ordinary code between stages" (dedupe / filter / merge /
4
+ // early-exit) of the code-orchestration model, expressed deterministically as a DAG node.
5
+ import { extractJsonValue } from "./output-schema.js";
6
+ /** Non-empty, trimmed lines of a string. */
7
+ function lines(s) {
8
+ return s
9
+ .split("\n")
10
+ .map(l => l.trim())
11
+ .filter(l => l.length > 0);
12
+ }
13
+ /**
14
+ * Built-in reducers, available to every workflow without registration. A user-supplied registry is
15
+ * merged over these (so a custom reducer can shadow a built-in of the same name).
16
+ */
17
+ export const builtinReducers = {
18
+ /** Concatenate every input's output, separated by blank lines, in dependency order. */
19
+ concat: inputs => inputs.map(i => i.output).join("\n\n"),
20
+ /** Union of non-empty lines across all inputs, first-seen order preserved (dedupe a fan-out). */
21
+ dedupe_lines: inputs => {
22
+ const seen = new Set();
23
+ const out = [];
24
+ for (const i of inputs) {
25
+ for (const line of lines(i.output)) {
26
+ if (!seen.has(line)) {
27
+ seen.add(line);
28
+ out.push(line);
29
+ }
30
+ }
31
+ }
32
+ return out.join("\n");
33
+ },
34
+ /** Parse each input as a JSON array, concatenate, dedupe by canonical JSON → a JSON array string. */
35
+ merge_json_arrays: inputs => {
36
+ const seen = new Set();
37
+ const merged = [];
38
+ for (const i of inputs) {
39
+ const v = extractJsonValue(i.output);
40
+ const arr = Array.isArray(v) ? v : v !== undefined ? [v] : [];
41
+ for (const el of arr) {
42
+ const key = JSON.stringify(el);
43
+ if (!seen.has(key)) {
44
+ seen.add(key);
45
+ merged.push(el);
46
+ }
47
+ }
48
+ }
49
+ return JSON.stringify(merged);
50
+ },
51
+ /** The number of inputs that produced any non-empty output — handy for early-exit/branch gates. */
52
+ count: inputs => String(inputs.filter(i => i.output.trim().length > 0).length),
53
+ };
54
+ /** Resolve a reducer by name from the built-ins overlaid with a user registry. */
55
+ export function resolveReducer(name, user) {
56
+ return user?.[name] ?? builtinReducers[name];
57
+ }
@@ -1,4 +1,4 @@
1
- import type { LLMProvider, Message, ToolSchema, StreamEvent, ToolSuspendEvent, PermissionRequestEvent, PermissionResponse, AsyncSummarizer, DreamSummarizer } from "../types.js";
1
+ import type { LLMProvider, Message, ContentPart, ToolSchema, StreamEvent, ToolSuspendEvent, PermissionRequestEvent, PermissionResponse, AsyncSummarizer, DreamSummarizer } from "../types.js";
2
2
  import type { DreamStore, MemoryEntry, MemoryQuery, MemoryWriteRequest } from "../memory/protocols.js";
3
3
  import type { KnowledgeSource } from "../knowledge/source.js";
4
4
  import type { SignalSource } from "../signals/types.js";
@@ -8,6 +8,7 @@ import type { ExecutionPlane } from "./execution-plane.js";
8
8
  import { type MemoryPolicy, type ResourceQuota } from "../kernel.js";
9
9
  import type { AgentRunSpec, MilestoneCheckResult, MilestoneContract, MilestonePolicy, WorkflowSpec } from "../types/agent.js";
10
10
  import { type SubAgentOrchestrator } from "./sub-agent-orchestrator.js";
11
+ import { type ReducerRegistry } from "./reducers.js";
11
12
  import { type GovernancePolicy } from "../governance.js";
12
13
  import { type NativeOsProfile, type OsProfileId } from "./os-profile.js";
13
14
  import { LargeResultSpool } from "./large-result-spool.js";
@@ -97,6 +98,9 @@ export interface RuntimeOptions {
97
98
  evalProvider: LLMProvider;
98
99
  maxAttempts?: number;
99
100
  };
101
+ /** G2: custom reducers for `NodeKind::Reduce` workflow nodes, merged over the built-ins
102
+ * (`concat` / `dedupe_lines` / `merge_json_arrays` / `count`). A reduce node runs no LLM. */
103
+ reducers?: ReducerRegistry;
100
104
  /** Optional system prompt injected into the dream synthesis call. */
101
105
  dreamSystemPrompt?: string;
102
106
  /** Custom LLM provider used for background memory consolidation (dream loop). */
@@ -159,6 +163,22 @@ export declare class RuntimeRunner {
159
163
  * Requires an active parent run (`run()` / `wake()` in progress or paused at milestone).
160
164
  */
161
165
  spawnSubAgent(spec: AgentRunSpec): AsyncIterable<StreamEvent>;
166
+ /**
167
+ * G3: run one workflow node, enforcing its `output_schema` (if any). Without a schema this is a
168
+ * plain `orchestrator.run`. With one, the node's agent is instructed to emit conforming JSON, its
169
+ * output is validated (the supported JSON-Schema subset), and on mismatch the node is re-run once
170
+ * with the validation errors fed back. If it still does not conform, the node is failed with the
171
+ * validation reason — a node that cannot meet its declared output contract starves its dependents,
172
+ * exactly as a denied spawn does.
173
+ */
174
+ private runWorkflowNode;
175
+ /**
176
+ * G2: execute a deterministic reduce node. Looks up the named reducer (built-ins overlaid with
177
+ * `opts.reducers`), runs it over the node's dependency outputs (gathered from `outputs`), and
178
+ * returns a synthetic completion carrying the reducer's output — no LLM, zero tokens. An unknown
179
+ * reducer or a thrown reducer fails the node (`Error` termination → the kernel starves dependents).
180
+ */
181
+ private runReduceNode;
162
182
  /**
163
183
  * W0-ABI: run a declarative workflow DAG. The kernel owns the DAG and gates every node spawn
164
184
  * through the syscall trap; this driver runs each kernel-emitted batch of nodes in parallel,
@@ -167,6 +187,7 @@ export declare class RuntimeRunner {
167
187
  */
168
188
  runWorkflow(spec: WorkflowSpec, opts?: {
169
189
  resumedCompleted?: string[];
190
+ resumedSubmissions?: Record<string, unknown>[][];
170
191
  }): Promise<{
171
192
  completed: string[];
172
193
  failed: string[];
@@ -185,6 +206,8 @@ export declare class RuntimeRunner {
185
206
  sessionId: string;
186
207
  goal: string;
187
208
  criteria?: string[];
209
+ /** Multimodal inputs (images / audio) attached to the task as a user message. */
210
+ attachments?: ContentPart[];
188
211
  extensions?: Record<string, unknown>;
189
212
  /** Parent transcript to preload (e.g. sub-agent full context inheritance). */
190
213
  inheritEvents?: Array<{