@ai-setting/roy-agent-core 1.7.53 → 1.7.56

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 (124) hide show
  1. package/dist/config/index.js +7 -7
  2. package/dist/env/agent/agent-component.d.ts +30 -0
  3. package/dist/env/agent/index.js +13 -12
  4. package/dist/env/commands/index.js +7 -7
  5. package/dist/env/context/index.js +3 -3
  6. package/dist/env/debug/index.js +7 -7
  7. package/dist/env/environment.d.ts +7 -0
  8. package/dist/env/error-reply.d.ts +184 -0
  9. package/dist/env/event-source/event-source-handlers.d.ts +9 -23
  10. package/dist/env/event-source/index.js +17 -17
  11. package/dist/env/hook/index.js +5 -5
  12. package/dist/env/index.js +41 -40
  13. package/dist/env/llm/adapter-ai-sdk.d.ts +6 -0
  14. package/dist/env/llm/adapter-pi-ai.d.ts +152 -2
  15. package/dist/env/llm/adapter.d.ts +21 -1
  16. package/dist/env/llm/index.js +10 -10
  17. package/dist/env/llm/invoke.d.ts +74 -2
  18. package/dist/env/llm/llm.d.ts +38 -0
  19. package/dist/env/llm/outbound-defense.d.ts +140 -0
  20. package/dist/env/llm/reasoning-defense.d.ts +104 -0
  21. package/dist/env/llm/types.d.ts +26 -0
  22. package/dist/env/log-trace/decorator.d.ts +46 -3
  23. package/dist/env/log-trace/index.d.ts +1 -0
  24. package/dist/env/log-trace/index.js +33 -7
  25. package/dist/env/log-trace/trace-filters.d.ts +127 -0
  26. package/dist/env/mcp/index.js +7 -7
  27. package/dist/env/memory/index.js +7 -7
  28. package/dist/env/plugin/index.js +7 -7
  29. package/dist/env/prompt/index.js +8 -8
  30. package/dist/env/prompt/prompts-index.d.ts +1 -1
  31. package/dist/env/session/index.js +11 -10
  32. package/dist/env/session/session-component.d.ts +4 -0
  33. package/dist/env/session/storage/index.js +5 -3
  34. package/dist/env/skill/index.js +7 -7
  35. package/dist/env/task/backend/index.js +0 -2
  36. package/dist/env/task/backend/types.d.ts +0 -11
  37. package/dist/env/task/delegate/index.js +8 -8
  38. package/dist/env/task/index.js +13 -13
  39. package/dist/env/task/plugins/card-chat-routing.d.ts +42 -89
  40. package/dist/env/task/plugins/index.js +10 -10
  41. package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +0 -51
  42. package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +14 -31
  43. package/dist/env/task/plugins/per-task-group-chat-plugin.d.ts +4 -4
  44. package/dist/env/task/storage/index.js +2 -2
  45. package/dist/env/task/tools/index.js +3 -3
  46. package/dist/env/tool/built-in/index.js +3 -3
  47. package/dist/env/tool/index.js +9 -9
  48. package/dist/env/workflow/engine/engine.d.ts +65 -0
  49. package/dist/env/workflow/engine/index.js +12 -12
  50. package/dist/env/workflow/hooks/index.js +7 -7
  51. package/dist/env/workflow/index.js +23 -22
  52. package/dist/env/workflow/nodes/index.js +2 -2
  53. package/dist/env/workflow/nodes/tool-node.d.ts +6 -0
  54. package/dist/env/workflow/plugins/index.js +8 -8
  55. package/dist/env/workflow/service/index.js +2 -2
  56. package/dist/env/workflow/storage/index.js +3 -3
  57. package/dist/env/workflow/tools/index.js +15 -14
  58. package/dist/env/workflow/tools/workflow-validate-tool.d.ts +11 -2
  59. package/dist/env/workflow/types/run.d.ts +19 -0
  60. package/dist/env/workflow/utils/agent-session-metadata.d.ts +13 -0
  61. package/dist/env/workflow/utils/index.js +5 -5
  62. package/dist/env/workflow/utils/redact-command-secrets.d.ts +70 -0
  63. package/dist/env/workflow/utils/shell-dialect-lint.d.ts +75 -0
  64. package/dist/env/workflow/utils/validate-workflow-definition.d.ts +22 -0
  65. package/dist/index.d.ts +1 -1
  66. package/dist/index.js +82 -56
  67. package/dist/shared/@ai-setting/{roy-agent-core-k25gv86p.js → roy-agent-core-0b3fdnn3.js} +1 -1
  68. package/dist/shared/@ai-setting/{roy-agent-core-vvxb9522.js → roy-agent-core-0bmk44ye.js} +71 -10
  69. package/dist/shared/@ai-setting/{roy-agent-core-k8b440n3.js → roy-agent-core-16t0zfbk.js} +1 -1
  70. package/dist/shared/@ai-setting/{roy-agent-core-4z3rq5gw.js → roy-agent-core-1pnfkspm.js} +2 -2
  71. package/dist/shared/@ai-setting/{roy-agent-core-na73mym1.js → roy-agent-core-202v00x4.js} +213 -2
  72. package/dist/shared/@ai-setting/{roy-agent-core-ph5qbqcx.js → roy-agent-core-2044fbsd.js} +471 -134
  73. package/dist/shared/@ai-setting/{roy-agent-core-fs36xgcs.js → roy-agent-core-3dkzqqsd.js} +1 -1
  74. package/dist/shared/@ai-setting/{roy-agent-core-qm9jda8p.js → roy-agent-core-3kz6934b.js} +1 -1
  75. package/dist/shared/@ai-setting/{roy-agent-core-tjg71zqp.js → roy-agent-core-3mc6vmvj.js} +1 -1
  76. package/dist/shared/@ai-setting/{roy-agent-core-tdbv7ff4.js → roy-agent-core-40xzbjtm.js} +2 -2
  77. package/dist/shared/@ai-setting/{roy-agent-core-athdr2m0.js → roy-agent-core-4zy2v3br.js} +10 -2
  78. package/dist/shared/@ai-setting/{roy-agent-core-t37wbysj.js → roy-agent-core-5gw6w424.js} +4 -4
  79. package/dist/shared/@ai-setting/{roy-agent-core-978b6wfx.js → roy-agent-core-5psjwd08.js} +1 -1
  80. package/dist/shared/@ai-setting/{roy-agent-core-7evvpfwc.js → roy-agent-core-5rxs0ra3.js} +1 -1
  81. package/dist/shared/@ai-setting/{roy-agent-core-gdsqp6j8.js → roy-agent-core-6masakz4.js} +45 -0
  82. package/dist/shared/@ai-setting/{roy-agent-core-stnddvx9.js → roy-agent-core-6qgq7h1g.js} +4 -4
  83. package/dist/shared/@ai-setting/{roy-agent-core-6167phd4.js → roy-agent-core-833t16n1.js} +3 -3
  84. package/dist/shared/@ai-setting/{roy-agent-core-2k4xffaa.js → roy-agent-core-8cgtwytv.js} +3 -3
  85. package/dist/shared/@ai-setting/{roy-agent-core-rx16h2m1.js → roy-agent-core-97eyghsp.js} +2 -2
  86. package/dist/shared/@ai-setting/{roy-agent-core-fvs48d38.js → roy-agent-core-98aa9r0x.js} +12 -7
  87. package/dist/shared/@ai-setting/{roy-agent-core-3tv1q6f2.js → roy-agent-core-9nv586rw.js} +57 -19
  88. package/dist/shared/@ai-setting/roy-agent-core-b599jgyd.js +578 -0
  89. package/dist/shared/@ai-setting/{roy-agent-core-yd21n6c7.js → roy-agent-core-bwzy00t2.js} +12 -5
  90. package/dist/shared/@ai-setting/{roy-agent-core-dwx9hjwx.js → roy-agent-core-c6at31ra.js} +1 -1
  91. package/dist/shared/@ai-setting/{roy-agent-core-1cv9j4dg.js → roy-agent-core-d14h726p.js} +3 -3
  92. package/dist/shared/@ai-setting/{roy-agent-core-4sx6sdb2.js → roy-agent-core-d4jpn06f.js} +22 -9
  93. package/dist/shared/@ai-setting/{roy-agent-core-39xejr39.js → roy-agent-core-e0wbq0qq.js} +7 -7
  94. package/dist/shared/@ai-setting/{roy-agent-core-vrsxhw9e.js → roy-agent-core-e677am8e.js} +1 -1
  95. package/dist/shared/@ai-setting/{roy-agent-core-hch2geta.js → roy-agent-core-ezsyyjxw.js} +21 -190
  96. package/dist/shared/@ai-setting/{roy-agent-core-ne2t1pay.js → roy-agent-core-f8sp1txg.js} +2 -2
  97. package/dist/shared/@ai-setting/{roy-agent-core-ak4dc3kg.js → roy-agent-core-gn8f4rhs.js} +1 -1
  98. package/dist/shared/@ai-setting/{roy-agent-core-qcspfszy.js → roy-agent-core-gngjkw3b.js} +1 -1
  99. package/dist/shared/@ai-setting/{roy-agent-core-9jzysq50.js → roy-agent-core-heft3j4c.js} +2 -2
  100. package/dist/shared/@ai-setting/{roy-agent-core-5apmk4qw.js → roy-agent-core-jszgaevt.js} +1 -1
  101. package/dist/shared/@ai-setting/{roy-agent-core-nde5gzta.js → roy-agent-core-m8n0dffe.js} +1 -1
  102. package/dist/shared/@ai-setting/{roy-agent-core-x8n1r7h4.js → roy-agent-core-n3b7ztms.js} +189 -8
  103. package/dist/shared/@ai-setting/{roy-agent-core-4vpk40ss.js → roy-agent-core-nhk0dsxr.js} +13 -13
  104. package/dist/shared/@ai-setting/{roy-agent-core-j8e92gj8.js → roy-agent-core-nntphgww.js} +1 -60
  105. package/dist/shared/@ai-setting/{roy-agent-core-8s9jd2qy.js → roy-agent-core-nra8vfrs.js} +1 -1
  106. package/dist/shared/@ai-setting/{roy-agent-core-pk1b3kvf.js → roy-agent-core-ns3pbyz4.js} +1 -1
  107. package/dist/shared/@ai-setting/{roy-agent-core-7g03zkkb.js → roy-agent-core-pemxr5n4.js} +1 -1
  108. package/dist/shared/@ai-setting/{roy-agent-core-skzjs973.js → roy-agent-core-pjxa78mp.js} +5 -5
  109. package/dist/shared/@ai-setting/{roy-agent-core-p8ak03s0.js → roy-agent-core-q7zksbw0.js} +117 -315
  110. package/dist/shared/@ai-setting/{roy-agent-core-zd3ddm4k.js → roy-agent-core-qjjf4tjb.js} +1 -1
  111. package/dist/shared/@ai-setting/{roy-agent-core-qamktze3.js → roy-agent-core-qyqnmz8c.js} +1 -1
  112. package/dist/shared/@ai-setting/{roy-agent-core-9csa89jx.js → roy-agent-core-r9qe0ghe.js} +2 -2
  113. package/dist/shared/@ai-setting/{roy-agent-core-h93v6p6y.js → roy-agent-core-tc7gw38m.js} +2 -2
  114. package/dist/shared/@ai-setting/{roy-agent-core-35s1w7tc.js → roy-agent-core-v2188rnk.js} +157 -85
  115. package/dist/shared/@ai-setting/{roy-agent-core-p2r27era.js → roy-agent-core-vmg30n5x.js} +8 -8
  116. package/dist/shared/@ai-setting/{roy-agent-core-rnwr39b0.js → roy-agent-core-w7hnjhvx.js} +5 -3
  117. package/dist/shared/@ai-setting/{roy-agent-core-dv714bqh.js → roy-agent-core-wafk25ff.js} +1 -1
  118. package/dist/shared/@ai-setting/{roy-agent-core-brwzs8wf.js → roy-agent-core-xcq04fq0.js} +5 -5
  119. package/dist/shared/@ai-setting/{roy-agent-core-2gay3sjt.js → roy-agent-core-yg247mk4.js} +1 -1
  120. package/dist/shared/@ai-setting/{roy-agent-core-zy3hqfwg.js → roy-agent-core-yv9x4wmg.js} +631 -35
  121. package/dist/shared/@ai-setting/{roy-agent-core-yhsssn86.js → roy-agent-core-zmeka63k.js} +1 -1
  122. package/dist/test-utils/child-process-mock.d.ts +73 -0
  123. package/package.json +2 -2
  124. package/dist/shared/@ai-setting/roy-agent-core-dsevjbck.js +0 -245
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  isValidSessionId
3
- } from "./roy-agent-core-yd21n6c7.js";
3
+ } from "./roy-agent-core-bwzy00t2.js";
4
4
  import {
5
5
  TAG_MAX_COUNT,
6
6
  TAG_MIN_COUNT
7
- } from "./roy-agent-core-7evvpfwc.js";
7
+ } from "./roy-agent-core-5rxs0ra3.js";
8
8
  import {
9
9
  validateWorkflowDefinition
10
- } from "./roy-agent-core-na73mym1.js";
10
+ } from "./roy-agent-core-202v00x4.js";
11
11
  import {
12
12
  getCurrentBgTaskId
13
13
  } from "./roy-agent-core-c1v263jn.js";
@@ -18,13 +18,14 @@ import {
18
18
  import {
19
19
  TracedAs,
20
20
  init_decorator
21
- } from "./roy-agent-core-dsevjbck.js";
21
+ } from "./roy-agent-core-b599jgyd.js";
22
22
  import {
23
23
  createLogger,
24
24
  init_logger
25
25
  } from "./roy-agent-core-8seq65bp.js";
26
26
  import {
27
- __legacyDecorateClassTS
27
+ __legacyDecorateClassTS,
28
+ __require
28
29
  } from "./roy-agent-core-fs0mn2jk.js";
29
30
 
30
31
  // src/env/workflow/tools/ask-user-tool.ts
@@ -480,8 +481,25 @@ var logger5 = createLogger("workflow-validate-tool");
480
481
  var WorkflowValidateInputSchema = z6.object({
481
482
  definition: z6.any().optional().describe("Workflow definition object (alternative to definition_yaml)"),
482
483
  definition_yaml: z6.string().optional().describe("Workflow definition as YAML or JSON string"),
483
- yaml: z6.string().optional().describe("Alias for definition_yaml — workflow definition as YAML or JSON string")
484
+ yaml: z6.string().optional().describe("Alias for definition_yaml — workflow definition as YAML or JSON string"),
485
+ file: z6.string().optional().describe("Path to a YAML/JSON file to validate. Use this INSTEAD of passing " + "`$(cat /path)` — shell substitutions are NOT expanded by this tool.")
484
486
  });
487
+ function isAllowedPath(absPath) {
488
+ const cwd = process.cwd();
489
+ const normalized = absPath.replace(/\\/g, "/");
490
+ if (normalized === cwd || normalized.startsWith(`${cwd}/`))
491
+ return true;
492
+ if (normalized === "/tmp" || normalized.startsWith("/tmp/"))
493
+ return true;
494
+ return false;
495
+ }
496
+ function detectShellSubstitution(text) {
497
+ const trimmed = text.trim();
498
+ const looksLikeSubstitution = /^\$\(.*\)$/.test(trimmed) || /^`.*`$/.test(trimmed) || /\$\(\s*cat\s+/.test(trimmed) || /`\s*cat\s+/.test(trimmed);
499
+ if (!looksLikeSubstitution)
500
+ return;
501
+ return "Shell substitutions such as `$(cat /path)` are NOT expanded by this tool — the " + "string is passed to the YAML parser verbatim, which is why you saw " + "`Parsed definition is not an object`. Pass the path directly instead: " + 'workflow_validate({ file: "/tmp/x.yaml" }).';
502
+ }
485
503
  function createWorkflowValidateTool(_workflowService) {
486
504
  return {
487
505
  name: "workflow_validate",
@@ -493,13 +511,56 @@ function createWorkflowValidateTool(_workflowService) {
493
511
  let definition;
494
512
  if (params.definition && typeof params.definition === "object") {
495
513
  definition = params.definition;
514
+ } else if (params.file && typeof params.file === "string") {
515
+ const fs = await import("node:fs/promises");
516
+ const nodePath = await import("node:path");
517
+ const abs = nodePath.resolve(params.file);
518
+ if (!isAllowedPath(abs)) {
519
+ return {
520
+ success: false,
521
+ output: "",
522
+ error: `Refusing to read outside the working directory or /tmp: ${abs}. Copy the file into CWD or /tmp first.`,
523
+ metadata: { execution_time_ms: 0 }
524
+ };
525
+ }
526
+ let raw;
527
+ try {
528
+ raw = await fs.readFile(abs, "utf-8");
529
+ } catch (readErr) {
530
+ const code = readErr?.code;
531
+ return {
532
+ success: false,
533
+ output: "",
534
+ error: code === "ENOENT" ? `Workflow file not found: ${abs}. Check the path (it is resolved against CWD: ${process.cwd()}).` : `Failed to read workflow file ${abs}: ${readErr instanceof Error ? readErr.message : String(readErr)}`,
535
+ metadata: { execution_time_ms: 0 }
536
+ };
537
+ }
538
+ try {
539
+ definition = yaml2.parse(raw);
540
+ } catch (yamlErr) {
541
+ return {
542
+ success: false,
543
+ output: "",
544
+ error: `Invalid YAML in ${abs}: ${yamlErr instanceof Error ? yamlErr.message : String(yamlErr)}`,
545
+ metadata: { execution_time_ms: 0 }
546
+ };
547
+ }
496
548
  } else {
497
549
  const yamlText = params.yaml ?? params.definition_yaml;
498
550
  if (!yamlText || typeof yamlText !== "string") {
499
551
  return {
500
552
  success: false,
501
553
  output: "",
502
- error: "Must provide either 'definition' (object), 'definition_yaml', or 'yaml' (string)",
554
+ error: "Must provide either 'definition' (object), 'file' (path), 'definition_yaml', or 'yaml' (string)",
555
+ metadata: { execution_time_ms: 0 }
556
+ };
557
+ }
558
+ const substitutionHint = detectShellSubstitution(yamlText);
559
+ if (substitutionHint) {
560
+ return {
561
+ success: false,
562
+ output: "",
563
+ error: substitutionHint,
503
564
  metadata: { execution_time_ms: 0 }
504
565
  };
505
566
  }
@@ -518,7 +579,7 @@ function createWorkflowValidateTool(_workflowService) {
518
579
  return {
519
580
  success: false,
520
581
  output: "",
521
- error: "Parsed definition is not an object",
582
+ error: `Parsed definition is not an object (got ${definition === null ? "null" : typeof definition}). If you passed a file path as a string, use workflow_validate({ file: "/path/to.yaml" }) ` + "instead of embedding `$(cat ...)` — shell substitutions are not expanded here.",
522
583
  metadata: { execution_time_ms: 0 }
523
584
  };
524
585
  }
@@ -803,7 +864,7 @@ class RunWorkflowToolRunner {
803
864
  __legacyDecorateClassTS([
804
865
  TracedAs("workflow.run-workflow-tool.execute", {
805
866
  recordParams: true,
806
- recordResult: true
867
+ recordResult: false
807
868
  })
808
869
  ], RunWorkflowToolRunner.prototype, "execute", null);
809
870
 
@@ -845,7 +906,7 @@ class RunWorkflowLifecycleTracer {
845
906
  __legacyDecorateClassTS([
846
907
  TracedAs("workflow.run-tool.pre-attach", {
847
908
  recordParams: true,
848
- recordResult: true
909
+ recordResult: false
849
910
  })
850
911
  ], RunWorkflowLifecycleTracer.prototype, "execute", null);
851
912
  function createRunWorkflowTool(workflowService, hooks) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  init_env_context,
3
3
  init_env_event
4
- } from "./roy-agent-core-5apmk4qw.js";
4
+ } from "./roy-agent-core-jszgaevt.js";
5
5
  import {
6
6
  __esm
7
7
  } from "./roy-agent-core-fs0mn2jk.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  init_context
3
- } from "./roy-agent-core-k8b440n3.js";
3
+ } from "./roy-agent-core-16t0zfbk.js";
4
4
  import {
5
5
  getEnvContext
6
- } from "./roy-agent-core-5apmk4qw.js";
6
+ } from "./roy-agent-core-jszgaevt.js";
7
7
  import {
8
8
  __esm
9
9
  } from "./roy-agent-core-fs0mn2jk.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DAGManager,
3
3
  init_dag_manager
4
- } from "./roy-agent-core-8s9jd2qy.js";
4
+ } from "./roy-agent-core-nra8vfrs.js";
5
5
 
6
6
  // src/env/workflow/utils/node-output-contracts.ts
7
7
  var VIRTUAL_OUTPUT_FIELD = "output";
@@ -315,6 +315,167 @@ function formatTemplateContractFindings(findings) {
315
315
 
316
316
  // src/env/workflow/utils/validate-workflow-definition.ts
317
317
  init_dag_manager();
318
+
319
+ // src/env/workflow/utils/shell-dialect-lint.ts
320
+ var RULES = [
321
+ {
322
+ name: "pipefail",
323
+ re: /\bset\s+(?:-[A-Za-z]+\s+)*-?[A-Za-z]*o[A-Za-z]*\s+pipefail\b/,
324
+ fix: "dash 不支持 set -o pipefail(含 set -euo pipefail)。改用落文件再取退出码: " + "`cmd > /tmp/o 2>&1; RC=$?; cat /tmp/o; exit $RC`"
325
+ },
326
+ {
327
+ name: "PIPESTATUS",
328
+ re: /\$\{PIPESTATUS\[/,
329
+ fix: "dash 不支持 ${PIPESTATUS[n]}。改用把管道左端输出落文件,再单独取 $?: " + "`cmd > /tmp/o 2>&1; RC=$?`"
330
+ },
331
+ {
332
+ name: "substring-replace",
333
+ re: /\$\{[A-Za-z_][A-Za-z0-9_]*\/\//,
334
+ fix: "dash 不支持 ${VAR//a/b}。改用 tr 或 sed: " + '`V=$(echo "$V" | tr -d ".")`'
335
+ },
336
+ {
337
+ name: "BASH_SOURCE",
338
+ re: /\$\{BASH_SOURCE/,
339
+ fix: "dash 不支持 ${BASH_SOURCE[0]}。改用 $0(或显式写绝对路径)"
340
+ },
341
+ {
342
+ name: "process-substitution",
343
+ re: /(?:^|[\s;&|(])[<>]\([^)]/,
344
+ fix: "dash 不支持进程替换 <(cmd) / >(cmd)。改用显式 bash 或临时文件: " + "`bash -c 'diff <(a) <(b)'` 或 `a > /tmp/a; b > /tmp/b; diff /tmp/a /tmp/b`"
345
+ },
346
+ {
347
+ name: "here-string",
348
+ re: /(?<!<)<<<\s*[^\s<]/,
349
+ fix: "dash 不支持 here-string <<<。改用 printf 管道: " + '`printf "%s\\n" "$X" | cmd`'
350
+ },
351
+ {
352
+ name: "ansi-c-quote",
353
+ re: /\$'(?:[^'\\]|\\.)*\\/,
354
+ fix: "dash 不支持 ANSI-C 转义 $'...'(会静默保留反斜杠)。改用 printf: " + '`printf "a\\tb\\n"`'
355
+ },
356
+ {
357
+ name: "dbl-bracket",
358
+ re: /\[\[(?=[\s\-!$\u0027])/,
359
+ fix: 'dash 不支持 [[ ]]。改用 [ ](注意 quote 变量: `[ -f "$F" ]`)'
360
+ },
361
+ {
362
+ name: "source",
363
+ re: /(?:^|[\s;&|(])source\s+(?:~|\$|\.|\/)[^\s;|&]*|(?:^|[\s;&|(])source\s+[A-Za-z0-9_.-]+\.sh\b/,
364
+ fix: "dash 不支持 source。改用 POSIX 点命令: `. <file>`"
365
+ },
366
+ {
367
+ name: "array",
368
+ re: /(^|[\s;&|(])([A-Za-z_][A-Za-z0-9_]*)=\(/,
369
+ fix: "dash 不支持数组。改用空格分隔的字符串 + for 循环: " + '`PYS="3.12 3.13"; for p in $PYS; do ...; done`'
370
+ },
371
+ {
372
+ name: "function-keyword",
373
+ re: /(^|\n)[ \t]*function\s+[A-Za-z_]/,
374
+ fix: "dash 不支持 function 关键字。改用 `name() { ... }`"
375
+ },
376
+ {
377
+ name: "RANDOM",
378
+ re: /\$RANDOM\b/,
379
+ fix: "dash 不支持 $RANDOM(静默为空)。改用 " + "`awk -v s=$(date +%s%N) 'BEGIN{srand(s); print int(rand()*32768)}'`"
380
+ },
381
+ {
382
+ name: "echo-e",
383
+ re: /(^|[\s;&|(])echo\s+-[ne]*e[A-Za-z]*\b/,
384
+ fix: 'dash 的 echo 不解析 -e(会输出字面 "-e")。改用 printf: `printf "%s\\n" "$X"`'
385
+ }
386
+ ];
387
+ function stripComments(command) {
388
+ const lines = command.split(`
389
+ `);
390
+ const out = [];
391
+ for (const line of lines) {
392
+ let inSingle = false;
393
+ let inDouble = false;
394
+ let cut = -1;
395
+ for (let i = 0;i < line.length; i++) {
396
+ const ch = line[i];
397
+ if (ch === "\\" && !inSingle) {
398
+ i++;
399
+ continue;
400
+ }
401
+ if (ch === "'" && !inDouble) {
402
+ inSingle = !inSingle;
403
+ continue;
404
+ }
405
+ if (ch === '"' && !inSingle) {
406
+ inDouble = !inDouble;
407
+ continue;
408
+ }
409
+ if (ch === "#" && !inSingle && !inDouble) {
410
+ const prev = i === 0 ? " " : line[i - 1];
411
+ if (/\s/.test(prev)) {
412
+ cut = i;
413
+ break;
414
+ }
415
+ }
416
+ }
417
+ out.push(cut >= 0 ? line.slice(0, cut) : line);
418
+ }
419
+ return out.join(`
420
+ `);
421
+ }
422
+ function stripQuotedHeredocBodies(command) {
423
+ const lines = command.split(`
424
+ `);
425
+ const out = [];
426
+ let delim = null;
427
+ for (const line of lines) {
428
+ if (delim !== null) {
429
+ if (line.trim() === delim) {
430
+ out.push(line);
431
+ delim = null;
432
+ }
433
+ continue;
434
+ }
435
+ const m = /<<-?\s*(?:'([^']+)'|"([^"]+)")/.exec(line);
436
+ out.push(line);
437
+ if (m)
438
+ delim = (m[1] ?? m[2] ?? "").trim();
439
+ }
440
+ return out.join(`
441
+ `);
442
+ }
443
+ function stripForeignShellBodies(command) {
444
+ const re = /(\b(?:bash|sh)\s+-[a-z]*c[a-z]*\s+)(?:'[\s\S]*?'|"[\s\S]*?")/g;
445
+ return command.replace(re, "$1<foreign-shell-body>");
446
+ }
447
+ function isEntirelyBashInvocation(command) {
448
+ const re = /^(?:[A-Za-z_][A-Za-z0-9_]*=[^\s;|&]*\s+)*\/?(?:[\w./-]*\/)?bash\s+-(?:[a-z]*c[a-z]*)\s+(?:'[\s\S]*'|"[\s\S]*")\s*$/;
449
+ return re.test(command.trim());
450
+ }
451
+ function lintShellDialect(command) {
452
+ if (!command || typeof command !== "string")
453
+ return [];
454
+ const scannable = stripComments(stripForeignShellBodies(stripQuotedHeredocBodies(command)));
455
+ if (scannable.trim().length === 0)
456
+ return [];
457
+ if (isEntirelyBashInvocation(scannable))
458
+ return [];
459
+ const findings = [];
460
+ const seen = new Set;
461
+ for (const rule of RULES) {
462
+ if (seen.has(rule.name))
463
+ continue;
464
+ const m = rule.re.exec(scannable);
465
+ if (!m)
466
+ continue;
467
+ seen.add(rule.name);
468
+ const start = Math.max(0, m.index - 30);
469
+ const excerpt = scannable.slice(start, m.index + m[0].length + 40).replace(/\s+/g, " ").trim();
470
+ findings.push({ name: rule.name, fix: rule.fix, excerpt });
471
+ }
472
+ return findings;
473
+ }
474
+ function formatShellDialectFindings(nodeId, findings) {
475
+ return findings.map((f) => `[shell-dialect] node "${nodeId}" uses bash-only "${f.name}": ${f.fix} | near: ${f.excerpt}`);
476
+ }
477
+
478
+ // src/env/workflow/utils/validate-workflow-definition.ts
318
479
  function validateWorkflowDefinition(definition, options = {}) {
319
480
  if (!definition || typeof definition !== "object") {
320
481
  return {
@@ -352,8 +513,17 @@ function validateWorkflowDefinition(definition, options = {}) {
352
513
  const contractErrors = findings.filter((f) => f.severity === "error");
353
514
  const contractWarnings = findings.filter((f) => f.severity === "warning");
354
515
  const contractInfos = findings.filter((f) => f.severity === "info");
516
+ const dialectWarnings = [];
517
+ if (structural.valid && options.lintShellDialect !== false) {
518
+ try {
519
+ dialectWarnings.push(...collectShellDialectWarnings(definition));
520
+ } catch {}
521
+ }
355
522
  const errors = [...structural.errors, ...formatTemplateContractFindings(contractErrors)];
356
- const warnings = formatTemplateContractFindings(contractWarnings);
523
+ const warnings = [
524
+ ...formatTemplateContractFindings(contractWarnings),
525
+ ...dialectWarnings
526
+ ];
357
527
  const infos = formatTemplateContractFindings(contractInfos);
358
528
  return {
359
529
  valid: structural.valid && contractErrors.length === 0,
@@ -363,5 +533,46 @@ function validateWorkflowDefinition(definition, options = {}) {
363
533
  ...findings.length > 0 ? { findings } : {}
364
534
  };
365
535
  }
536
+ function extractToolCommands(config) {
537
+ if (!config || typeof config !== "object")
538
+ return [];
539
+ const out = [];
540
+ const COMMAND_KEYS = ["command", "cmd"];
541
+ const CONTAINER_KEYS = ["input", "inputs", "args"];
542
+ for (const key of COMMAND_KEYS) {
543
+ const v = config[key];
544
+ if (typeof v === "string")
545
+ out.push(v);
546
+ }
547
+ for (const key of CONTAINER_KEYS) {
548
+ const container = config[key];
549
+ if (!container || typeof container !== "object" || Array.isArray(container))
550
+ continue;
551
+ for (const cmdKey of COMMAND_KEYS) {
552
+ const v = container[cmdKey];
553
+ if (typeof v === "string")
554
+ out.push(v);
555
+ }
556
+ }
557
+ return out;
558
+ }
559
+ function collectShellDialectWarnings(definition) {
560
+ const def = definition;
561
+ if (!def || !Array.isArray(def.nodes))
562
+ return [];
563
+ const warnings = [];
564
+ for (const node of def.nodes) {
565
+ if (node?.type !== "tool")
566
+ continue;
567
+ const nodeId = typeof node.id === "string" ? node.id : String(node.id ?? "<unnamed>");
568
+ for (const command of extractToolCommands(node.config)) {
569
+ const findings = lintShellDialect(command);
570
+ if (findings.length > 0) {
571
+ warnings.push(...formatShellDialectFindings(nodeId, findings));
572
+ }
573
+ }
574
+ }
575
+ return warnings;
576
+ }
366
577
 
367
578
  export { VIRTUAL_OUTPUT_FIELD, NODE_OUTPUT_CONTRACTS, getNodeOutputContract, hasClosedOutputContract, RESULT_INVALID_NODE_TYPES, getAgentDeclaredJsonFields, validateTemplateContracts, getTemplateContractErrors, getTemplateContractWarnings, formatTemplateContractFindings, validateWorkflowDefinition };