@qodo/sdk 0.7.0 → 0.8.0

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 (148) hide show
  1. package/.claude/skills/qodo-agent/SKILL.md +233 -3
  2. package/.claude/skills/qodo-agent/assets/programmatic-agent.ts +74 -1
  3. package/.claude/skills/qodo-agent/references/common-issues.md +79 -0
  4. package/dist/api/agent.d.ts.map +1 -1
  5. package/dist/api/agent.js +13 -8
  6. package/dist/api/agent.js.map +1 -1
  7. package/dist/api/http.d.ts.map +1 -1
  8. package/dist/api/http.js +5 -2
  9. package/dist/api/http.js.map +1 -1
  10. package/dist/api/websocket.d.ts.map +1 -1
  11. package/dist/api/websocket.js +6 -3
  12. package/dist/api/websocket.js.map +1 -1
  13. package/dist/auth/index.d.ts +18 -0
  14. package/dist/auth/index.d.ts.map +1 -1
  15. package/dist/auth/index.js +69 -10
  16. package/dist/auth/index.js.map +1 -1
  17. package/dist/context/messageManager.d.ts +1 -1
  18. package/dist/context/messageManager.d.ts.map +1 -1
  19. package/dist/context/messageManager.js +1 -1
  20. package/dist/context/messageManager.js.map +1 -1
  21. package/dist/index.d.ts +21 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +18 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/mcp/MCPManager.js +2 -2
  26. package/dist/mcp/MCPManager.js.map +1 -1
  27. package/dist/mcp/baseServer.js +1 -1
  28. package/dist/mcp/baseServer.js.map +1 -1
  29. package/dist/mcp/servers/ripgrep.d.ts.map +1 -1
  30. package/dist/mcp/servers/ripgrep.js +6 -2
  31. package/dist/mcp/servers/ripgrep.js.map +1 -1
  32. package/dist/mcp/servers/shell.js +1 -1
  33. package/dist/mcp/servers/shell.js.map +1 -1
  34. package/dist/mcp/serversRegistry.d.ts.map +1 -1
  35. package/dist/mcp/serversRegistry.js +7 -1
  36. package/dist/mcp/serversRegistry.js.map +1 -1
  37. package/dist/mcp/toolProcessor.d.ts +44 -4
  38. package/dist/mcp/toolProcessor.d.ts.map +1 -1
  39. package/dist/mcp/toolProcessor.js +255 -20
  40. package/dist/mcp/toolProcessor.js.map +1 -1
  41. package/dist/messages/index.d.ts +8 -0
  42. package/dist/messages/index.d.ts.map +1 -0
  43. package/dist/messages/index.js +7 -0
  44. package/dist/messages/index.js.map +1 -0
  45. package/dist/messages/openai.d.ts +26 -0
  46. package/dist/messages/openai.d.ts.map +1 -0
  47. package/dist/messages/openai.js +55 -0
  48. package/dist/messages/openai.js.map +1 -0
  49. package/dist/messages/types.d.ts +73 -0
  50. package/dist/messages/types.d.ts.map +1 -0
  51. package/dist/messages/types.js +78 -0
  52. package/dist/messages/types.js.map +1 -0
  53. package/dist/parser/index.js +3 -3
  54. package/dist/parser/index.js.map +1 -1
  55. package/dist/sdk/QodoSDK.d.ts +30 -10
  56. package/dist/sdk/QodoSDK.d.ts.map +1 -1
  57. package/dist/sdk/QodoSDK.js +165 -33
  58. package/dist/sdk/QodoSDK.js.map +1 -1
  59. package/dist/sdk/artifacts.d.ts +156 -0
  60. package/dist/sdk/artifacts.d.ts.map +1 -0
  61. package/dist/sdk/artifacts.js +166 -0
  62. package/dist/sdk/artifacts.js.map +1 -0
  63. package/dist/sdk/bootstrap.d.ts.map +1 -1
  64. package/dist/sdk/bootstrap.js +11 -4
  65. package/dist/sdk/bootstrap.js.map +1 -1
  66. package/dist/sdk/discovery.js +1 -1
  67. package/dist/sdk/discovery.js.map +1 -1
  68. package/dist/sdk/events.d.ts +46 -1
  69. package/dist/sdk/events.d.ts.map +1 -1
  70. package/dist/sdk/events.js +6 -0
  71. package/dist/sdk/events.js.map +1 -1
  72. package/dist/sdk/middleware.d.ts +59 -0
  73. package/dist/sdk/middleware.d.ts.map +1 -0
  74. package/dist/sdk/middleware.js +69 -0
  75. package/dist/sdk/middleware.js.map +1 -0
  76. package/dist/sdk/pipeline/PipelineBuilder.d.ts +61 -0
  77. package/dist/sdk/pipeline/PipelineBuilder.d.ts.map +1 -0
  78. package/dist/sdk/pipeline/PipelineBuilder.js +91 -0
  79. package/dist/sdk/pipeline/PipelineBuilder.js.map +1 -0
  80. package/dist/sdk/pipeline/PipelineRunner.d.ts +28 -0
  81. package/dist/sdk/pipeline/PipelineRunner.d.ts.map +1 -0
  82. package/dist/sdk/pipeline/PipelineRunner.js +210 -0
  83. package/dist/sdk/pipeline/PipelineRunner.js.map +1 -0
  84. package/dist/sdk/pipeline/compiler.d.ts +24 -0
  85. package/dist/sdk/pipeline/compiler.d.ts.map +1 -0
  86. package/dist/sdk/pipeline/compiler.js +197 -0
  87. package/dist/sdk/pipeline/compiler.js.map +1 -0
  88. package/dist/sdk/pipeline/declarative.d.ts +34 -0
  89. package/dist/sdk/pipeline/declarative.d.ts.map +1 -0
  90. package/dist/sdk/pipeline/declarative.js +9 -0
  91. package/dist/sdk/pipeline/declarative.js.map +1 -0
  92. package/dist/sdk/pipeline/index.d.ts +20 -0
  93. package/dist/sdk/pipeline/index.d.ts.map +1 -0
  94. package/dist/sdk/pipeline/index.js +19 -0
  95. package/dist/sdk/pipeline/index.js.map +1 -0
  96. package/dist/sdk/pipeline/types.d.ts +75 -0
  97. package/dist/sdk/pipeline/types.d.ts.map +1 -0
  98. package/dist/sdk/pipeline/types.js +10 -0
  99. package/dist/sdk/pipeline/types.js.map +1 -0
  100. package/dist/sdk/policies.d.ts +163 -0
  101. package/dist/sdk/policies.d.ts.map +1 -0
  102. package/dist/sdk/policies.js +243 -0
  103. package/dist/sdk/policies.js.map +1 -0
  104. package/dist/sdk/runner/AgentRunner.js +5 -5
  105. package/dist/sdk/runner/AgentRunner.js.map +1 -1
  106. package/dist/sdk/runner/finalize.d.ts +47 -0
  107. package/dist/sdk/runner/finalize.d.ts.map +1 -1
  108. package/dist/sdk/runner/finalize.js +42 -2
  109. package/dist/sdk/runner/finalize.js.map +1 -1
  110. package/dist/sdk/runner/formats.d.ts +1 -1
  111. package/dist/sdk/runner/formats.d.ts.map +1 -1
  112. package/dist/sdk/runner/formats.js +22 -37
  113. package/dist/sdk/runner/formats.js.map +1 -1
  114. package/dist/sdk/runner/progress.d.ts +1 -1
  115. package/dist/sdk/runner/progress.d.ts.map +1 -1
  116. package/dist/sdk/runner/progress.js +1 -1
  117. package/dist/sdk/runner/progress.js.map +1 -1
  118. package/dist/session/SessionContext.d.ts +1 -0
  119. package/dist/session/SessionContext.d.ts.map +1 -1
  120. package/dist/session/SessionContext.js +3 -0
  121. package/dist/session/SessionContext.js.map +1 -1
  122. package/dist/session/environment.d.ts +16 -6
  123. package/dist/session/environment.d.ts.map +1 -1
  124. package/dist/session/environment.js.map +1 -1
  125. package/dist/session/serverData.d.ts.map +1 -1
  126. package/dist/session/serverData.js +21 -3
  127. package/dist/session/serverData.js.map +1 -1
  128. package/dist/tracing/PipelineTracer.d.ts +37 -0
  129. package/dist/tracing/PipelineTracer.d.ts.map +1 -0
  130. package/dist/tracing/PipelineTracer.js +64 -0
  131. package/dist/tracing/PipelineTracer.js.map +1 -0
  132. package/dist/tracing/SdkTracer.d.ts +91 -0
  133. package/dist/tracing/SdkTracer.d.ts.map +1 -0
  134. package/dist/tracing/SdkTracer.js +243 -0
  135. package/dist/tracing/SdkTracer.js.map +1 -0
  136. package/dist/tracing/index.d.ts +6 -0
  137. package/dist/tracing/index.d.ts.map +1 -0
  138. package/dist/tracing/index.js +3 -0
  139. package/dist/tracing/index.js.map +1 -0
  140. package/dist/tracing/pipelineHelpers.d.ts +20 -0
  141. package/dist/tracing/pipelineHelpers.d.ts.map +1 -0
  142. package/dist/tracing/pipelineHelpers.js +140 -0
  143. package/dist/tracing/pipelineHelpers.js.map +1 -0
  144. package/dist/tracing/types.d.ts +46 -0
  145. package/dist/tracing/types.d.ts.map +1 -0
  146. package/dist/tracing/types.js +9 -0
  147. package/dist/tracing/types.js.map +1 -0
  148. package/package.json +10 -7
@@ -39,6 +39,47 @@ Use this skill when working with `@qodo/sdk` to build AI-powered agents. This sk
39
39
  | `parseArgsWithSchema()` | Validate args with Zod, get typed result |
40
40
  | `QodoSchemaError` | Schema validation error class |
41
41
 
42
+ ### Pipeline DSL
43
+
44
+ | Export | Purpose |
45
+ |--------|---------|
46
+ | `sdkPipeline()` | Build multi-step agent workflows |
47
+ | `runPipeline()` | Execute a pipeline with an SDK instance |
48
+ | `PipelineExecutionError` | Pipeline step failure error |
49
+
50
+ ### Tool Middleware
51
+
52
+ | Export | Purpose |
53
+ |--------|---------|
54
+ | `ToolMiddleware` (type) | Pre/post execution hook interface |
55
+ | `ToolMiddlewareError` | Middleware failure error |
56
+ | `MIDDLEWARE_TIMEOUT_MS` | Default hook timeout (10s) |
57
+ | `PROTECTED_TOOL_FIELDS` | Fields middleware cannot overwrite |
58
+
59
+ ### Tool Approval Policies
60
+
61
+ | Export | Purpose |
62
+ |--------|---------|
63
+ | `sdkPolicy()` | Fluent builder for declarative tool approval rules |
64
+ | `policyFromRules()` | Build a policy from a rule array |
65
+ | `globMatch()` | Glob pattern matching utility |
66
+
67
+ ### Artifacts
68
+
69
+ | Export | Purpose |
70
+ |--------|---------|
71
+ | `ArtifactStore` | Store/persist intermediate pipeline results |
72
+ | `withArtifacts()` | Wrap a pipeline step to auto-store output |
73
+ | `jsonSerializer` | Default JSON serializer |
74
+
75
+ ### OpenTelemetry Tracing
76
+
77
+ | Export | Purpose |
78
+ |--------|---------|
79
+ | `createSdkTracer()` | Create event-to-span tracer (duck-typed OTel API) |
80
+ | `tracePipeline()` | Trace pipeline execution with nested spans |
81
+ | `SdkTracer` | Tracer class for manual usage |
82
+
42
83
  ### Event Handling
43
84
 
44
85
  | Export | Purpose |
@@ -60,6 +101,9 @@ Use this skill when working with `@qodo/sdk` to build AI-powered agents. This sk
60
101
  | `QodoSchemaError` | Schema validation failures |
61
102
  | `QodoAuthError` | Authentication failures |
62
103
  | `QodoBackendBootstrapError` | Backend initialization failures |
104
+ | `QodoOutputValidationError` | Structured output didn't match Zod schema |
105
+ | `ToolMiddlewareError` | Tool middleware hook failure |
106
+ | `PipelineExecutionError` | Pipeline step failure |
63
107
 
64
108
  ### Built-in Tools
65
109
 
@@ -437,6 +481,178 @@ try {
437
481
  }
438
482
  ```
439
483
 
484
+ ### Pattern 8: Pipeline DSL (Multi-Step Workflows)
485
+
486
+ ```typescript
487
+ import { QodoSDK, sdkPipeline, runPipeline } from '@qodo/sdk';
488
+
489
+ const sdk = new QodoSDK({ autoApproveTools: true });
490
+
491
+ const pipeline = sdkPipeline<{ repo: string }>()
492
+ .step('lint', async ({ state, run }) => {
493
+ const r = await run('lint', { args: { path: state.repo } });
494
+ return { lintOutput: r.result.final_output };
495
+ })
496
+ .step('review', async ({ state, run }) => {
497
+ // TypeScript knows state has `repo` + `lintOutput`
498
+ const r = await run('review', {
499
+ extraInstructions: `Lint results:\n${state.lintOutput}`,
500
+ });
501
+ return { reviewOutput: r.result.final_output };
502
+ })
503
+ .build();
504
+
505
+ const result = await runPipeline(sdk, pipeline, { repo: './src' });
506
+ console.log(result.state.reviewOutput);
507
+ console.log(result.steps); // Step logs with timing
508
+
509
+ await sdk.dispose();
510
+ ```
511
+
512
+ Features: typed state threading, `.parallel([...])`, gates, error strategies (`'skip'`, `'throw'`, custom recovery).
513
+
514
+ ### Pattern 9: Dry Run Mode
515
+
516
+ ```typescript
517
+ import { QodoSDK } from '@qodo/sdk';
518
+
519
+ const sdk = new QodoSDK({ autoApproveTools: true });
520
+
521
+ // Preview what the agent would do without executing tools
522
+ const result = await sdk.run('review', {
523
+ args: { path: './src' },
524
+ dryRun: true,
525
+ });
526
+
527
+ console.log(result.result.final_output); // Agent describes planned actions
528
+ console.log(result.meta.dry_run); // true
529
+
530
+ await sdk.dispose();
531
+ ```
532
+
533
+ ### Pattern 10: Tool Middleware (Auditing & Control)
534
+
535
+ ```typescript
536
+ import { QodoSDK, type ToolMiddleware } from '@qodo/sdk';
537
+
538
+ const auditLogger: ToolMiddleware = {
539
+ name: 'audit-logger',
540
+ preExecute(toolData) {
541
+ console.log(`[AUDIT] ${toolData.server_name}.${toolData.tool_name}`);
542
+ },
543
+ postExecute(toolData, result) {
544
+ console.log(`[AUDIT] Result: ${result.isError ? 'ERROR' : 'OK'}`);
545
+ },
546
+ };
547
+
548
+ const readOnly: ToolMiddleware = {
549
+ name: 'read-only',
550
+ preExecute(toolData) {
551
+ if (['write_file', 'edit_file', 'delete_files'].includes(toolData.tool_name)) {
552
+ throw new Error(`Blocked: ${toolData.tool_name}`);
553
+ }
554
+ },
555
+ };
556
+
557
+ const sdk = new QodoSDK({
558
+ autoApproveTools: true,
559
+ toolMiddleware: [auditLogger, readOnly],
560
+ });
561
+ ```
562
+
563
+ ### Pattern 11: Tool Approval Policy DSL
564
+
565
+ ```typescript
566
+ import { QodoSDK, sdkPolicy } from '@qodo/sdk';
567
+
568
+ const policy = sdkPolicy()
569
+ .approve({ tools: ['read_*', 'list_*', 'git_status', 'git_log'] }, 'allow-reads')
570
+ .deny({ servers: ['shell'] }, 'block-shell')
571
+ .deny({ tools: ['write_file', 'edit_file', 'delete_files'] }, 'block-writes')
572
+ .requireHuman({ tools: ['git_commit'] }, 'review-commits')
573
+ .default('deny')
574
+ .build();
575
+
576
+ const sdk = new QodoSDK({
577
+ autoApproveTools: false,
578
+ toolApproval: policy.evaluate,
579
+ });
580
+
581
+ // Inspect policy decisions:
582
+ const result = policy.evaluateDetailed({
583
+ tool_name: 'read_files', server_name: 'filesystem', tool_args: {},
584
+ });
585
+ console.log(result.decision, result.matchedRule?.name); // 'approve', 'allow-reads'
586
+ ```
587
+
588
+ ### Pattern 12: Execution Timeouts and Retries
589
+
590
+ ```typescript
591
+ import { QodoSDK } from '@qodo/sdk';
592
+
593
+ const sdk = new QodoSDK({ autoApproveTools: true });
594
+
595
+ const result = await sdk.run('review', {
596
+ args: { path: './src' },
597
+ timeout: 30000, // 30 second limit per attempt
598
+ maxRetries: 2, // Retry up to 2 times on failure
599
+ });
600
+
601
+ if (result.meta.timed_out) {
602
+ console.warn('Run timed out');
603
+ }
604
+
605
+ await sdk.dispose();
606
+ ```
607
+
608
+ ### Pattern 13: Artifacts (Pipeline Intermediate Results)
609
+
610
+ ```typescript
611
+ import { QodoSDK, sdkPipeline, runPipeline, ArtifactStore, withArtifacts } from '@qodo/sdk';
612
+ import fs from 'fs/promises';
613
+
614
+ const store = new ArtifactStore({
615
+ onPersist: async (artifact) => {
616
+ await fs.writeFile(`./out/${artifact.stepName}.json`, artifact.serialized);
617
+ },
618
+ });
619
+
620
+ const pipeline = sdkPipeline<{ repo: string }>()
621
+ .step('lint', withArtifacts(store, async ({ state, run }) => {
622
+ const r = await run('lint', { args: { path: state.repo } });
623
+ return { lintOutput: r.result.final_output };
624
+ }))
625
+ .build();
626
+
627
+ const result = await runPipeline(sdk, pipeline, { repo: './src' });
628
+ console.log(store.all()); // All artifacts
629
+ console.log(store.getErrors()); // Persistence failures (non-fatal)
630
+ ```
631
+
632
+ ### Pattern 14: OpenTelemetry Tracing
633
+
634
+ ```typescript
635
+ import * as otel from '@opentelemetry/api';
636
+ import { QodoSDK, createSdkTracer, SdkEventType, matchSdkEvent } from '@qodo/sdk';
637
+
638
+ const sdk = new QodoSDK({ autoApproveTools: true });
639
+ const tracer = createSdkTracer(otel, { tracerName: 'my-service' });
640
+
641
+ // Events flow through unchanged, spans created automatically
642
+ for await (const ev of tracer.wrapStream(sdk.stream('review'))) {
643
+ matchSdkEvent(ev, {
644
+ [SdkEventType.MessageDelta]: (e) => process.stdout.write(e.data.delta),
645
+ [SdkEventType.Final]: (e) => console.log('Done:', e.data.success),
646
+ default: () => {},
647
+ });
648
+ }
649
+
650
+ tracer.flush();
651
+ await sdk.dispose();
652
+ ```
653
+
654
+ No compile-time OTel dependency — install `@opentelemetry/api` as optional peer dep.
655
+
440
656
  ---
441
657
 
442
658
  ## Zod Schema Patterns
@@ -519,15 +735,15 @@ const args = z.object({
519
735
  | Event Type | When Emitted | Key Data Fields |
520
736
  |------------|--------------|-----------------|
521
737
  | `sdk.init` | SDK initialized | `sdk_version`, `backend.base_url`, `model` |
522
- | `sdk.run.started` | Run begins | `session_id`, `command`, `prompt_mode`, `cwd` |
738
+ | `sdk.run.started` | Run begins | `session_id`, `command`, `prompt_mode`, `cwd`, `dry_run?` |
523
739
  | `sdk.message.delta` | Streaming text | `delta`, `message_id`, `role` |
524
740
  | `sdk.message.full` | Full message update | `messages.langchain`, `messages.openai` |
525
741
  | `sdk.progress` | Progress update | `title`, `status`, `percent` |
526
742
  | `sdk.tool.requested` | Tool call requested | `tool_call_id`, `server_name`, `tool_name`, `tool_args`, `pending_approval` |
527
743
  | `sdk.tool.approved` | Tool approved/denied | `tool_call_id`, `approved`, `reason` |
528
- | `sdk.tool.executed` | Tool finished | `tool_call_id`, `result.isError`, `result.content` |
744
+ | `sdk.tool.executed` | Tool finished | `tool_call_id`, `result.isError`, `result.content`, `dry_run?` |
529
745
  | `sdk.error` | Error occurred | `message`, `cause` |
530
- | `sdk.final` | Run completed | `success`, `result.structured_output`, `result.final_output`, `messages` |
746
+ | `sdk.final` | Run completed | `success`, `result.structured_output`, `result.final_output`, `meta.dry_run?`, `meta.timed_out?` |
531
747
 
532
748
  ---
533
749
 
@@ -552,6 +768,7 @@ interface QodoSDKOptions {
552
768
  // Tool handling
553
769
  autoApproveTools?: boolean; // Auto-approve tools (default: true)
554
770
  toolApproval?: (req) => Promise<boolean> | boolean; // Custom approval
771
+ toolMiddleware?: ToolMiddleware[]; // Pre/post execution hooks
555
772
 
556
773
  // Session
557
774
  contextSessionIds?: string[]; // Previous sessions for context
@@ -654,3 +871,16 @@ z.object({
654
871
  - [assets/programmatic-agent.ts](assets/programmatic-agent.ts) - Complete working TypeScript agent template
655
872
  - [references/builtin-tools.md](references/builtin-tools.md) - Detailed guide to filesystem, git, ripgrep, and shell tools
656
873
  - [references/common-issues.md](references/common-issues.md) - Troubleshooting guide for common problems
874
+
875
+ ### Documentation (in `docs/`)
876
+
877
+ | Feature | Doc |
878
+ |---------|-----|
879
+ | Pipeline DSL | [docs/pipeline.md](../../../docs/pipeline.md) |
880
+ | Dry Run Mode | [docs/dry-run.md](../../../docs/dry-run.md) |
881
+ | Tool Middleware | [docs/tool-middleware.md](../../../docs/tool-middleware.md) |
882
+ | Structured Output | [docs/structured-output.md](../../../docs/structured-output.md) |
883
+ | Execution Timeouts | [docs/execution-timeouts.md](../../../docs/execution-timeouts.md) |
884
+ | OpenTelemetry | [docs/opentelemetry.md](../../../docs/opentelemetry.md) |
885
+ | Approval Policies | [docs/approval-policies.md](../../../docs/approval-policies.md) |
886
+ | Artifacts | [docs/artifacts.md](../../../docs/artifacts.md) |
@@ -5,6 +5,10 @@
5
5
  * - sdkAgent() and sdkCommand() builders
6
6
  * - Zod schemas with proper descriptions
7
7
  * - Event streaming with matchSdkEvent()
8
+ * - Pipeline DSL with typed state threading
9
+ * - Tool middleware for auditing
10
+ * - Dry run mode
11
+ * - Execution timeouts
8
12
  * - Error handling
9
13
  * - Proper cleanup with dispose()
10
14
  *
@@ -23,7 +27,11 @@ import {
23
27
  zJsonAny,
24
28
  QodoSchemaError,
25
29
  QodoAuthError,
26
- z
30
+ QodoOutputValidationError,
31
+ sdkPipeline,
32
+ runPipeline,
33
+ z,
34
+ type ToolMiddleware,
27
35
  } from '@qodo/sdk';
28
36
 
29
37
  // =============================================================================
@@ -180,6 +188,17 @@ Guidelines:
180
188
  // =============================================================================
181
189
 
182
190
  async function main() {
191
+ // Optional: create tool middleware for auditing
192
+ const auditMiddleware: ToolMiddleware = {
193
+ name: 'audit',
194
+ preExecute(toolData) {
195
+ console.log(` [audit] >>> ${toolData.server_name}.${toolData.tool_name}`);
196
+ },
197
+ postExecute(toolData, result) {
198
+ console.log(` [audit] <<< ${result.isError ? 'ERROR' : 'OK'}`);
199
+ },
200
+ };
201
+
183
202
  // Create SDK from the agent configuration
184
203
  const sdk = QodoSDK.fromAgent(codeAssistantAgent, {
185
204
  // Optional: override project path (defaults to cwd)
@@ -188,6 +207,9 @@ async function main() {
188
207
  // Optional: enable debug logging
189
208
  // debug: true,
190
209
 
210
+ // Optional: tool middleware for auditing, rate limiting, etc.
211
+ toolMiddleware: [auditMiddleware],
212
+
191
213
  // Optional: custom tool approval (default: auto-approve)
192
214
  // autoApproveTools: false,
193
215
  // toolApproval: async ({ tool_name, tool_args }) => {
@@ -296,6 +318,54 @@ async function main() {
296
318
  );
297
319
  // @ts-ignore
298
320
  console.log('Response:', promptResult.final_output);
321
+ // -------------------------------------------------------------------------
322
+ // Example 4: Pipeline (multi-step workflow)
323
+ // -------------------------------------------------------------------------
324
+ console.log('\n\n--- Pipeline example ---\n');
325
+
326
+ const pipeline = sdkPipeline<{ path: string }>()
327
+ .step('analyze', async ({ state, run }) => {
328
+ const r = await run('analyze', { args: { path: state.path } });
329
+ return { analysis: r.result.structured_output };
330
+ })
331
+ .step('summarize', async ({ state, run }) => {
332
+ const r = await run('', {
333
+ extraInstructions: `Summarize these findings in one paragraph: ${JSON.stringify(state.analysis)}`,
334
+ });
335
+ return { summary: r.result.final_output };
336
+ })
337
+ .build();
338
+
339
+ const pipelineResult = await runPipeline(sdk, pipeline, { path: './src' });
340
+ console.log('Pipeline summary:', pipelineResult.state.summary);
341
+ for (const log of pipelineResult.steps) {
342
+ console.log(` ${log.name}: ${log.status} (${log.durationMs}ms)`);
343
+ }
344
+
345
+ // -------------------------------------------------------------------------
346
+ // Example 5: Dry run mode (preview without side effects)
347
+ // -------------------------------------------------------------------------
348
+ console.log('\n\n--- Dry run example ---\n');
349
+
350
+ const dryResult = await sdk.run('analyze', {
351
+ args: { path: './src' },
352
+ dryRun: true,
353
+ });
354
+ console.log('Dry run output:', dryResult.result.final_output?.slice(0, 200));
355
+ console.log('Was dry run:', dryResult.meta.dry_run);
356
+
357
+ // -------------------------------------------------------------------------
358
+ // Example 6: Execution timeout with retry
359
+ // -------------------------------------------------------------------------
360
+ console.log('\n\n--- Timeout example ---\n');
361
+
362
+ const timedResult = await sdk.run('analyze', {
363
+ args: { path: './src' },
364
+ timeout: 60000, // 60 second limit
365
+ maxRetries: 1, // Retry once on failure
366
+ });
367
+ console.log('Timed out:', timedResult.meta.timed_out ?? false);
368
+
299
369
  } catch (error) {
300
370
  // Handle specific error types
301
371
  if (error instanceof QodoSchemaError) {
@@ -304,6 +374,9 @@ async function main() {
304
374
  } else if (error instanceof QodoAuthError) {
305
375
  console.error('Authentication error:', error.message);
306
376
  console.error('Check your QODO_API_KEY environment variable');
377
+ } else if (error instanceof QodoOutputValidationError) {
378
+ console.error('Output validation error:', error.issues);
379
+ console.error('Raw output:', error.rawOutput);
307
380
  } else {
308
381
  throw error;
309
382
  }
@@ -422,6 +422,85 @@ const sdk = new QodoSDK({
422
422
 
423
423
  ---
424
424
 
425
+ ## Pipeline and Middleware Errors
426
+
427
+ ### Pipeline Step Failed
428
+
429
+ **Error:**
430
+ ```
431
+ PipelineExecutionError: Step "lint" failed: ...
432
+ ```
433
+
434
+ **Cause:** A pipeline step threw an error and no error strategy was set.
435
+
436
+ **Solution:** Add error handling per step:
437
+
438
+ ```typescript
439
+ sdkPipeline()
440
+ .step('lint', lintFn, { onError: 'skip' }) // Skip on failure
441
+ .step('review', reviewFn, {
442
+ onError: (error, state) => ({ fallback: true }), // Custom recovery
443
+ })
444
+ .build();
445
+ ```
446
+
447
+ ---
448
+
449
+ ### Structured Output Validation Failed
450
+
451
+ **Error:**
452
+ ```
453
+ QodoOutputValidationError: Structured output validation failed
454
+ ```
455
+
456
+ **Cause:** The agent returned data that doesn't match the Zod output schema.
457
+
458
+ **Solution:** Check `error.issues` for details and `error.rawOutput` for what the agent actually returned:
459
+
460
+ ```typescript
461
+ import { QodoOutputValidationError } from '@qodo/sdk';
462
+
463
+ try {
464
+ await sdk.run('analyze');
465
+ } catch (e) {
466
+ if (e instanceof QodoOutputValidationError) {
467
+ console.error('Issues:', e.issues); // [{ path: ['score'], message: '...' }]
468
+ console.error('Raw:', e.rawOutput); // What the agent returned
469
+ }
470
+ }
471
+ ```
472
+
473
+ ---
474
+
475
+ ### Tool Middleware Error
476
+
477
+ **Error:**
478
+ ```
479
+ ToolMiddlewareError: Middleware "my-middleware" failed in preExecute
480
+ ```
481
+
482
+ **Cause:** A tool middleware hook threw or timed out (default: 10s).
483
+
484
+ **Solution:** Check the middleware implementation and handle errors gracefully:
485
+
486
+ ```typescript
487
+ import { ToolMiddlewareError, MIDDLEWARE_TIMEOUT_MS } from '@qodo/sdk';
488
+
489
+ // Middleware hooks should be fast and defensive
490
+ const safeMiddleware = {
491
+ name: 'safe-mw',
492
+ preExecute(toolData) {
493
+ try {
494
+ // Your logic here
495
+ } catch {
496
+ // Don't throw — return void to pass through
497
+ }
498
+ },
499
+ };
500
+ ```
501
+
502
+ ---
503
+
425
504
  ## Quick Debugging Checklist
426
505
 
427
506
  When something isn't working:
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/api/agent.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAC,MAAM,QAAQ,CAAC;AAGpC,OAAO,EAGL,YAAY,EAEb,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAK9C,OAAO,EAAkB,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAuBhE,qBAAa,QAAS,SAAQ,YAAY;IAkC5B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAjC5C,OAAO,CAAC,QAAQ,CAAkB;IAElC,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,cAAc,CAAC,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAsC;IAI1E,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;IACvD,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,kBAAkB,CAAsB;IAChD,OAAO,CAAC,cAAc,CAAS;IAI/B,OAAO,CAAC,wBAAwB,CAA8B;gBAEjC,cAAc,CAAC,EAAE,cAAc,YAAA;YAuB9C,eAAe;IAyB7B,OAAO,CAAC,sBAAsB;IAkF9B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,oBAAoB;IAKrB,kBAAkB,IAAI,eAAe;IAIrC,uBAAuB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,IAAI;IAKtF,OAAO,CAAC,oBAAoB;YAUd,6BAA6B;IAkBpC,iBAAiB,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI;IAgB5C,kBAAkB,IAAI,IAAI;IAK3B,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBxE,OAAO,CAAC,yBAAyB;IAUpB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAmC1G,UAAU,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAarD,SAAS,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB7C,iBAAiB,CAAC,OAAO,GAAE,OAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE,OAAO,CAAC,eAAe;YA4BT,cAAc;IAKrB,oBAAoB,IAAI,IAAI;IAInC,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,sBAAsB;YAUhB,iBAAiB;YASjB,kBAAkB;YAWlB,aAAa;YAqEb,sBAAsB;YAStB,cAAc;YAsCd,uBAAuB;YA0CvB,kBAAkB;IA4ChC,OAAO,CAAC,mBAAmB;YAIb,gBAAgB;YAWhB,aAAa;YAab,mBAAmB;YAqDnB,iBAAiB;YAajB,QAAQ;YAiGR,gBAAgB;IAmD9B,OAAO,CAAC,kBAAkB;YAwBZ,oBAAoB;YAgBpB,mBAAmB;YAUnB,WAAW;IAkClB,kBAAkB,IAAI,IAAI;IAK1B,OAAO,IAAI,IAAI;IAUf,cAAc,IAAI,WAAW;CAGrC"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/api/agent.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAC,MAAM,QAAQ,CAAC;AAGpC,OAAO,EAGL,YAAY,EAEb,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAK9C,OAAO,EAAkB,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAuBhE,qBAAa,QAAS,SAAQ,YAAY;IAkC5B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAjC5C,OAAO,CAAC,QAAQ,CAAkB;IAElC,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,KAAK;IAMb,OAAO,CAAC,cAAc,CAAC,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAsC;IAI1E,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;IACvD,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,kBAAkB,CAAsB;IAChD,OAAO,CAAC,cAAc,CAAS;IAI/B,OAAO,CAAC,wBAAwB,CAA8B;gBAEjC,cAAc,CAAC,EAAE,cAAc,YAAA;YAuB9C,eAAe;IAyB7B,OAAO,CAAC,sBAAsB;IAkF9B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,oBAAoB;IAKrB,kBAAkB,IAAI,eAAe;IAIrC,uBAAuB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,IAAI;IAKtF,OAAO,CAAC,oBAAoB;YAUd,6BAA6B;IAkBpC,iBAAiB,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI;IAgB5C,kBAAkB,IAAI,IAAI;IAK3B,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBxE,OAAO,CAAC,yBAAyB;IAUpB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAmC1G,UAAU,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAarD,SAAS,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB7C,iBAAiB,CAAC,OAAO,GAAE,OAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE,OAAO,CAAC,eAAe;YA4BT,cAAc;IAKrB,oBAAoB,IAAI,IAAI;IAInC,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,sBAAsB;YAUhB,iBAAiB;YASjB,kBAAkB;YAWlB,aAAa;YAqEb,sBAAsB;YAStB,cAAc;YAsCd,uBAAuB;YA0CvB,kBAAkB;IA4ChC,OAAO,CAAC,mBAAmB;YAIb,gBAAgB;YAWhB,aAAa;YAab,mBAAmB;YA0DnB,iBAAiB;YAajB,QAAQ;YAiGR,gBAAgB;IAmD9B,OAAO,CAAC,kBAAkB;YAwBZ,oBAAoB;YAgBpB,mBAAmB;YAUnB,WAAW;IAkClB,kBAAkB,IAAI,IAAI;IAK1B,OAAO,IAAI,IAAI;IAUf,cAAc,IAAI,WAAW;CAGrC"}
package/dist/api/agent.js CHANGED
@@ -6,7 +6,7 @@ import { getUserData, getCurrentGitSha1 } from "./utils.js";
6
6
  import { EndNode, UserResponse } from "../constants/tools.js";
7
7
  import { SessionContext } from "../session/index.js";
8
8
  import { TaskTracker } from "./taskTracking.js";
9
- import { toolProcessorManager } from "../mcp/index.js";
9
+ import { ToolProcessorManager } from "../mcp/index.js";
10
10
  import process from "node:process";
11
11
  import { ServerData } from "../session/index.js";
12
12
  import { httpRequest } from "./http.js";
@@ -166,7 +166,7 @@ export class AgentAPI extends EventEmitter {
166
166
  await this.responseCallback({ forceStop: true });
167
167
  }
168
168
  }
169
- catch { }
169
+ catch { /* best-effort: forceStop on reconnect should not crash the WS listener */ }
170
170
  });
171
171
  this.wsClient.on('checkpointRecovery', (info) => {
172
172
  this.debug('[AgentAPI] Checkpoint recovery initiated', `| Reason: ${info.reason}`, info.checkpoint ? `| Checkpoint: ${info.checkpoint.substring(0, 8)}` : '');
@@ -639,10 +639,15 @@ export class AgentAPI extends EventEmitter {
639
639
  return;
640
640
  }
641
641
  const isAutoApprovedTool = this.mcpManager.isAutoApprovedTool(toolData.server_name, toolData.tool, toolData.tool_args);
642
- // Dry run tools if configured
643
- const processedToolData = await toolProcessorManager.preProcessTool(toolData, this.sessionContext);
642
+ const processedToolData = await ToolProcessorManager.getInstance().preProcessTool(toolData, this.sessionContext);
644
643
  await this.processToolReasoning(processedToolData, isAutoApprovedTool);
645
- if (isAutoApprovedTool) {
644
+ // Dry run: simulate tool execution without side effects
645
+ if (this.sessionContext?.isDryRun()) {
646
+ const dryResult = await ToolProcessorManager.getInstance().dryRunTool(processedToolData, this.sessionContext);
647
+ await this.processToolResponse(processedToolData, dryResult);
648
+ await this.sendToolResponse(processedToolData, dryResult);
649
+ }
650
+ else if (isAutoApprovedTool) {
646
651
  await this.callTool(processedToolData);
647
652
  }
648
653
  else {
@@ -701,7 +706,7 @@ export class AgentAPI extends EventEmitter {
701
706
  }
702
707
  }
703
708
  }
704
- catch { }
709
+ catch { /* best-effort: tool arg patching should not block tool execution */ }
705
710
  if (!this.mcpManager) {
706
711
  // Should not be reachable because we guard earlier, but keep a safe fallback
707
712
  // that gracefully declines the tool call instead of throwing.
@@ -723,7 +728,7 @@ export class AgentAPI extends EventEmitter {
723
728
  return;
724
729
  }
725
730
  // Post-process the tool result
726
- const processedResponse = await toolProcessorManager.postProcessTool(toolData, response);
731
+ const processedResponse = await ToolProcessorManager.getInstance().postProcessTool(toolData, response);
727
732
  await this.processToolResponse(toolData, processedResponse);
728
733
  // Send tool response via WebSocket
729
734
  await this.sendToolResponse(toolData, processedResponse);
@@ -870,7 +875,7 @@ export class AgentAPI extends EventEmitter {
870
875
  try {
871
876
  this.cleanupConnections();
872
877
  }
873
- catch { }
878
+ catch { /* cleanup: connection teardown must not throw */ }
874
879
  this.pendingToolRequests.clear();
875
880
  this.currentSession = undefined;
876
881
  this.latestSessionId = "";