@struktur/sdk 2.2.0 → 2.3.1

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 (103) hide show
  1. package/dist/artifacts/fileToArtifact.d.ts +8 -0
  2. package/dist/artifacts/fileToArtifact.d.ts.map +1 -0
  3. package/dist/artifacts/input.d.ts +60 -0
  4. package/dist/artifacts/input.d.ts.map +1 -0
  5. package/dist/artifacts/providers.d.ts +5 -0
  6. package/dist/artifacts/providers.d.ts.map +1 -0
  7. package/dist/artifacts/urlToArtifact.d.ts +3 -0
  8. package/dist/artifacts/urlToArtifact.d.ts.map +1 -0
  9. package/dist/auth/config.d.ts +34 -0
  10. package/dist/auth/config.d.ts.map +1 -0
  11. package/dist/auth/tokens.d.ts +18 -0
  12. package/dist/auth/tokens.d.ts.map +1 -0
  13. package/dist/chunking/ArtifactBatcher.d.ts +11 -0
  14. package/dist/chunking/ArtifactBatcher.d.ts.map +1 -0
  15. package/dist/chunking/ArtifactSplitter.d.ts +10 -0
  16. package/dist/chunking/ArtifactSplitter.d.ts.map +1 -0
  17. package/dist/debug/logger.d.ts +169 -0
  18. package/dist/debug/logger.d.ts.map +1 -0
  19. package/dist/extract.d.ts +3 -0
  20. package/dist/extract.d.ts.map +1 -0
  21. package/dist/fields.d.ts +75 -0
  22. package/dist/fields.d.ts.map +1 -0
  23. package/dist/index.d.ts +24 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +1493 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/llm/LLMClient.d.ts +40 -0
  28. package/dist/llm/LLMClient.d.ts.map +1 -0
  29. package/dist/llm/RetryingRunner.d.ts +37 -0
  30. package/dist/llm/RetryingRunner.d.ts.map +1 -0
  31. package/dist/llm/message.d.ts +12 -0
  32. package/dist/llm/message.d.ts.map +1 -0
  33. package/dist/llm/models.d.ts +13 -0
  34. package/dist/llm/models.d.ts.map +1 -0
  35. package/dist/llm/resolveModel.d.ts +3 -0
  36. package/dist/llm/resolveModel.d.ts.map +1 -0
  37. package/dist/merge/Deduplicator.d.ts +4 -0
  38. package/dist/merge/Deduplicator.d.ts.map +1 -0
  39. package/dist/merge/SmartDataMerger.d.ts +7 -0
  40. package/dist/merge/SmartDataMerger.d.ts.map +1 -0
  41. package/dist/parsers/collect.d.ts +7 -0
  42. package/dist/parsers/collect.d.ts.map +1 -0
  43. package/dist/parsers/index.d.ts +7 -0
  44. package/dist/parsers/index.d.ts.map +1 -0
  45. package/dist/parsers/mime.d.ts +12 -0
  46. package/dist/parsers/mime.d.ts.map +1 -0
  47. package/dist/parsers/npm.d.ts +16 -0
  48. package/dist/parsers/npm.d.ts.map +1 -0
  49. package/dist/parsers/pdf.d.ts +36 -0
  50. package/dist/parsers/pdf.d.ts.map +1 -0
  51. package/dist/parsers/runner.d.ts +4 -0
  52. package/dist/parsers/runner.d.ts.map +1 -0
  53. package/dist/parsers/types.d.ts +27 -0
  54. package/dist/parsers/types.d.ts.map +1 -0
  55. package/dist/parsers.d.ts +1 -0
  56. package/dist/prompts/DeduplicationPrompt.d.ts +5 -0
  57. package/dist/prompts/DeduplicationPrompt.d.ts.map +1 -0
  58. package/dist/prompts/ExtractorPrompt.d.ts +6 -0
  59. package/dist/prompts/ExtractorPrompt.d.ts.map +1 -0
  60. package/dist/prompts/ParallelMergerPrompt.d.ts +5 -0
  61. package/dist/prompts/ParallelMergerPrompt.d.ts.map +1 -0
  62. package/dist/prompts/SequentialExtractorPrompt.d.ts +6 -0
  63. package/dist/prompts/SequentialExtractorPrompt.d.ts.map +1 -0
  64. package/dist/prompts/formatArtifacts.d.ts +3 -0
  65. package/dist/prompts/formatArtifacts.d.ts.map +1 -0
  66. package/dist/strategies/DoublePassAutoMergeStrategy.d.ts +23 -0
  67. package/dist/strategies/DoublePassAutoMergeStrategy.d.ts.map +1 -0
  68. package/dist/strategies/DoublePassStrategy.d.ts +22 -0
  69. package/dist/strategies/DoublePassStrategy.d.ts.map +1 -0
  70. package/dist/strategies/ParallelAutoMergeStrategy.d.ts +27 -0
  71. package/dist/strategies/ParallelAutoMergeStrategy.d.ts.map +1 -0
  72. package/dist/strategies/ParallelStrategy.d.ts +22 -0
  73. package/dist/strategies/ParallelStrategy.d.ts.map +1 -0
  74. package/dist/strategies/SequentialAutoMergeStrategy.d.ts +22 -0
  75. package/dist/strategies/SequentialAutoMergeStrategy.d.ts.map +1 -0
  76. package/dist/strategies/SequentialStrategy.d.ts +20 -0
  77. package/dist/strategies/SequentialStrategy.d.ts.map +1 -0
  78. package/dist/strategies/SimpleStrategy.d.ts +18 -0
  79. package/dist/strategies/SimpleStrategy.d.ts.map +1 -0
  80. package/dist/strategies/agent/AgentStrategy.d.ts +44 -0
  81. package/dist/strategies/agent/AgentStrategy.d.ts.map +1 -0
  82. package/dist/strategies/agent/AgentTools.d.ts +55 -0
  83. package/dist/strategies/agent/AgentTools.d.ts.map +1 -0
  84. package/dist/strategies/agent/ArtifactFilesystem.d.ts +51 -0
  85. package/dist/strategies/agent/ArtifactFilesystem.d.ts.map +1 -0
  86. package/dist/strategies/agent/index.d.ts +4 -0
  87. package/dist/strategies/agent/index.d.ts.map +1 -0
  88. package/dist/strategies/concurrency.d.ts +2 -0
  89. package/dist/strategies/concurrency.d.ts.map +1 -0
  90. package/dist/strategies/index.d.ts +9 -0
  91. package/dist/strategies/index.d.ts.map +1 -0
  92. package/dist/strategies/utils.d.ts +39 -0
  93. package/dist/strategies/utils.d.ts.map +1 -0
  94. package/dist/strategies.d.ts +1 -0
  95. package/dist/strategies.js +1495 -0
  96. package/dist/strategies.js.map +1 -1
  97. package/dist/tokenization.d.ts +11 -0
  98. package/dist/tokenization.d.ts.map +1 -0
  99. package/dist/types.d.ts +178 -0
  100. package/dist/types.d.ts.map +1 -0
  101. package/dist/validation/validator.d.ts +20 -0
  102. package/dist/validation/validator.d.ts.map +1 -0
  103. package/package.json +14 -10
@@ -2416,7 +2416,1501 @@ var DoublePassAutoMergeStrategy = class {
2416
2416
  var doublePassAutoMerge = (config) => {
2417
2417
  return new DoublePassAutoMergeStrategy(config);
2418
2418
  };
2419
+
2420
+ // src/strategies/agent/AgentStrategy.ts
2421
+ import {
2422
+ createAgentSession,
2423
+ AuthStorage,
2424
+ ModelRegistry,
2425
+ SessionManager,
2426
+ SettingsManager,
2427
+ DefaultResourceLoader
2428
+ } from "@mariozechner/pi-coding-agent";
2429
+ import { Bash as Bash2 } from "just-bash";
2430
+
2431
+ // src/strategies/agent/ArtifactFilesystem.ts
2432
+ var detectImageFormat = (base64) => {
2433
+ if (base64.startsWith("/9j/")) {
2434
+ return "jpg";
2435
+ }
2436
+ if (base64.startsWith("iVBOR")) {
2437
+ return "png";
2438
+ }
2439
+ if (base64.startsWith("R0lGOD")) {
2440
+ return "gif";
2441
+ }
2442
+ if (base64.startsWith("UklGR")) {
2443
+ return "webp";
2444
+ }
2445
+ if (base64.startsWith("Qk")) {
2446
+ return "bmp";
2447
+ }
2448
+ if (base64.startsWith("PHN2Zy") || base64.startsWith("data:image/svg")) {
2449
+ return "svg";
2450
+ }
2451
+ return "bin";
2452
+ };
2453
+ var sanitizeArtifactName = (name) => {
2454
+ return name.replace(/[^a-zA-Z0-9]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase();
2455
+ };
2456
+ var createVirtualFilesystem = (artifacts) => {
2457
+ const virtualFiles = /* @__PURE__ */ new Map();
2458
+ const transformedArtifacts = artifacts.map((artifact) => {
2459
+ const artifactName = sanitizeArtifactName(artifact.id);
2460
+ return {
2461
+ id: artifact.id,
2462
+ type: artifact.type,
2463
+ metadata: artifact.metadata,
2464
+ tokens: artifact.tokens,
2465
+ contents: artifact.contents.map((content, contentIndex) => {
2466
+ const pageNumber = content.page;
2467
+ return {
2468
+ page: content.page,
2469
+ text: content.text,
2470
+ media: content.media?.map((media, mediaIndex) => {
2471
+ if (media.base64 && media.base64.length > 0) {
2472
+ const extension = detectImageFormat(media.base64);
2473
+ let virtualPath;
2474
+ if (pageNumber !== void 0) {
2475
+ virtualPath = `/images/${artifactName}-page-${pageNumber}-image-${mediaIndex}.${extension}`;
2476
+ } else {
2477
+ virtualPath = `/images/${artifactName}-image-${mediaIndex}.${extension}`;
2478
+ }
2479
+ virtualFiles.set(virtualPath, media.base64);
2480
+ return {
2481
+ type: media.type,
2482
+ url: media.url,
2483
+ text: media.text,
2484
+ x: media.x,
2485
+ y: media.y,
2486
+ width: media.width,
2487
+ height: media.height,
2488
+ imageType: media.imageType,
2489
+ virtualPath,
2490
+ originalBase64: `[BASE64: ${media.base64.length} chars]`
2491
+ // For debugging
2492
+ };
2493
+ }
2494
+ return {
2495
+ type: media.type,
2496
+ url: media.url,
2497
+ text: media.text,
2498
+ x: media.x,
2499
+ y: media.y,
2500
+ width: media.width,
2501
+ height: media.height,
2502
+ imageType: media.imageType
2503
+ };
2504
+ })
2505
+ };
2506
+ })
2507
+ };
2508
+ });
2509
+ const totalImagesWithBase64 = Array.from(virtualFiles.keys()).length;
2510
+ const manifest = {
2511
+ count: artifacts.length,
2512
+ artifacts: transformedArtifacts,
2513
+ totalTokens: transformedArtifacts.reduce((sum, a) => sum + (a.tokens || 0), 0),
2514
+ summary: {
2515
+ textArtifacts: artifacts.filter((a) => a.type === "text").length,
2516
+ imageArtifacts: artifacts.filter((a) => a.type === "image").length,
2517
+ pdfArtifacts: artifacts.filter((a) => a.type === "pdf").length,
2518
+ fileArtifacts: artifacts.filter((a) => a.type === "file").length
2519
+ },
2520
+ virtualFiles: {
2521
+ count: totalImagesWithBase64,
2522
+ paths: Array.from(virtualFiles.keys())
2523
+ }
2524
+ };
2525
+ const getImageByPath = (path) => {
2526
+ return virtualFiles.get(path);
2527
+ };
2528
+ const filesystem = {
2529
+ "/artifact.json": JSON.stringify(transformedArtifacts, null, 2),
2530
+ "/manifest.json": JSON.stringify(manifest, null, 2),
2531
+ virtualFiles,
2532
+ getImageByPath
2533
+ };
2534
+ return filesystem;
2535
+ };
2536
+
2537
+ // src/strategies/agent/AgentTools.ts
2538
+ import "just-bash";
2539
+ import { Type } from "@sinclair/typebox";
2540
+ var BashParams = Type.Object({
2541
+ command: Type.String({
2542
+ description: "The bash command to execute"
2543
+ }),
2544
+ timeout: Type.Optional(
2545
+ Type.Number({
2546
+ description: "Timeout in milliseconds (default: 30000)"
2547
+ })
2548
+ )
2549
+ });
2550
+ var ReadParams = Type.Object({
2551
+ file_path: Type.String({
2552
+ description: "The absolute path to the file to read"
2553
+ }),
2554
+ offset: Type.Optional(
2555
+ Type.Number({
2556
+ description: "Line number to start reading from (1-indexed, default: 1)"
2557
+ })
2558
+ ),
2559
+ limit: Type.Optional(
2560
+ Type.Number({
2561
+ description: "Maximum number of lines to read"
2562
+ })
2563
+ )
2564
+ });
2565
+ var GrepParams = Type.Object({
2566
+ pattern: Type.String({
2567
+ description: "The search pattern"
2568
+ }),
2569
+ path: Type.String({
2570
+ description: "The file or directory to search in"
2571
+ }),
2572
+ options: Type.Optional(
2573
+ Type.String({
2574
+ description: "Additional grep options (e.g., '-r' for recursive, '-i' for case-insensitive)"
2575
+ })
2576
+ )
2577
+ });
2578
+ var FindParams = Type.Object({
2579
+ path: Type.String({
2580
+ description: "The directory to search in"
2581
+ }),
2582
+ name: Type.Optional(
2583
+ Type.String({
2584
+ description: "Filename pattern to match (e.g., '*.json')"
2585
+ })
2586
+ )
2587
+ });
2588
+ var LsParams = Type.Object({
2589
+ path: Type.String({
2590
+ description: "The directory to list"
2591
+ }),
2592
+ recursive: Type.Optional(
2593
+ Type.Boolean({
2594
+ description: "List recursively"
2595
+ })
2596
+ )
2597
+ });
2598
+ var SetOutputDataParams = Type.Object({
2599
+ data: Type.Any({
2600
+ description: "The output data to set. Can be any shape - will be validated against the schema."
2601
+ })
2602
+ });
2603
+ var UpdateOutputDataParams = Type.Object({
2604
+ changes: Type.Record(Type.String(), Type.Any(), {
2605
+ description: "Changes to merge into the existing output data. Uses deep merge. Missing fields are preserved."
2606
+ })
2607
+ });
2608
+ var ViewImageParams = Type.Object({
2609
+ image_path: Type.String({
2610
+ description: "The absolute path to the image file to view (e.g., '/artifacts/images/artifact-name-page-1-image-0.png')"
2611
+ })
2612
+ });
2613
+ var FinishParams = Type.Object({});
2614
+ var FailParams = Type.Object({
2615
+ reason: Type.String({
2616
+ description: "Explanation of why extraction failed or what data could not be found."
2617
+ })
2618
+ });
2619
+ var createVirtualFilesystemTools = (bash, getImageByPath) => {
2620
+ const bashTool = {
2621
+ name: "bash",
2622
+ label: "Bash",
2623
+ description: "Execute bash commands in the virtual filesystem. Use this to explore artifacts with commands like cat, grep, head, tail, jq, etc.",
2624
+ parameters: BashParams,
2625
+ execute: async (toolCallId, params, signal, onUpdate, ctx) => {
2626
+ try {
2627
+ const result = await bash.exec(params.command);
2628
+ return {
2629
+ content: [
2630
+ {
2631
+ type: "text",
2632
+ text: result.exitCode === 0 ? result.stdout || "(no output)" : `Exit code ${result.exitCode}: ${result.stderr || result.stdout || "(no output)"}`
2633
+ }
2634
+ ],
2635
+ details: {
2636
+ exitCode: result.exitCode,
2637
+ ...result.stderr && { stderr: result.stderr }
2638
+ }
2639
+ };
2640
+ } catch (error) {
2641
+ const errorMsg = error.message;
2642
+ console.error(`[AgentTools] Bash command error: ${errorMsg}`);
2643
+ console.error(`[AgentTools] Command: ${params.command}`);
2644
+ return {
2645
+ content: [
2646
+ {
2647
+ type: "text",
2648
+ text: `Error: ${errorMsg}`
2649
+ }
2650
+ ],
2651
+ details: {
2652
+ error: errorMsg
2653
+ },
2654
+ isError: true
2655
+ };
2656
+ }
2657
+ }
2658
+ };
2659
+ const readTool = {
2660
+ name: "read",
2661
+ label: "Read File",
2662
+ description: "Read the contents of a file from the virtual filesystem. Supports pagination with offset and limit parameters. Can read virtual image files (binary/base64 content).",
2663
+ parameters: ReadParams,
2664
+ execute: async (toolCallId, params, signal, onUpdate, ctx) => {
2665
+ try {
2666
+ if (getImageByPath && params.file_path.startsWith("/images/")) {
2667
+ const imageData = getImageByPath(params.file_path);
2668
+ if (imageData) {
2669
+ const displayData = imageData.length > 1e3 ? imageData.slice(0, 1e3) + "... [truncated]" : imageData;
2670
+ return {
2671
+ content: [
2672
+ {
2673
+ type: "text",
2674
+ text: `[IMAGE FILE: ${params.file_path}]
2675
+ Base64 content (${imageData.length} chars):
2676
+ ${displayData}`
2677
+ }
2678
+ ],
2679
+ details: {
2680
+ path: params.file_path,
2681
+ size: imageData.length,
2682
+ truncated: imageData.length > 1e3
2683
+ }
2684
+ };
2685
+ }
2686
+ }
2687
+ let command;
2688
+ const offset = params.offset || 1;
2689
+ const limit = params.limit;
2690
+ const endLine = limit ? offset + limit - 1 : void 0;
2691
+ if (limit && endLine) {
2692
+ command = `sed -n '${offset},${endLine}p' "${params.file_path}"`;
2693
+ } else if (offset > 1) {
2694
+ command = `sed -n '${offset},$p' "${params.file_path}"`;
2695
+ } else {
2696
+ command = `cat "${params.file_path}"`;
2697
+ }
2698
+ const result = await bash.exec(command);
2699
+ if (result.exitCode !== 0) {
2700
+ return {
2701
+ content: [
2702
+ {
2703
+ type: "text",
2704
+ text: `Error reading file: ${result.stderr || "File not found"}`
2705
+ }
2706
+ ],
2707
+ details: {
2708
+ error: result.stderr || "File not found"
2709
+ },
2710
+ isError: true
2711
+ };
2712
+ }
2713
+ return {
2714
+ content: [
2715
+ {
2716
+ type: "text",
2717
+ text: result.stdout
2718
+ }
2719
+ ],
2720
+ details: {
2721
+ lines: result.stdout.split("\n").length,
2722
+ characters: result.stdout.length
2723
+ }
2724
+ };
2725
+ } catch (error) {
2726
+ const errorMsg = error.message;
2727
+ console.error(`[AgentTools] Read error: ${errorMsg}`);
2728
+ console.error(`[AgentTools] File path: ${params.file_path}`);
2729
+ return {
2730
+ content: [
2731
+ {
2732
+ type: "text",
2733
+ text: `Error: ${errorMsg}`
2734
+ }
2735
+ ],
2736
+ details: {
2737
+ error: errorMsg
2738
+ },
2739
+ isError: true
2740
+ };
2741
+ }
2742
+ }
2743
+ };
2744
+ const grepTool = {
2745
+ name: "grep",
2746
+ label: "Grep",
2747
+ description: "Search for patterns in files using grep",
2748
+ parameters: GrepParams,
2749
+ execute: async (toolCallId, params, signal, onUpdate, ctx) => {
2750
+ try {
2751
+ const options = params.options || "";
2752
+ const command = `grep ${options} "${params.pattern}" "${params.path}" 2>/dev/null || echo "(no matches found)"`;
2753
+ const result = await bash.exec(command);
2754
+ return {
2755
+ content: [
2756
+ {
2757
+ type: "text",
2758
+ text: result.stdout || "(no matches found)"
2759
+ }
2760
+ ],
2761
+ details: {
2762
+ matches: result.stdout.split("\n").filter((line) => line.trim()).length
2763
+ }
2764
+ };
2765
+ } catch (error) {
2766
+ return {
2767
+ content: [
2768
+ {
2769
+ type: "text",
2770
+ text: `(no matches found)`
2771
+ }
2772
+ ],
2773
+ details: {}
2774
+ };
2775
+ }
2776
+ }
2777
+ };
2778
+ const findTool = {
2779
+ name: "find",
2780
+ label: "Find",
2781
+ description: "Find files by name or pattern",
2782
+ parameters: FindParams,
2783
+ execute: async (toolCallId, params, signal, onUpdate, ctx) => {
2784
+ try {
2785
+ let command = `find "${params.path}" -type f`;
2786
+ if (params.name) {
2787
+ command = `find "${params.path}" -type f -name "${params.name}"`;
2788
+ }
2789
+ const result = await bash.exec(command);
2790
+ return {
2791
+ content: [
2792
+ {
2793
+ type: "text",
2794
+ text: result.stdout || "(no files found)"
2795
+ }
2796
+ ],
2797
+ details: {
2798
+ files: result.stdout.split("\n").filter((line) => line.trim()).length
2799
+ }
2800
+ };
2801
+ } catch (error) {
2802
+ return {
2803
+ content: [
2804
+ {
2805
+ type: "text",
2806
+ text: `Error: ${error.message}`
2807
+ }
2808
+ ],
2809
+ details: {
2810
+ error: error.message
2811
+ },
2812
+ isError: true
2813
+ };
2814
+ }
2815
+ }
2816
+ };
2817
+ const lsTool = {
2818
+ name: "ls",
2819
+ label: "List Directory",
2820
+ description: "List files and directories",
2821
+ parameters: LsParams,
2822
+ execute: async (toolCallId, params, signal, onUpdate, ctx) => {
2823
+ try {
2824
+ let command = `ls -la "${params.path}"`;
2825
+ if (params.recursive) {
2826
+ command = `ls -laR "${params.path}"`;
2827
+ }
2828
+ const result = await bash.exec(command);
2829
+ if (result.exitCode !== 0) {
2830
+ return {
2831
+ content: [
2832
+ {
2833
+ type: "text",
2834
+ text: `Error: ${result.stderr || "Directory not found"}`
2835
+ }
2836
+ ],
2837
+ details: {
2838
+ error: result.stderr || "Directory not found"
2839
+ },
2840
+ isError: true
2841
+ };
2842
+ }
2843
+ return {
2844
+ content: [
2845
+ {
2846
+ type: "text",
2847
+ text: result.stdout
2848
+ }
2849
+ ],
2850
+ details: {
2851
+ entries: result.stdout.split("\n").filter((line) => line.trim() && !line.startsWith("total ")).length
2852
+ }
2853
+ };
2854
+ } catch (error) {
2855
+ return {
2856
+ content: [
2857
+ {
2858
+ type: "text",
2859
+ text: `Error: ${error.message}`
2860
+ }
2861
+ ],
2862
+ details: {
2863
+ error: error.message
2864
+ },
2865
+ isError: true
2866
+ };
2867
+ }
2868
+ }
2869
+ };
2870
+ const viewImageTool = {
2871
+ name: "view_image",
2872
+ label: "View Image",
2873
+ description: "View an image file from the virtual filesystem. This injects the image as a visual message that the AI can see and analyze. Use this to examine images, screenshots, diagrams, or any visual content in the artifacts.",
2874
+ parameters: ViewImageParams,
2875
+ execute: async (toolCallId, params, signal, onUpdate, ctx) => {
2876
+ try {
2877
+ if (!getImageByPath) {
2878
+ return {
2879
+ content: [
2880
+ {
2881
+ type: "text",
2882
+ text: "Error: Image viewing is not available"
2883
+ }
2884
+ ],
2885
+ details: {
2886
+ error: "Image viewing is not available"
2887
+ },
2888
+ isError: true
2889
+ };
2890
+ }
2891
+ const imageData = getImageByPath(params.image_path);
2892
+ if (!imageData) {
2893
+ return {
2894
+ content: [
2895
+ {
2896
+ type: "text",
2897
+ text: `Error: Image not found at ${params.image_path}`
2898
+ }
2899
+ ],
2900
+ details: {
2901
+ error: `Image not found at ${params.image_path}`
2902
+ },
2903
+ isError: true
2904
+ };
2905
+ }
2906
+ const getImageFormat = (path, data) => {
2907
+ if (path.endsWith(".png")) return "image/png";
2908
+ if (path.endsWith(".jpg") || path.endsWith(".jpeg")) return "image/jpeg";
2909
+ if (path.endsWith(".gif")) return "image/gif";
2910
+ if (path.endsWith(".webp")) return "image/webp";
2911
+ if (path.endsWith(".bmp")) return "image/bmp";
2912
+ if (path.endsWith(".svg")) return "image/svg+xml";
2913
+ if (data.startsWith("/9j/")) return "image/jpeg";
2914
+ if (data.startsWith("iVBORw0KGgo")) return "image/png";
2915
+ if (data.startsWith("R0lGOD")) return "image/gif";
2916
+ if (data.startsWith("Qk")) return "image/bmp";
2917
+ if (data.startsWith("PHN2Zy")) return "image/svg+xml";
2918
+ return "image/png";
2919
+ };
2920
+ const mimeType = getImageFormat(params.image_path, imageData);
2921
+ return {
2922
+ content: [
2923
+ {
2924
+ type: "text",
2925
+ text: `[Viewing image: ${params.image_path}]`
2926
+ },
2927
+ {
2928
+ type: "image",
2929
+ data: imageData,
2930
+ mimeType
2931
+ }
2932
+ ],
2933
+ details: {
2934
+ path: params.image_path,
2935
+ format: mimeType,
2936
+ size: imageData.length
2937
+ }
2938
+ };
2939
+ } catch (error) {
2940
+ const errorMsg = error.message;
2941
+ console.error(`[AgentTools] View image error: ${errorMsg}`);
2942
+ console.error(`[AgentTools] Image path: ${params.image_path}`);
2943
+ return {
2944
+ content: [
2945
+ {
2946
+ type: "text",
2947
+ text: `Error viewing image: ${errorMsg}`
2948
+ }
2949
+ ],
2950
+ details: {
2951
+ error: errorMsg
2952
+ },
2953
+ isError: true
2954
+ };
2955
+ }
2956
+ }
2957
+ };
2958
+ return {
2959
+ bash: bashTool,
2960
+ read: readTool,
2961
+ grep: grepTool,
2962
+ find: findTool,
2963
+ ls: lsTool,
2964
+ view_image: viewImageTool
2965
+ };
2966
+ };
2967
+
2968
+ // src/strategies/agent/AgentStrategy.ts
2969
+ var defaultSystemPrompt = (schema, outputInstructions) => {
2970
+ return `You are an autonomous data extraction agent. Your task is to explore the provided artifacts and extract structured data according to the given JSON schema.
2971
+
2972
+ ## Your Environment
2973
+
2974
+ You have access to a virtual filesystem containing the artifacts to extract from:
2975
+ - "/artifact.json" - All artifacts in a structured JSON format (with embedded images replaced by virtual file paths)
2976
+ - "/manifest.json" - Summary and metadata about the artifacts
2977
+ - "/images/" - Virtual directory containing extracted image files (when artifacts have embedded images)
2978
+
2979
+ ## Virtual Image Files
2980
+
2981
+ When artifacts contain embedded images (base64-encoded), they are extracted to separate files in "/images/" for easier access:
2982
+ - Image files are named: "/images/{artifact-name}-page-{n}-image-{i}.{ext}"
2983
+ - {artifact-name}: Sanitized artifact ID (lowercase, special chars become dashes)
2984
+ - page-{n}: Page number from the artifact (if available)
2985
+ - image-{i}: Image index within that page
2986
+ - {ext}: File extension determined from base64 (jpg, png, gif, webp, bmp, svg, or bin)
2987
+ - Examples: "/images/invoice-page-3-image-0.jpg" or "/images/report-image-1.png"
2988
+ - Use the "/images/" directory to access image data
2989
+ - The manifest shows which virtual files are available
2990
+ - Image format is shown in the file extension for easy identification
2991
+
2992
+ ## IMPORTANT: Do NOT Install Tools
2993
+
2994
+ This is a **sandboxed environment** - you CANNOT install packages or tools:
2995
+ - \u274C DO NOT run: apt-get, pip install, npm install, brew install, etc.
2996
+ - \u274C DO NOT try to install tesseract, ocrmypdf, poppler, or any OCR tools
2997
+ - \u274C DO NOT check if tools exist with "which" or "command -v"
2998
+ - \u2705 ONLY use the provided tools listed below
2999
+ - \u2705 If a tool is missing, work with what you have or report it via fail()
3000
+
3001
+ ## Available Tools
3002
+
3003
+ ### Exploration Tools
3004
+ - **read** - Read file contents with pagination support (e.g., read {"file_path": "/manifest.json", "limit": 50})
3005
+ - **view_image** - View an image to see its contents visually (e.g., view_image {"image_path": "/images/doc-page-1-image-0.png"})
3006
+ - **bash** - Run shell commands (e.g., bash {"command": "head -20 /artifact.json"})
3007
+ - **grep** - Search for patterns in files
3008
+ - **find** - Find files by name or pattern
3009
+ - **ls** - List files and directories
3010
+
3011
+ ### Output Management Tools (IMPORTANT - Use These!)
3012
+ - **set_output_data** - Set the initial extraction output. Call this as soon as you find the first piece of data.
3013
+ - Example: set_output_data({"data": {"company_name": "Acme Corp"}})
3014
+ - The data can be any shape - you'll update it incrementally
3015
+
3016
+ - **update_output_data** - Add or modify fields in the existing output data
3017
+ - Example: update_output_data({"changes": {"address": "123 Main St"}})
3018
+ - This merges new data with existing data (deep merge)
3019
+ - Call this frequently as you discover more information
3020
+
3021
+ - **finish** - Call this when extraction is complete and data validates against the schema
3022
+ - Only works if the data is valid according to the schema
3023
+ - This ends the extraction successfully
3024
+
3025
+ - **fail** - Call this if the schema cannot be satisfied with available data
3026
+ - Provide a reason explaining what data was missing or why extraction failed
3027
+
3028
+ ## CRITICAL: Incremental Data Updates
3029
+
3030
+ **You MUST update the output data continuously as you explore!**
3031
+
3032
+ 1. **Start immediately**: As soon as you find the first field, call set_output_data
3033
+ 2. **Update frequently**: Every time you find new information, call update_output_data
3034
+ 3. **Build incrementally**: Don't wait until the end - keep adding data as you go
3035
+ 4. **Use all tools**: Combine exploration tools with output tools
3036
+
3037
+ ### Example Workflow
3038
+
3039
+ 1. Read manifest: read {"file_path": "/manifest.json", "limit": 20}
3040
+ 2. Find first data point: grep "company_name" /artifact.json
3041
+ 3. **Set initial data**: set_output_data({"data": {"company_name": "Acme Inc"}})
3042
+ 4. Continue exploring: read {"file_path": "/artifact.json", "offset": 50, "limit": 30}
3043
+ 5. **Update with more data**: update_output_data({"changes": {"address": "123 Main St", "city": "Berlin"}})
3044
+ 6. Check images: view_image {"image_path": "/images/doc-page-1-image-0.png"}
3045
+ 7. **Update again**: update_output_data({"changes": {"has_logo": true}})
3046
+ 8. Verify complete: Check all schema fields are present
3047
+ 9. **Finish**: finish()
3048
+
3049
+ ## Efficient Exploration Strategy
3050
+
3051
+ **Don't read entire files at once.** Files may be large. Instead:
3052
+
3053
+ 1. **Start small**: Read just the first 20-50 lines to understand the structure
3054
+ 2. **Navigate selectively**: Use offset and limit to jump to relevant sections
3055
+ 3. **Search first**: Use grep to find specific data before reading full content
3056
+ 4. **Iterate**: Make multiple small reads rather than one giant read
3057
+ 5. **Update as you go**: Call update_output_data immediately when you find data
3058
+
3059
+ ### Pagination Examples
3060
+
3061
+ Read first 30 lines:
3062
+ read {"file_path": "/artifact.json", "limit": 30}
3063
+
3064
+ Read lines 31-60 (page 2):
3065
+ read {"file_path": "/artifact.json", "offset": 31, "limit": 30}
3066
+
3067
+ Read from line 100 to end:
3068
+ read {"file_path": "/artifact.json", "offset": 100}
3069
+
3070
+ ## Output Rules
3071
+
3072
+ - **Update continuously**: Call update_output_data every time you find new information
3073
+ - **Start early**: Don't wait until the end - set initial data as soon as possible
3074
+ - **Use null for missing values**: If a field can't be found, set it to null
3075
+ - **Never guess**: Only extract information explicitly present in the artifacts
3076
+ - **Validate as you go**: The tools will tell you if your data has validation issues
3077
+ - **Finish properly**: You MUST call finish() to complete extraction successfully
3078
+ - **Fail if needed**: Use fail() if the schema truly cannot be satisfied
3079
+
3080
+ ${outputInstructions ? `
3081
+ ## Additional Instructions
3082
+
3083
+ ${outputInstructions}
3084
+ ` : ""}
3085
+
3086
+ ## JSON Schema
3087
+
3088
+ ${schema}
3089
+
3090
+ ## CRITICAL: Tool Calling Format
3091
+
3092
+ When calling tools, you MUST provide the correct parameters:
3093
+
3094
+ **CORRECT - read with file_path:**
3095
+ read {"file_path": "/manifest.json"}
3096
+
3097
+ **CORRECT - read with pagination:**
3098
+ read {"file_path": "/artifact.json", "offset": 1, "limit": 50}
3099
+
3100
+ **CORRECT - view image:**
3101
+ view_image {"image_path": "/images/doc-page-1-image-0.png"}
3102
+
3103
+ **CORRECT - set output data:**
3104
+ set_output_data {"data": {"company_name": "Acme Corp"}}
3105
+
3106
+ **CORRECT - update output:**
3107
+ update_output_data {"changes": {"address": "123 Main St"}}
3108
+
3109
+ **CORRECT - finish:**
3110
+ finish {}
3111
+
3112
+ **CORRECT - fail:**
3113
+ fail {"reason": "Document is not an invoice"}
3114
+
3115
+ ## Common Mistakes to AVOID
3116
+
3117
+ \u274C WRONG: read {} (missing file_path)
3118
+ \u274C WRONG: read {file_path: "/path"} (missing quotes around property names)
3119
+ \u274C WRONG: read /path (not using JSON format)
3120
+ \u274C WRONG: set_output_data {company: "Name"} (missing quotes and data wrapper)
3121
+ \u274C WRONG: Trying to install tools with apt-get, pip, npm, etc. (not allowed in sandbox)
3122
+
3123
+ ## Remember
3124
+
3125
+ 1. **ALWAYS** use set_output_data or update_output_data when you find information
3126
+ 2. **ALWAYS** call finish() when done (or fail() if impossible)
3127
+ 3. **ALWAYS** provide required parameters when calling tools (file_path for read, data for set_output_data, etc.)
3128
+ 4. **NEVER** try to install packages or external tools - work with what you have
3129
+ 5. The output tools will validate your data and report issues
3130
+ 6. You can update data multiple times - keep refining as you explore
3131
+ 7. The CLI shows your progress in real-time as you update the output`;
3132
+ };
3133
+ var AgentStrategy = class {
3134
+ name = "agent";
3135
+ config;
3136
+ constructor(config) {
3137
+ this.config = config;
3138
+ }
3139
+ getEstimatedSteps() {
3140
+ return this.config.maxSteps ?? 50;
3141
+ }
3142
+ async run(options) {
3143
+ const debug = options.debug ?? this.config.debug;
3144
+ const { telemetry } = options;
3145
+ const maxSteps = this.config.maxSteps ?? 50;
3146
+ const agentSpan = telemetry?.startSpan({
3147
+ name: "strategy.agent",
3148
+ kind: "AGENT",
3149
+ attributes: {
3150
+ "strategy.name": this.name,
3151
+ "agent.max_steps": maxSteps,
3152
+ "agent.model": this.config.model ? "custom" : `${this.config.provider}/${this.config.modelId}`,
3153
+ "agent.artifacts.count": options.artifacts.length
3154
+ }
3155
+ });
3156
+ const activeMessageSpans = /* @__PURE__ */ new Map();
3157
+ const activeToolSpans = /* @__PURE__ */ new Map();
3158
+ await options.events?.onStep?.({
3159
+ step: 1,
3160
+ total: this.getEstimatedSteps(),
3161
+ label: "agent_explore"
3162
+ });
3163
+ debug?.step({
3164
+ step: 1,
3165
+ total: this.getEstimatedSteps(),
3166
+ label: "agent_explore",
3167
+ strategy: this.name
3168
+ });
3169
+ const filesystem = createVirtualFilesystem(options.artifacts);
3170
+ const files = {
3171
+ "/artifact.json": filesystem["/artifact.json"],
3172
+ "/manifest.json": filesystem["/manifest.json"]
3173
+ };
3174
+ for (const [path, content] of filesystem.virtualFiles) {
3175
+ files[path] = content;
3176
+ }
3177
+ const bash = new Bash2({
3178
+ files,
3179
+ cwd: "/"
3180
+ });
3181
+ const virtualTools = createVirtualFilesystemTools(bash, filesystem.getImageByPath);
3182
+ const schema = JSON.stringify(options.schema, null, 2);
3183
+ const systemPrompt = this.config.systemPrompt ?? defaultSystemPrompt(schema, this.config.outputInstructions);
3184
+ const callId = `agent_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
3185
+ debug?.llmCallStart({
3186
+ callId,
3187
+ model: this.config.model ? JSON.stringify(this.config.model) : "default",
3188
+ schemaName: "extract",
3189
+ systemLength: systemPrompt.length,
3190
+ userLength: 0,
3191
+ artifactCount: options.artifacts.length
3192
+ });
3193
+ debug?.promptSystem({ callId, system: systemPrompt });
3194
+ const startTime = Date.now();
3195
+ const agentDir = this.config.agentDir;
3196
+ const authStorage = agentDir ? AuthStorage.create(`${agentDir}/auth.json`) : AuthStorage.create();
3197
+ if (this.config.apiKey && this.config.provider) {
3198
+ authStorage.setRuntimeApiKey(this.config.provider, this.config.apiKey);
3199
+ }
3200
+ const modelRegistry = new ModelRegistry(authStorage);
3201
+ let model = this.config.model;
3202
+ if (!model && this.config.provider && this.config.modelId) {
3203
+ if (this.config.verbose) {
3204
+ console.error(`[AgentStrategy] Looking up model: ${this.config.provider}/${this.config.modelId}`);
3205
+ }
3206
+ model = modelRegistry.find(this.config.provider, this.config.modelId);
3207
+ if (this.config.verbose) {
3208
+ console.error(`[AgentStrategy] Model resolved: ${model ? "success" : "failed"}`);
3209
+ if (model) {
3210
+ console.error(`[AgentStrategy] Model info:`, JSON.stringify(model).slice(0, 200));
3211
+ }
3212
+ }
3213
+ } else if (model && this.config.verbose) {
3214
+ console.error(`[AgentStrategy] Using pre-configured model`);
3215
+ }
3216
+ const loader = new DefaultResourceLoader({
3217
+ cwd: "/artifacts",
3218
+ agentDir: agentDir || void 0,
3219
+ systemPromptOverride: () => systemPrompt
3220
+ });
3221
+ await loader.reload();
3222
+ const settingsManager = SettingsManager.inMemory({
3223
+ compaction: { enabled: false }
3224
+ // Disable compaction for extraction tasks
3225
+ });
3226
+ let currentOutput = null;
3227
+ let isFinished = false;
3228
+ let finishError = null;
3229
+ let extractionFailed = false;
3230
+ let failureReason = null;
3231
+ const validateData = (data) => {
3232
+ try {
3233
+ JSON.stringify(data);
3234
+ return { valid: true, errors: [] };
3235
+ } catch (e) {
3236
+ return { valid: false, errors: [e.message] };
3237
+ }
3238
+ };
3239
+ const deepMerge = (target, source) => {
3240
+ const output = Object.assign({}, target);
3241
+ if (isObject(target) && isObject(source)) {
3242
+ Object.keys(source).forEach((key) => {
3243
+ if (isObject(source[key])) {
3244
+ if (!(key in target)) {
3245
+ Object.assign(output, { [key]: source[key] });
3246
+ } else {
3247
+ output[key] = deepMerge(target[key], source[key]);
3248
+ }
3249
+ } else {
3250
+ Object.assign(output, { [key]: source[key] });
3251
+ }
3252
+ });
3253
+ }
3254
+ return output;
3255
+ };
3256
+ const isObject = (item) => {
3257
+ return item && typeof item === "object" && !Array.isArray(item);
3258
+ };
3259
+ await options.events?.onStep?.({
3260
+ step: 2,
3261
+ total: this.getEstimatedSteps(),
3262
+ label: "agent_init"
3263
+ });
3264
+ debug?.step({
3265
+ step: 2,
3266
+ total: this.getEstimatedSteps(),
3267
+ label: "agent_init",
3268
+ strategy: this.name
3269
+ });
3270
+ const { Type: Type2 } = await import("@sinclair/typebox");
3271
+ const setOutputDataTool = {
3272
+ name: "set_output_data",
3273
+ label: "Set Output Data",
3274
+ description: "Set the initial output data. You can use any structure - it will be validated against the schema.",
3275
+ parameters: Type2.Object({
3276
+ data: Type2.Any({ description: "The output data to set" })
3277
+ }),
3278
+ execute: async (toolCallId, params) => {
3279
+ currentOutput = params.data;
3280
+ const validation = validateData(params.data);
3281
+ const status = validation.valid ? "\u2713 Valid structure" : `\u2717 Validation issues: ${validation.errors.join(", ")}`;
3282
+ await options.events?.onStep?.({
3283
+ step: stepCount + 1,
3284
+ total: this.getEstimatedSteps(),
3285
+ label: `Output: ${JSON.stringify(params.data).slice(0, 50)}...`
3286
+ });
3287
+ return {
3288
+ content: [{ type: "text", text: `Output data set. ${status}` }],
3289
+ details: { validation }
3290
+ };
3291
+ }
3292
+ };
3293
+ const updateOutputDataTool = {
3294
+ name: "update_output_data",
3295
+ label: "Update Output Data",
3296
+ description: "Update the output data by merging changes. Existing fields are preserved, new fields are added.",
3297
+ parameters: Type2.Object({
3298
+ changes: Type2.Record(Type2.String(), Type2.Any(), {
3299
+ description: "Changes to merge into existing data"
3300
+ })
3301
+ }),
3302
+ execute: async (toolCallId, params) => {
3303
+ if (currentOutput === null) {
3304
+ return {
3305
+ content: [{ type: "text", text: "Error: No output data set yet. Use set_output_data first." }],
3306
+ isError: true
3307
+ };
3308
+ }
3309
+ currentOutput = deepMerge(currentOutput, params.changes);
3310
+ const validation = validateData(currentOutput);
3311
+ const status = validation.valid ? "\u2713 Valid structure" : `\u2717 Validation issues: ${validation.errors.join(", ")}`;
3312
+ await options.events?.onStep?.({
3313
+ step: stepCount + 1,
3314
+ total: this.getEstimatedSteps(),
3315
+ label: `Updated: ${JSON.stringify(params.changes).slice(0, 50)}...`
3316
+ });
3317
+ return {
3318
+ content: [{ type: "text", text: `Output data updated. ${status}` }],
3319
+ details: { validation, currentOutput }
3320
+ };
3321
+ }
3322
+ };
3323
+ const finishTool = {
3324
+ name: "finish",
3325
+ label: "Finish Extraction",
3326
+ description: "Complete the extraction. Can only be called when data validates against the schema.",
3327
+ parameters: Type2.Object({}),
3328
+ execute: async (toolCallId) => {
3329
+ if (extractionFailed) {
3330
+ return {
3331
+ content: [{ type: "text", text: "Cannot finish - extraction was marked as failed." }],
3332
+ isError: true
3333
+ };
3334
+ }
3335
+ if (currentOutput === null) {
3336
+ return {
3337
+ content: [{ type: "text", text: "Error: No output data set. Extract data first." }],
3338
+ isError: true
3339
+ };
3340
+ }
3341
+ const validation = validateData(currentOutput);
3342
+ if (!validation.valid) {
3343
+ finishError = `Schema validation failed: ${validation.errors.join(", ")}`;
3344
+ return {
3345
+ content: [{
3346
+ type: "text",
3347
+ text: `Cannot finish: ${finishError}
3348
+
3349
+ Fix the data and try again, or use fail() if extraction is impossible.`
3350
+ }],
3351
+ isError: true
3352
+ };
3353
+ }
3354
+ isFinished = true;
3355
+ return {
3356
+ content: [{ type: "text", text: "\u2713 Extraction complete! Data validated successfully." }]
3357
+ };
3358
+ }
3359
+ };
3360
+ const failTool = {
3361
+ name: "fail",
3362
+ label: "Fail Extraction",
3363
+ description: "Mark extraction as failed when the schema cannot be satisfied with the available data.",
3364
+ parameters: Type2.Object({
3365
+ reason: Type2.String({ description: "Why extraction failed or what data was missing" })
3366
+ }),
3367
+ execute: async (toolCallId, params) => {
3368
+ extractionFailed = true;
3369
+ failureReason = params.reason;
3370
+ return {
3371
+ content: [{ type: "text", text: `Extraction marked as failed: ${params.reason}` }]
3372
+ };
3373
+ }
3374
+ };
3375
+ const allTools = [
3376
+ virtualTools.read,
3377
+ virtualTools.bash,
3378
+ virtualTools.grep,
3379
+ virtualTools.find,
3380
+ virtualTools.ls,
3381
+ setOutputDataTool,
3382
+ updateOutputDataTool,
3383
+ finishTool,
3384
+ failTool
3385
+ ];
3386
+ if (this.config.verbose) {
3387
+ console.error(`[AgentStrategy] Creating session with ${allTools.length} tools`);
3388
+ console.error(`[AgentStrategy] Tool names: ${allTools.map((t) => t.name).join(", ")}`);
3389
+ allTools.forEach((tool) => {
3390
+ console.error(`[AgentStrategy] Tool "${tool.name}" details:`);
3391
+ console.error(` - label: ${tool.label}`);
3392
+ console.error(` - description: ${tool.description?.slice(0, 100)}...`);
3393
+ console.error(` - has parameters: ${!!tool.parameters}`);
3394
+ if (tool.parameters) {
3395
+ console.error(` - parameters type: ${tool.parameters.type}`);
3396
+ console.error(` - required fields: ${JSON.stringify(tool.parameters.required)}`);
3397
+ }
3398
+ });
3399
+ }
3400
+ const { session } = await createAgentSession({
3401
+ model,
3402
+ authStorage,
3403
+ modelRegistry,
3404
+ resourceLoader: loader,
3405
+ sessionManager: SessionManager.inMemory(),
3406
+ settingsManager,
3407
+ tools: [],
3408
+ // No default tools
3409
+ customTools: allTools
3410
+ });
3411
+ if (this.config.verbose) {
3412
+ console.error(`[AgentStrategy] Session created successfully`);
3413
+ }
3414
+ await options.events?.onStep?.({
3415
+ step: 3,
3416
+ total: this.getEstimatedSteps(),
3417
+ label: "agent_session_ready"
3418
+ });
3419
+ debug?.step({
3420
+ step: 3,
3421
+ total: this.getEstimatedSteps(),
3422
+ label: "agent_session_ready",
3423
+ strategy: this.name
3424
+ });
3425
+ let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
3426
+ let stepCount = 0;
3427
+ let finalResponse = "";
3428
+ const maxToolCalls = maxSteps;
3429
+ let textBuffer = "";
3430
+ try {
3431
+ const unsubscribe = session.subscribe((event) => {
3432
+ try {
3433
+ switch (event.type) {
3434
+ case "message_update": {
3435
+ if (event.assistantMessageEvent.type === "text_delta") {
3436
+ const delta = event.assistantMessageEvent.delta;
3437
+ finalResponse += delta;
3438
+ options.events?.onAgentMessage?.({
3439
+ content: delta,
3440
+ role: "assistant"
3441
+ });
3442
+ textBuffer += delta;
3443
+ let newlineIndex;
3444
+ while ((newlineIndex = textBuffer.indexOf("\n")) !== -1) {
3445
+ const line = textBuffer.slice(0, newlineIndex).trim();
3446
+ textBuffer = textBuffer.slice(newlineIndex + 1);
3447
+ if (line.length > 0) {
3448
+ options.events?.onStep?.({
3449
+ step: stepCount,
3450
+ total: this.getEstimatedSteps(),
3451
+ label: `\u2192 ${line.slice(0, 120)}`
3452
+ });
3453
+ }
3454
+ }
3455
+ if (textBuffer.length > 100) {
3456
+ const line = textBuffer.trim();
3457
+ if (line.length > 0) {
3458
+ options.events?.onStep?.({
3459
+ step: stepCount,
3460
+ total: this.getEstimatedSteps(),
3461
+ label: `\u2192 ${line.slice(0, 120)}`
3462
+ });
3463
+ }
3464
+ textBuffer = "";
3465
+ }
3466
+ }
3467
+ break;
3468
+ }
3469
+ case "tool_execution_start": {
3470
+ stepCount++;
3471
+ if (telemetry && agentSpan) {
3472
+ const toolSpan = telemetry.startSpan({
3473
+ name: `agent.tool.${event.toolName}`,
3474
+ kind: "TOOL",
3475
+ parentSpan: agentSpan,
3476
+ attributes: {
3477
+ "tool.name": event.toolName,
3478
+ "tool.call_id": event.toolCallId,
3479
+ "tool.args": JSON.stringify(event.args || {})
3480
+ }
3481
+ });
3482
+ activeToolSpans.set(event.toolCallId, toolSpan);
3483
+ }
3484
+ let label;
3485
+ let detail;
3486
+ const args = event.args;
3487
+ const toolName = event.toolName;
3488
+ const toolStartEvent = {
3489
+ toolName,
3490
+ toolCallId: event.toolCallId,
3491
+ args: args || {}
3492
+ };
3493
+ options.events?.onAgentToolStart?.(toolStartEvent);
3494
+ if (toolName === "read" && args?.file_path) {
3495
+ const fileName = args.file_path.split("/").pop() || args.file_path;
3496
+ const pagination = [];
3497
+ if (args.offset && args.offset > 1) {
3498
+ pagination.push(`offset ${args.offset}`);
3499
+ }
3500
+ if (args.limit) {
3501
+ pagination.push(`limit ${args.limit}`);
3502
+ }
3503
+ const paginationStr = pagination.length > 0 ? ` (${pagination.join(", ")})` : "";
3504
+ label = `Read ${fileName}${paginationStr}`;
3505
+ detail = "";
3506
+ } else if (toolName === "bash" && args?.command) {
3507
+ const cmd = args.command.length > 40 ? args.command.slice(0, 37) + "..." : args.command;
3508
+ label = `Bash: ${cmd}`;
3509
+ detail = "";
3510
+ } else if (toolName === "grep" && args?.pattern) {
3511
+ label = `Grep "${args.pattern}"`;
3512
+ detail = args.path ? `in ${args.path.split("/").pop()}` : "";
3513
+ } else if (toolName === "find" && args?.path) {
3514
+ label = `Find`;
3515
+ detail = args.name ? `"${args.name}" in ${args.path}` : `in ${args.path}`;
3516
+ } else if (toolName === "ls" && args?.path) {
3517
+ label = `List ${args.path}`;
3518
+ detail = args.recursive ? "recursive" : "";
3519
+ } else if (toolName === "set_output_data") {
3520
+ label = "Set Output";
3521
+ detail = args?.data ? JSON.stringify(args.data).slice(0, 80) : "";
3522
+ } else if (toolName === "update_output_data") {
3523
+ label = "Update Output";
3524
+ detail = args?.changes ? JSON.stringify(args.changes).slice(0, 80) : "";
3525
+ } else if (toolName === "finish") {
3526
+ label = "Finish";
3527
+ detail = "";
3528
+ } else if (toolName === "fail") {
3529
+ label = "Fail";
3530
+ detail = args?.reason || "";
3531
+ } else {
3532
+ label = toolName;
3533
+ detail = args ? JSON.stringify(args).slice(0, 100) : "";
3534
+ }
3535
+ options.events?.onStep?.({
3536
+ step: stepCount + 1,
3537
+ total: this.getEstimatedSteps(),
3538
+ label,
3539
+ detail
3540
+ });
3541
+ debug?.step({
3542
+ step: stepCount + 1,
3543
+ total: this.getEstimatedSteps(),
3544
+ label,
3545
+ strategy: this.name
3546
+ });
3547
+ break;
3548
+ }
3549
+ case "tool_execution_end": {
3550
+ const toolEndEvent = event;
3551
+ const toolSpan = activeToolSpans.get(toolEndEvent.toolCallId);
3552
+ if (toolSpan && telemetry) {
3553
+ const hasError = toolEndEvent.isError || toolEndEvent.error;
3554
+ telemetry.endSpan(toolSpan, {
3555
+ status: hasError ? "error" : "ok",
3556
+ error: hasError ? new Error(toolEndEvent.error || "Tool execution failed") : void 0,
3557
+ output: toolEndEvent.result
3558
+ });
3559
+ activeToolSpans.delete(toolEndEvent.toolCallId);
3560
+ }
3561
+ options.events?.onAgentToolEnd?.({
3562
+ toolCallId: toolEndEvent.toolCallId,
3563
+ result: toolEndEvent.result,
3564
+ error: toolEndEvent.error || toolEndEvent.isError ? toolEndEvent.error || "Tool execution failed" : void 0
3565
+ });
3566
+ if (toolEndEvent.isError || toolEndEvent.error) {
3567
+ const errorMsg = toolEndEvent.error || "Unknown tool error";
3568
+ const toolName = toolEndEvent.toolName || "unknown";
3569
+ const toolCallId = toolEndEvent.toolCallId || "unknown";
3570
+ if (this.config.verbose) {
3571
+ console.error(`[AgentStrategy] Tool execution failed: ${errorMsg}`);
3572
+ console.error(`[AgentStrategy] Tool: ${toolName}, Call ID: ${toolCallId}`);
3573
+ if (toolEndEvent.result) {
3574
+ console.error(`[AgentStrategy] Result:`, JSON.stringify(toolEndEvent.result));
3575
+ }
3576
+ }
3577
+ }
3578
+ break;
3579
+ }
3580
+ case "agent_end": {
3581
+ if (textBuffer.trim().length > 0) {
3582
+ options.events?.onStep?.({
3583
+ step: stepCount,
3584
+ total: this.getEstimatedSteps(),
3585
+ label: `\u2192 ${textBuffer.trim().slice(0, 120)}`
3586
+ });
3587
+ textBuffer = "";
3588
+ }
3589
+ if (event.messages && event.messages.length > 0) {
3590
+ const inputTokens = event.messages.reduce((sum, msg) => {
3591
+ if (msg.role === "user") {
3592
+ return sum + Math.ceil(JSON.stringify(msg.content).length / 4);
3593
+ }
3594
+ return sum;
3595
+ }, 0);
3596
+ const outputTokens = event.messages.reduce((sum, msg) => {
3597
+ if (msg.role === "assistant") {
3598
+ return sum + Math.ceil(JSON.stringify(msg.content).length / 4);
3599
+ }
3600
+ return sum;
3601
+ }, 0);
3602
+ usage = {
3603
+ inputTokens,
3604
+ outputTokens,
3605
+ totalTokens: inputTokens + outputTokens
3606
+ };
3607
+ }
3608
+ options.events?.onStep?.({
3609
+ step: stepCount + 1,
3610
+ total: this.getEstimatedSteps(),
3611
+ label: "agent_complete"
3612
+ });
3613
+ debug?.step({
3614
+ step: stepCount + 1,
3615
+ total: this.getEstimatedSteps(),
3616
+ label: "agent_complete",
3617
+ strategy: this.name
3618
+ });
3619
+ break;
3620
+ }
3621
+ case "message_start": {
3622
+ const messageKey = `msg_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
3623
+ if (telemetry && agentSpan) {
3624
+ const llmSpan = telemetry.startSpan({
3625
+ name: "agent.llm.generate",
3626
+ kind: "LLM",
3627
+ parentSpan: agentSpan,
3628
+ attributes: {
3629
+ "llm.message_type": event.message?.role || "unknown",
3630
+ "llm.type": "agent_message"
3631
+ }
3632
+ });
3633
+ activeMessageSpans.set(messageKey, llmSpan);
3634
+ event._telemetryKey = messageKey;
3635
+ }
3636
+ break;
3637
+ }
3638
+ case "message_end": {
3639
+ const messageKey = event._telemetryKey;
3640
+ if (messageKey) {
3641
+ const llmSpan = activeMessageSpans.get(messageKey);
3642
+ if (llmSpan && telemetry) {
3643
+ telemetry.endSpan(llmSpan, {
3644
+ status: "ok",
3645
+ output: finalResponse.slice(-200)
3646
+ // Last 200 chars as output preview
3647
+ });
3648
+ activeMessageSpans.delete(messageKey);
3649
+ }
3650
+ }
3651
+ break;
3652
+ }
3653
+ case "agent_start": {
3654
+ if (this.config.verbose) {
3655
+ console.error("[AgentStrategy] Agent started processing");
3656
+ }
3657
+ break;
3658
+ }
3659
+ case "turn_start":
3660
+ case "turn_end": {
3661
+ break;
3662
+ }
3663
+ case "auto_compaction_start":
3664
+ case "auto_compaction_end":
3665
+ case "auto_retry_start":
3666
+ case "auto_retry_end": {
3667
+ break;
3668
+ }
3669
+ default: {
3670
+ const unhandledEvent = event;
3671
+ if (unhandledEvent.type && !unhandledEvent.type.includes("_")) {
3672
+ if (this.config.verbose) {
3673
+ console.error(`[AgentStrategy] Unexpected event type: ${unhandledEvent.type}`);
3674
+ }
3675
+ }
3676
+ break;
3677
+ }
3678
+ }
3679
+ } catch (eventHandlerError) {
3680
+ if (this.config.verbose) {
3681
+ console.error(`[AgentStrategy] Error in event handler: ${eventHandlerError.message}`);
3682
+ console.error("AgentStrategy event handler error:", eventHandlerError);
3683
+ }
3684
+ throw eventHandlerError;
3685
+ }
3686
+ });
3687
+ await session.prompt(
3688
+ "Begin exploring the artifacts and extract the required data according to the schema. Start by reading the manifest file.",
3689
+ {
3690
+ // Ensure the agent keeps running until it calls finish() or fail()
3691
+ }
3692
+ );
3693
+ if (currentOutput === null && !extractionFailed && !isFinished) {
3694
+ if (this.config.verbose) {
3695
+ console.error("[AgentStrategy] No output after first run. Sending retry prompt...");
3696
+ }
3697
+ await options.events?.onStep?.({
3698
+ step: stepCount + 1,
3699
+ total: this.getEstimatedSteps(),
3700
+ label: "Retry: forcing output extraction"
3701
+ });
3702
+ const retryUnsubscribe = session.subscribe((event) => {
3703
+ try {
3704
+ switch (event.type) {
3705
+ case "message_update": {
3706
+ if (event.assistantMessageEvent.type === "text_delta") {
3707
+ const delta = event.assistantMessageEvent.delta;
3708
+ finalResponse += delta;
3709
+ options.events?.onAgentMessage?.({
3710
+ content: delta,
3711
+ role: "assistant"
3712
+ });
3713
+ textBuffer += delta;
3714
+ let newlineIndex;
3715
+ while ((newlineIndex = textBuffer.indexOf("\n")) !== -1) {
3716
+ const line = textBuffer.slice(0, newlineIndex).trim();
3717
+ textBuffer = textBuffer.slice(newlineIndex + 1);
3718
+ if (line.length > 0) {
3719
+ options.events?.onStep?.({
3720
+ step: stepCount,
3721
+ total: this.getEstimatedSteps(),
3722
+ label: `\u2192 ${line.slice(0, 120)}`
3723
+ });
3724
+ }
3725
+ }
3726
+ if (textBuffer.length > 100) {
3727
+ const line = textBuffer.trim();
3728
+ if (line.length > 0) {
3729
+ options.events?.onStep?.({
3730
+ step: stepCount,
3731
+ total: this.getEstimatedSteps(),
3732
+ label: `\u2192 ${line.slice(0, 120)}`
3733
+ });
3734
+ }
3735
+ textBuffer = "";
3736
+ }
3737
+ }
3738
+ break;
3739
+ }
3740
+ case "tool_execution_start": {
3741
+ stepCount++;
3742
+ const toolName = event.toolName;
3743
+ const args = event.args;
3744
+ let label = toolName;
3745
+ if (toolName === "set_output_data") {
3746
+ label = "Set Output (retry)";
3747
+ } else if (toolName === "update_output_data") {
3748
+ label = "Update Output (retry)";
3749
+ } else if (toolName === "finish") {
3750
+ label = "Finish (retry)";
3751
+ } else if (toolName === "fail") {
3752
+ label = "Fail (retry)";
3753
+ }
3754
+ options.events?.onStep?.({
3755
+ step: stepCount + 1,
3756
+ total: this.getEstimatedSteps(),
3757
+ label
3758
+ });
3759
+ break;
3760
+ }
3761
+ }
3762
+ } catch (eventHandlerError) {
3763
+ if (this.config.verbose) {
3764
+ console.error(`[AgentStrategy] Error in retry event handler: ${eventHandlerError.message}`);
3765
+ }
3766
+ }
3767
+ });
3768
+ await session.prompt(
3769
+ `You have explored the artifacts but haven't called any output tools yet. You MUST now extract data and call either:
3770
+ 1. set_output_data with the extracted data, then finish()
3771
+ 2. fail() if the document doesn't contain the required information
3772
+
3773
+ The schema requires: ${JSON.stringify(options.schema).slice(0, 200)}...
3774
+
3775
+ Extract what you can from the artifacts and set the output data NOW.`,
3776
+ {}
3777
+ );
3778
+ retryUnsubscribe();
3779
+ }
3780
+ unsubscribe();
3781
+ const durationMs = Date.now() - startTime;
3782
+ let extractedData;
3783
+ if (extractionFailed) {
3784
+ throw new Error(`Extraction failed: ${failureReason}`);
3785
+ }
3786
+ if (!isFinished) {
3787
+ if (currentOutput !== null) {
3788
+ if (this.config.verbose) {
3789
+ console.error("[AgentStrategy] Warning: Agent did not call finish(). Using collected data.");
3790
+ }
3791
+ const validation = validateData(currentOutput);
3792
+ if (!validation.valid && this.config.verbose) {
3793
+ console.error(`[AgentStrategy] Data validation issues: ${validation.errors.join(", ")}`);
3794
+ }
3795
+ extractedData = currentOutput;
3796
+ } else {
3797
+ const toolCallsMade = stepCount > 0;
3798
+ const toolsFailed = toolCallsMade && currentOutput === null;
3799
+ if (toolsFailed) {
3800
+ const errorMsg = `Agent did not produce any output data. The model may not support tool calling properly.
3801
+
3802
+ Troubleshooting:
3803
+ 1. Check if your model supports function calling/tool use
3804
+ 2. Try a different model like anthropic/claude-sonnet-4 or openai/gpt-4o
3805
+ 3. See MODEL_COMPATIBILITY.md for supported models
3806
+
3807
+ If you continue to see "Tool execution failed" errors with empty tool names,
3808
+ the model is not compatible with the agent strategy. Use --strategy simple instead.`;
3809
+ throw new Error(errorMsg);
3810
+ } else {
3811
+ const errorMsg = `Agent did not produce any output data. No data was extracted.
3812
+
3813
+ This can happen when:
3814
+ - The model doesn't support tool calling properly
3815
+ - The agent got confused and didn't use the output tools
3816
+ - The document doesn't contain extractable data
3817
+
3818
+ Suggestions:
3819
+ 1. Try a different model with better tool support (anthropic/claude-sonnet-4)
3820
+ 2. Use --strategy simple for models without tool calling
3821
+ 3. Check if the document actually contains the data specified in your schema
3822
+
3823
+ Retry was attempted but the agent still didn't produce output.`;
3824
+ throw new Error(errorMsg);
3825
+ }
3826
+ }
3827
+ } else {
3828
+ if (currentOutput === null) {
3829
+ throw new Error("Agent called finish() but no output data was set.");
3830
+ }
3831
+ extractedData = currentOutput;
3832
+ }
3833
+ debug?.rawResponse({ callId, response: extractedData });
3834
+ debug?.llmCallComplete({
3835
+ callId,
3836
+ success: true,
3837
+ inputTokens: usage.inputTokens,
3838
+ outputTokens: usage.outputTokens,
3839
+ totalTokens: usage.totalTokens,
3840
+ durationMs
3841
+ });
3842
+ await options.events?.onStep?.({
3843
+ step: this.getEstimatedSteps(),
3844
+ total: this.getEstimatedSteps(),
3845
+ label: "extract"
3846
+ });
3847
+ debug?.step({
3848
+ step: this.getEstimatedSteps(),
3849
+ total: this.getEstimatedSteps(),
3850
+ label: "extract",
3851
+ strategy: this.name
3852
+ });
3853
+ if (telemetry) {
3854
+ for (const [key, span] of activeMessageSpans.entries()) {
3855
+ telemetry.endSpan(span, { status: "ok" });
3856
+ }
3857
+ activeMessageSpans.clear();
3858
+ for (const [key, span] of activeToolSpans.entries()) {
3859
+ telemetry.endSpan(span, { status: "ok" });
3860
+ }
3861
+ activeToolSpans.clear();
3862
+ if (agentSpan) {
3863
+ telemetry.endSpan(agentSpan, {
3864
+ status: "ok",
3865
+ output: extractedData
3866
+ });
3867
+ }
3868
+ }
3869
+ return { data: extractedData, usage };
3870
+ } catch (error) {
3871
+ const durationMs = Date.now() - startTime;
3872
+ debug?.llmCallComplete({
3873
+ callId,
3874
+ success: false,
3875
+ inputTokens: usage.inputTokens,
3876
+ outputTokens: usage.outputTokens,
3877
+ totalTokens: usage.totalTokens,
3878
+ durationMs,
3879
+ error: error.message
3880
+ });
3881
+ if (telemetry) {
3882
+ for (const [key, span] of activeMessageSpans.entries()) {
3883
+ telemetry.endSpan(span, {
3884
+ status: "error",
3885
+ error: error instanceof Error ? error : new Error(String(error))
3886
+ });
3887
+ }
3888
+ activeMessageSpans.clear();
3889
+ for (const [key, span] of activeToolSpans.entries()) {
3890
+ telemetry.endSpan(span, {
3891
+ status: "error",
3892
+ error: error instanceof Error ? error : new Error(String(error))
3893
+ });
3894
+ }
3895
+ activeToolSpans.clear();
3896
+ if (agentSpan) {
3897
+ telemetry.endSpan(agentSpan, {
3898
+ status: "error",
3899
+ error: error instanceof Error ? error : new Error(String(error))
3900
+ });
3901
+ }
3902
+ }
3903
+ throw error;
3904
+ } finally {
3905
+ session.dispose();
3906
+ }
3907
+ }
3908
+ };
3909
+ var agent = (config) => {
3910
+ return new AgentStrategy(config);
3911
+ };
2419
3912
  export {
3913
+ AgentStrategy,
2420
3914
  DoublePassAutoMergeStrategy,
2421
3915
  DoublePassStrategy,
2422
3916
  ParallelAutoMergeStrategy,
@@ -2424,6 +3918,7 @@ export {
2424
3918
  SequentialAutoMergeStrategy,
2425
3919
  SequentialStrategy,
2426
3920
  SimpleStrategy,
3921
+ agent,
2427
3922
  doublePass,
2428
3923
  doublePassAutoMerge,
2429
3924
  parallel,