bosun 0.41.2 → 0.41.3

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 (71) hide show
  1. package/.env.example +1 -1
  2. package/agent/agent-prompt-catalog.mjs +971 -0
  3. package/agent/agent-prompts.mjs +2 -970
  4. package/agent/agent-supervisor.mjs +6 -3
  5. package/agent/autofix-git.mjs +33 -0
  6. package/agent/autofix-prompts.mjs +151 -0
  7. package/agent/autofix.mjs +11 -175
  8. package/agent/bosun-skills.mjs +3 -2
  9. package/bosun.config.example.json +17 -0
  10. package/bosun.schema.json +87 -188
  11. package/cli.mjs +34 -1
  12. package/config/config-doctor.mjs +5 -250
  13. package/config/config-file-names.mjs +5 -0
  14. package/config/config.mjs +89 -493
  15. package/config/executor-config.mjs +493 -0
  16. package/config/repo-root.mjs +1 -2
  17. package/config/workspace-health.mjs +242 -0
  18. package/git/git-safety.mjs +15 -0
  19. package/github/github-oauth-portal.mjs +46 -0
  20. package/infra/library-manager-utils.mjs +22 -0
  21. package/infra/library-manager-well-known-sources.mjs +578 -0
  22. package/infra/library-manager.mjs +512 -1030
  23. package/infra/monitor.mjs +28 -9
  24. package/infra/session-tracker.mjs +10 -7
  25. package/kanban/kanban-adapter.mjs +17 -1
  26. package/lib/codebase-audit-manifests.mjs +117 -0
  27. package/lib/codebase-audit.mjs +18 -115
  28. package/package.json +18 -3
  29. package/server/ui-server.mjs +1194 -79
  30. package/shell/codex-config-file.mjs +178 -0
  31. package/shell/codex-config.mjs +538 -575
  32. package/task/task-cli.mjs +54 -3
  33. package/task/task-executor.mjs +143 -13
  34. package/task/task-store.mjs +409 -1
  35. package/telegram/telegram-bot.mjs +127 -0
  36. package/tools/apply-pr-suggestions.mjs +401 -0
  37. package/tools/syntax-check.mjs +21 -9
  38. package/ui/app.js +3 -14
  39. package/ui/components/kanban-board.js +227 -4
  40. package/ui/components/session-list.js +85 -5
  41. package/ui/demo-defaults.js +334 -80
  42. package/ui/demo.html +155 -0
  43. package/ui/modules/session-api.js +96 -0
  44. package/ui/modules/settings-schema.js +1 -2
  45. package/ui/modules/state.js +21 -3
  46. package/ui/setup.html +4 -5
  47. package/ui/styles/components.css +58 -4
  48. package/ui/tabs/agents.js +12 -15
  49. package/ui/tabs/control.js +1 -0
  50. package/ui/tabs/library.js +484 -22
  51. package/ui/tabs/manual-flows.js +105 -29
  52. package/ui/tabs/tasks.js +785 -140
  53. package/ui/tabs/telemetry.js +129 -11
  54. package/ui/tabs/workflow-canvas-utils.mjs +130 -0
  55. package/ui/tabs/workflows.js +293 -23
  56. package/voice/voice-tool-definitions.mjs +757 -0
  57. package/voice/voice-tools.mjs +34 -778
  58. package/workflow/manual-flow-audit.mjs +165 -0
  59. package/workflow/manual-flows.mjs +164 -259
  60. package/workflow/workflow-engine.mjs +147 -58
  61. package/workflow/workflow-nodes/definitions.mjs +1207 -0
  62. package/workflow/workflow-nodes/transforms.mjs +612 -0
  63. package/workflow/workflow-nodes.mjs +304 -52
  64. package/workflow/workflow-templates.mjs +313 -191
  65. package/workflow-templates/_helpers.mjs +154 -0
  66. package/workflow-templates/agents.mjs +61 -4
  67. package/workflow-templates/code-quality.mjs +7 -7
  68. package/workflow-templates/github.mjs +20 -10
  69. package/workflow-templates/task-batch.mjs +20 -9
  70. package/workflow-templates/task-lifecycle.mjs +31 -6
  71. package/workspace/worktree-manager.mjs +277 -3
@@ -14,6 +14,7 @@ import { resolve as resolvePath } from "node:path";
14
14
  import { randomUUID } from "node:crypto";
15
15
  import { resolveAgentRepoRoot } from "../config/repo-root.mjs";
16
16
  import { getVisionSessionState } from "./vision-session-state.mjs";
17
+ import { TOOL_DEFS } from "./voice-tool-definitions.mjs";
17
18
 
18
19
  // ── Voice response shaping (inspired by claude-phone VOICE_CONTEXT pattern) ──
19
20
 
@@ -31,6 +32,31 @@ For code results or lists, summarise the key finding verbally instead of dumping
31
32
 
32
33
  `;
33
34
 
35
+ /**
36
+ * Regular expressions for commands that are considered safe to execute
37
+ * directly in the workspace shell. Shared so they can be maintained and
38
+ * potentially tested independently of the call site.
39
+ */
40
+ const SAFE_WORKSPACE_COMMAND_PATTERNS = [
41
+ /^git\s+(status|log|diff\s|show\s|branch|remote|tag|stash\s+list|ls-files|rev-parse|shortlog|describe)\b/i,
42
+ /^git\s+diff$/i,
43
+ /^git\s+log$/i,
44
+ /^npm\s+(test|run\s+(test|lint|build|check)|ls|audit)\b/i,
45
+ /^node\s+(--version|-v)$/i,
46
+ /^npm\s+(--version|-v)$/i,
47
+ /^ls(\s|$)/i,
48
+ /^cat\s+/i,
49
+ /^head\s+/i,
50
+ /^tail(\s|$)/i,
51
+ /^wc(\s|$)/i,
52
+ /^find\s+/i,
53
+ /^grep(\s|$)/i,
54
+ /^echo\s+/i,
55
+ /^pwd$/i,
56
+ /^which\s+/i,
57
+ /^type\s+/i,
58
+ ];
59
+
34
60
  /**
35
61
  * Extract the TTS-ready fragment from an agent response.
36
62
  *
@@ -59,7 +85,7 @@ function formatVoiceToolResult(text) {
59
85
  .replace(/\n/g, " ")
60
86
  .trim();
61
87
 
62
- // 3) Truncate to 100 words for spoken clarity
88
+ // 3) Truncate to a maximum of 100 words for spoken clarity
63
89
  const words = stripped.split(/\s+/);
64
90
  if (words.length <= 100) return stripped;
65
91
  return words.slice(0, 100).join(" ") + "…";
@@ -519,763 +545,7 @@ function isPrivilegedVoiceContext(context = {}) {
519
545
 
520
546
  // ── Tool Definitions (OpenAI function-calling format) ────────────────────────
521
547
 
522
- const TOOL_DEFS = [
523
- // ── Workspace Tools ──
524
- {
525
- type: "function",
526
- name: "list_tasks",
527
- description: "List tasks from the kanban board. Returns task IDs, titles, status, and assignees.",
528
- parameters: {
529
- type: "object",
530
- properties: {
531
- status: {
532
- type: "string",
533
- enum: ["todo", "inprogress", "inreview", "done", "cancelled", "all"],
534
- description: "Filter by task status. Default: all",
535
- },
536
- limit: {
537
- type: "number",
538
- description: "Max number of tasks to return. Default: 20",
539
- },
540
- },
541
- },
542
- },
543
- {
544
- type: "function",
545
- name: "get_task",
546
- description: "Get detailed information about a specific task by ID or number.",
547
- parameters: {
548
- type: "object",
549
- properties: {
550
- taskId: { type: "string", description: "Task ID or issue number" },
551
- },
552
- required: ["taskId"],
553
- },
554
- },
555
- {
556
- type: "function",
557
- name: "create_task",
558
- description: "Create a new task on the kanban board.",
559
- parameters: {
560
- type: "object",
561
- properties: {
562
- title: { type: "string", description: "Task title" },
563
- description: { type: "string", description: "Task description/body" },
564
- priority: { type: "string", enum: ["low", "medium", "high", "critical"] },
565
- labels: {
566
- type: "array",
567
- items: { type: "string" },
568
- description: "Labels to apply",
569
- },
570
- },
571
- required: ["title"],
572
- },
573
- },
574
- {
575
- type: "function",
576
- name: "update_task_status",
577
- description: "Update the status of a task (move between columns).",
578
- parameters: {
579
- type: "object",
580
- properties: {
581
- taskId: { type: "string", description: "Task ID or issue number" },
582
- status: {
583
- type: "string",
584
- enum: ["todo", "inprogress", "inreview", "done", "cancelled"],
585
- },
586
- },
587
- required: ["taskId", "status"],
588
- },
589
- },
590
- // ── Agent Tools ──
591
- {
592
- type: "function",
593
- name: "delegate_to_agent",
594
- description: "Execute a CODE MODIFICATION task via a coding agent (codex, copilot, claude, gemini, or opencode). Creates a live session for writing code, modifying files, creating PRs, or running multi-step workflows. Do NOT use this for questions, status checks, or information retrieval — use ask_agent_context or direct tools instead. Only use when the user explicitly asks to write, fix, create, implement, refactor, or deploy code.",
595
- parameters: {
596
- type: "object",
597
- properties: {
598
- message: {
599
- type: "string",
600
- description: "The instruction to send to the agent. Be specific and detailed.",
601
- },
602
- executor: {
603
- type: "string",
604
- enum: ["codex-sdk", "copilot-sdk", "claude-sdk", "gemini-sdk", "opencode-sdk"],
605
- description: "Which agent to use. Defaults to the configured primary agent.",
606
- },
607
- mode: {
608
- type: "string",
609
- enum: ["ask", "agent", "plan", "code", "architect"],
610
- description: "Agent mode: code (make changes), ask (read-only), architect (plan). Default: code",
611
- },
612
- model: {
613
- type: "string",
614
- description: "Optional model override for the delegated call.",
615
- },
616
- },
617
- required: ["message"],
618
- },
619
- },
620
- {
621
- type: "function",
622
- name: "ask_agent_context",
623
- description: "Ask the coding agent a quick question in ask/instant mode and return the answer in this voice turn. Use for context, project understanding, debugging questions, and fast reasoning that needs workspace awareness.",
624
- parameters: {
625
- type: "object",
626
- properties: {
627
- message: {
628
- type: "string",
629
- description: "Question or instruction for the agent.",
630
- },
631
- mode: {
632
- type: "string",
633
- enum: ["ask", "instant"],
634
- description: "Low-latency query mode. Default: instant",
635
- },
636
- model: {
637
- type: "string",
638
- description: "Optional model override for this quick query.",
639
- },
640
- },
641
- required: ["message"],
642
- },
643
- },
644
- {
645
- type: "function",
646
- name: "get_agent_status",
647
- description: "Get the current status of the active coding agent (busy, idle, session info).",
648
- parameters: { type: "object", properties: {} },
649
- },
650
- {
651
- type: "function",
652
- name: "switch_agent",
653
- description: "Switch the active primary agent to a different executor.",
654
- parameters: {
655
- type: "object",
656
- properties: {
657
- executor: {
658
- type: "string",
659
- enum: ["codex-sdk", "copilot-sdk", "claude-sdk", "gemini-sdk", "opencode-sdk"],
660
- description: "The executor to switch to",
661
- },
662
- },
663
- required: ["executor"],
664
- },
665
- },
666
- // ── Session Tools ──
667
- {
668
- type: "function",
669
- name: "list_sessions",
670
- description: "List active and historical chat/agent sessions with metadata. Returns session summaries (not full transcripts) for fast browsing. Use get_session_history with fullTranscript=true for complete message text.",
671
- parameters: {
672
- type: "object",
673
- properties: {
674
- limit: { type: "number", description: "Max sessions per page. Default: 10" },
675
- page: { type: "number", description: "Page number for pagination. Default: 1" },
676
- includeHistory: { type: "boolean", description: "Include completed/archived sessions. Default: true" },
677
- },
678
- },
679
- },
680
- {
681
- type: "function",
682
- name: "get_session_history",
683
- description: "Get the recent message history from a session. Returns metadata-first (truncated content) by default. Set fullTranscript=true for complete message text.",
684
- parameters: {
685
- type: "object",
686
- properties: {
687
- sessionId: { type: "string", description: "Session ID to retrieve" },
688
- limit: { type: "number", description: "Max messages. Default: 20" },
689
- fullTranscript: { type: "boolean", description: "Return full message text instead of truncated preview. Default: false" },
690
- },
691
- required: ["sessionId"],
692
- },
693
- },
694
- // ── System Tools ──
695
- {
696
- type: "function",
697
- name: "get_system_status",
698
- description: "Get the overall bosun system status including agent health, task counts, and fleet info.",
699
- parameters: { type: "object", properties: {} },
700
- },
701
- {
702
- type: "function",
703
- name: "get_fleet_status",
704
- description: "Get fleet coordination status across workstations.",
705
- parameters: { type: "object", properties: {} },
706
- },
707
- {
708
- type: "function",
709
- name: "run_command",
710
- description:
711
- "Execute a Bosun system command by name. Supported commands: status, health, config, " +
712
- "fleet, sync, tasks, agents, version, maintenance. These map to the equivalent Bosun " +
713
- "CLI operations and return live results. For free-form workspace shell commands " +
714
- "(git, npm, grep…) use run_workspace_command instead.",
715
- parameters: {
716
- type: "object",
717
- properties: {
718
- command: {
719
- type: "string",
720
- description:
721
- "Bosun command name. Examples: 'status', 'health', 'config', 'fleet', 'tasks inprogress', 'sync'.",
722
- },
723
- },
724
- required: ["command"],
725
- },
726
- },
727
- // ── Git/PR Tools ──
728
- {
729
- type: "function",
730
- name: "get_pr_status",
731
- description: "Get the status of open pull requests.",
732
- parameters: {
733
- type: "object",
734
- properties: {
735
- limit: { type: "number", description: "Max PRs to return. Default: 10" },
736
- },
737
- },
738
- },
739
- // ── Config Tools ──
740
- {
741
- type: "function",
742
- name: "get_config",
743
- description: "Get current bosun configuration values.",
744
- parameters: {
745
- type: "object",
746
- properties: {
747
- key: { type: "string", description: "Specific config key to retrieve. Omit for full config summary." },
748
- },
749
- },
750
- },
751
- {
752
- type: "function",
753
- name: "update_config",
754
- description: "Update a bosun configuration value.",
755
- parameters: {
756
- type: "object",
757
- properties: {
758
- key: { type: "string", description: "Config key to update" },
759
- value: { type: "string", description: "New value" },
760
- },
761
- required: ["key", "value"],
762
- },
763
- },
764
- {
765
- type: "function",
766
- name: "get_effective_config",
767
- description: "Get the full effective bosun configuration with sensitive values redacted. Owner/admin only. Returns all config sections for debugging and inspection.",
768
- parameters: {
769
- type: "object",
770
- properties: {
771
- key: { type: "string", description: "Specific config key. Omit for all." },
772
- },
773
- },
774
- },
775
- {
776
- type: "function",
777
- name: "get_admin_help",
778
- description: "Get a complete listing of all available Voice tools, slash commands, and dispatch actions for admin reference.",
779
- parameters: { type: "object", properties: {} },
780
- },
781
- // ── Workspace Navigation ──
782
- {
783
- type: "function",
784
- name: "search_code",
785
- description: "Search for code patterns in the workspace.",
786
- parameters: {
787
- type: "object",
788
- properties: {
789
- query: { type: "string", description: "Search query or regex pattern" },
790
- filePattern: { type: "string", description: "Glob pattern to filter files. E.g., '**/*.mjs'" },
791
- maxResults: { type: "number", description: "Max results. Default: 20" },
792
- },
793
- required: ["query"],
794
- },
795
- },
796
- {
797
- type: "function",
798
- name: "read_file_content",
799
- description: "Read the content of a file in the workspace.",
800
- parameters: {
801
- type: "object",
802
- properties: {
803
- filePath: { type: "string", description: "Path relative to workspace root" },
804
- startLine: { type: "number", description: "Start line (1-indexed)" },
805
- endLine: { type: "number", description: "End line (1-indexed)" },
806
- },
807
- required: ["filePath"],
808
- },
809
- },
810
- {
811
- type: "function",
812
- name: "list_directory",
813
- description: "List files and directories in a workspace path.",
814
- parameters: {
815
- type: "object",
816
- properties: {
817
- path: { type: "string", description: "Directory path relative to workspace root. Default: root" },
818
- },
819
- },
820
- },
821
- {
822
- type: "function",
823
- name: "get_workspace_context",
824
- description: "Get current workspace and repository context for this voice/chat session.",
825
- parameters: { type: "object", properties: {} },
826
- },
827
- {
828
- type: "function",
829
- name: "query_live_view",
830
- description: "Analyze the latest live camera/screen frame for this session. Provide a query when available; if omitted, it will infer from recent voice context and still return a best-effort screen summary.",
831
- parameters: {
832
- type: "object",
833
- properties: {
834
- query: {
835
- type: "string",
836
- description: "Question about the current visual frame. Example: 'What error is shown on screen?'",
837
- },
838
- },
839
- },
840
- },
841
- // ── Monitoring ──
842
- {
843
- type: "function",
844
- name: "get_recent_logs",
845
- description: "Get recent agent, system, or all log types. Supports paging through log files.",
846
- parameters: {
847
- type: "object",
848
- properties: {
849
- type: {
850
- type: "string",
851
- enum: ["agent", "system", "monitor", "orchestrator", "voice", "all"],
852
- description: "Log type (or 'all' for every source). Default: agent",
853
- },
854
- lines: {
855
- type: "number",
856
- description: "Number of lines to return per source. Default: 50",
857
- },
858
- page: {
859
- type: "number",
860
- description: "Page through log files (1 = most recent). Default: 1",
861
- },
862
- },
863
- },
864
- },
865
- // ── Task Management (extended) ──
866
- {
867
- type: "function",
868
- name: "search_tasks",
869
- description: "Search tasks by text query across titles, descriptions, and labels.",
870
- parameters: {
871
- type: "object",
872
- properties: {
873
- query: { type: "string", description: "Search text" },
874
- limit: { type: "number", description: "Max results. Default: 20" },
875
- },
876
- required: ["query"],
877
- },
878
- },
879
- {
880
- type: "function",
881
- name: "get_task_stats",
882
- description: "Get task board statistics (counts by status, backlog size, etc.).",
883
- parameters: { type: "object", properties: {} },
884
- },
885
- {
886
- type: "function",
887
- name: "delete_task",
888
- description: "Delete a task from the kanban board.",
889
- parameters: {
890
- type: "object",
891
- properties: {
892
- taskId: { type: "string", description: "Task ID or issue number" },
893
- },
894
- required: ["taskId"],
895
- },
896
- },
897
- {
898
- type: "function",
899
- name: "comment_on_task",
900
- description: "Add a comment to a task.",
901
- parameters: {
902
- type: "object",
903
- properties: {
904
- taskId: { type: "string", description: "Task ID" },
905
- body: { type: "string", description: "Comment text" },
906
- },
907
- required: ["taskId", "body"],
908
- },
909
- },
910
- // ── Agent Mode ──
911
- {
912
- type: "function",
913
- name: "set_agent_mode",
914
- description: "Set the agent interaction mode (ask for questions, agent for code changes, plan for architecture).",
915
- parameters: {
916
- type: "object",
917
- properties: {
918
- mode: {
919
- type: "string",
920
- enum: ["ask", "agent", "plan"],
921
- description: "The interaction mode to set",
922
- },
923
- },
924
- required: ["mode"],
925
- },
926
- },
927
- // ── Workflow & Skills ──
928
- {
929
- type: "function",
930
- name: "list_workflows",
931
- description: "List available workflow templates and installed workflow definitions.",
932
- parameters: { type: "object", properties: {} },
933
- },
934
- {
935
- type: "function",
936
- name: "create_workflow",
937
- description: "Create a new workflow from a JSON definition (or create a blank workflow by name).",
938
- parameters: {
939
- type: "object",
940
- properties: {
941
- definition: {
942
- description: "Workflow definition object (or JSON string)",
943
- },
944
- name: { type: "string", description: "Workflow name (used for blank workflow creation)" },
945
- description: { type: "string", description: "Workflow description" },
946
- enabled: { type: "boolean", description: "Whether workflow should be enabled on save. Default: false" },
947
- workflowId: { type: "string", description: "Optional explicit workflow id" },
948
- },
949
- },
950
- },
951
- {
952
- type: "function",
953
- name: "update_workflow_definition",
954
- description: "Update an existing workflow definition (merge patch by default, or replace).",
955
- parameters: {
956
- type: "object",
957
- properties: {
958
- workflowId: { type: "string", description: "Workflow id to update" },
959
- patch: {
960
- description: "Partial workflow object patch (or JSON string)",
961
- },
962
- replace: {
963
- type: "boolean",
964
- description: "Replace the definition instead of merge-patch. Default: false",
965
- },
966
- },
967
- required: ["workflowId"],
968
- },
969
- },
970
- {
971
- type: "function",
972
- name: "delete_workflow",
973
- description: "Delete a workflow definition by id.",
974
- parameters: {
975
- type: "object",
976
- properties: {
977
- workflowId: { type: "string", description: "Workflow id" },
978
- },
979
- required: ["workflowId"],
980
- },
981
- },
982
- {
983
- type: "function",
984
- name: "create_workflow_from_template",
985
- description: "Install a workflow template as a new workflow instance, with optional variable overrides.",
986
- parameters: {
987
- type: "object",
988
- properties: {
989
- templateId: { type: "string", description: "Workflow template id" },
990
- overrides: {
991
- description: "Variable override object (or JSON string)",
992
- },
993
- executeAfterCreate: { type: "boolean", description: "Run the new workflow immediately. Default: false" },
994
- input: { description: "Input object (or JSON string) for executeAfterCreate" },
995
- },
996
- required: ["templateId"],
997
- },
998
- },
999
- {
1000
- type: "function",
1001
- name: "generate_workflow_with_agent",
1002
- description: "Ask the coding agent to generate a workflow JSON, then optionally save it.",
1003
- parameters: {
1004
- type: "object",
1005
- properties: {
1006
- prompt: { type: "string", description: "What workflow to generate" },
1007
- save: { type: "boolean", description: "Save generated workflow automatically. Default: true" },
1008
- enabled: { type: "boolean", description: "When saving, set enabled state. Default: false" },
1009
- executor: {
1010
- type: "string",
1011
- enum: ["codex-sdk", "copilot-sdk", "claude-sdk", "gemini-sdk", "opencode-sdk"],
1012
- description: "Agent executor to use for generation",
1013
- },
1014
- },
1015
- required: ["prompt"],
1016
- },
1017
- },
1018
- {
1019
- type: "function",
1020
- name: "get_workflow_definition",
1021
- description: "Get a saved workflow definition (nodes, edges, metadata) by workflow id.",
1022
- parameters: {
1023
- type: "object",
1024
- properties: {
1025
- workflowId: { type: "string", description: "Workflow id" },
1026
- includeDisabled: {
1027
- type: "boolean",
1028
- description: "Include disabled workflows in lookups where relevant. Default: true",
1029
- },
1030
- },
1031
- required: ["workflowId"],
1032
- },
1033
- },
1034
- {
1035
- type: "function",
1036
- name: "execute_workflow",
1037
- description: "Execute a workflow now with optional input payload.",
1038
- parameters: {
1039
- type: "object",
1040
- properties: {
1041
- workflowId: { type: "string", description: "Workflow id to run" },
1042
- input: { description: "Input payload object (or JSON string)" },
1043
- force: { type: "boolean", description: "Force run even if workflow is disabled. Default: false" },
1044
- },
1045
- required: ["workflowId"],
1046
- },
1047
- },
1048
- {
1049
- type: "function",
1050
- name: "list_workflow_runs",
1051
- description: "List workflow run history across all workflows or for one workflow.",
1052
- parameters: {
1053
- type: "object",
1054
- properties: {
1055
- workflowId: { type: "string", description: "Optional workflow id filter" },
1056
- status: {
1057
- type: "string",
1058
- enum: ["running", "completed", "failed", "paused", "cancelled"],
1059
- description: "Optional status filter",
1060
- },
1061
- limit: { type: "number", description: "Max runs to return. Default: 20" },
1062
- },
1063
- },
1064
- },
1065
- {
1066
- type: "function",
1067
- name: "get_workflow_run",
1068
- description: "Get workflow run detail, including errors and recent logs.",
1069
- parameters: {
1070
- type: "object",
1071
- properties: {
1072
- runId: { type: "string", description: "Workflow run id" },
1073
- includeLogs: {
1074
- type: "boolean",
1075
- description: "Include run logs in response. Default: true",
1076
- },
1077
- logLimit: {
1078
- type: "number",
1079
- description: "Max log entries to return when includeLogs=true. Default: 120",
1080
- },
1081
- includeNodeStatusEvents: {
1082
- type: "boolean",
1083
- description: "Include node status event timeline. Default: false",
1084
- },
1085
- },
1086
- required: ["runId"],
1087
- },
1088
- },
1089
- {
1090
- type: "function",
1091
- name: "analyze_workflow",
1092
- description: "Analyze workflow health using structure + recent run history.",
1093
- parameters: {
1094
- type: "object",
1095
- properties: {
1096
- workflowId: { type: "string", description: "Workflow id. Omit to analyze multiple workflows." },
1097
- limit: { type: "number", description: "Max runs used for analysis. Default: 30" },
1098
- },
1099
- },
1100
- },
1101
- {
1102
- type: "function",
1103
- name: "retry_workflow_run",
1104
- description: "Retry workflow run: from_failed (only failed runs) or from_scratch (any run).",
1105
- parameters: {
1106
- type: "object",
1107
- properties: {
1108
- runId: { type: "string", description: "Original failed workflow run id to retry" },
1109
- mode: {
1110
- type: "string",
1111
- enum: ["from_failed", "from_scratch"],
1112
- description: "Retry mode. Default: from_failed",
1113
- },
1114
- },
1115
- required: ["runId"],
1116
- },
1117
- },
1118
- {
1119
- type: "function",
1120
- name: "list_skills",
1121
- description: "List available agent skills from the knowledge base.",
1122
- parameters: { type: "object", properties: {} },
1123
- },
1124
- {
1125
- type: "function",
1126
- name: "list_prompts",
1127
- description: "List available agent prompt definitions.",
1128
- parameters: { type: "object", properties: {} },
1129
- },
1130
- {
1131
- type: "function",
1132
- name: "sync_prompt_defaults",
1133
- description: "Compare current workspace prompt files against Bosun defaults and return update candidates; optionally apply safe default updates for selected prompt keys.",
1134
- parameters: {
1135
- type: "object",
1136
- properties: {
1137
- apply: {
1138
- type: "boolean",
1139
- description: "When true, apply default updates for prompt keys with updateAvailable=true. Default: false",
1140
- },
1141
- keys: {
1142
- type: "array",
1143
- items: { type: "string" },
1144
- description: "Optional prompt key filter when apply=true. Example: ['orchestrator', 'voiceAgent']",
1145
- },
1146
- },
1147
- },
1148
- },
1149
- // ── Batch Action ──
1150
- {
1151
- type: "function",
1152
- name: "dispatch_action",
1153
- description: "Execute a Bosun action by name. Use for any action not covered by dedicated tools. Actions: task.list, task.create, agent.delegate, system.status, workflow.list, skill.list, prompt.list, etc.",
1154
- parameters: {
1155
- type: "object",
1156
- properties: {
1157
- action: { type: "string", description: "Action name (e.g. task.stats, agent.status)" },
1158
- params: {
1159
- type: "object",
1160
- description: "Action parameters",
1161
- },
1162
- },
1163
- required: ["action"],
1164
- },
1165
- },
1166
- // ── Generic MCP Gateway ──
1167
- {
1168
- type: "function",
1169
- name: "invoke_mcp_tool",
1170
- description: "Call any MCP (Model Context Protocol) tool by name via the agent. Use for GitHub operations (create PR, list issues), kanban integrations, or any capability exposed by a configured MCP server. This is the preferred way to trigger one-shot MCP actions from voice without needing a dedicated tool wrapper.",
1171
- parameters: {
1172
- type: "object",
1173
- properties: {
1174
- tool: {
1175
- type: "string",
1176
- description: "The MCP tool name, e.g. 'create_issue', 'create_pull_request', 'list_tasks'. For GitHub tools omit the server prefix.",
1177
- },
1178
- server: {
1179
- type: "string",
1180
- description: "Optional MCP server name to disambiguate, e.g. 'github', 'linear', 'jira'. Leave empty if unambiguous.",
1181
- },
1182
- args: {
1183
- type: "object",
1184
- description: "Arguments to pass to the MCP tool as key/value pairs.",
1185
- },
1186
- },
1187
- required: ["tool"],
1188
- },
1189
- },
1190
- // ── Context Warm-up ──
1191
- {
1192
- type: "function",
1193
- name: "warm_codebase_context",
1194
- description: "Pre-load codebase context into the agent so subsequent code questions answer instantly. Call this once at the start of a voice session when you know the user will ask project-specific questions.",
1195
- parameters: { type: "object", properties: {} },
1196
- },
1197
-
1198
- // ── Slash Commands ──
1199
- {
1200
- type: "function",
1201
- name: "bosun_slash_command",
1202
- description:
1203
- "Invoke a Bosun slash command by exact name. Supports: " +
1204
- "/instant <prompt> (fast inline answer), " +
1205
- "/ask <prompt> (read-only agent answer), " +
1206
- "/agent <prompt> or /handoff <prompt> (create a dedicated live handoff session), " +
1207
- "/status, /tasks, /agents, /health, /version, /commands, " +
1208
- "/prompts, /promptsync [apply [keys...]], " +
1209
- "/mcp <tool_name> [server] (invoke an MCP tool). " +
1210
- "Use this when the user explicitly says a slash command or when you need a fast inline " +
1211
- "answer vs a direct handoff session.",
1212
- parameters: {
1213
- type: "object",
1214
- properties: {
1215
- command: {
1216
- type: "string",
1217
- description:
1218
- "Full slash command string including leading /. Examples: " +
1219
- "'/instant what does the auth module do?', " +
1220
- "'/agent write unit tests for config.mjs', " +
1221
- "'/ask summarize the git log', " +
1222
- "'/promptsync', '/promptsync apply orchestrator,voiceAgent', " +
1223
- "'/mcp create_issue server=github', " +
1224
- "'/status'",
1225
- },
1226
- },
1227
- required: ["command"],
1228
- },
1229
- },
1230
548
 
1231
- // ── Workspace Shell ──
1232
- {
1233
- type: "function",
1234
- name: "run_workspace_command",
1235
- description:
1236
- "Execute a workspace shell command and return live output. " +
1237
- "Standard sessions run read-only commands directly; privileged owner/admin sessions can run broader commands. " +
1238
- "Use this for diagnostics, git operations, tests/builds, and direct shell workflows.",
1239
- parameters: {
1240
- type: "object",
1241
- properties: {
1242
- command: {
1243
- type: "string",
1244
- description:
1245
- "Shell command to run in the workspace root. Examples: " +
1246
- "'git status --short', 'git log --oneline -10', " +
1247
- "'npm test -- --passWithNoTests 2>&1 | tail -20', " +
1248
- "'cat package.json', 'ls src/', 'grep -r TODO . --include=*.mjs | head -20'",
1249
- },
1250
- },
1251
- required: ["command"],
1252
- },
1253
- },
1254
-
1255
- // ── Background Session Polling ──
1256
- {
1257
- type: "function",
1258
- name: "poll_background_session",
1259
- description:
1260
- "Check the current status and latest output of a background agent session that was " +
1261
- "previously started with delegate_to_agent. Use this when the user asks 'what's the status of that " +
1262
- "background task?' or 'is the agent done yet?'.",
1263
- parameters: {
1264
- type: "object",
1265
- properties: {
1266
- backgroundSessionId: {
1267
- type: "string",
1268
- description: "The background session ID returned by delegate_to_agent (starts with 'voice-bg-').",
1269
- },
1270
- limit: {
1271
- type: "number",
1272
- description: "Number of most-recent messages to include. Default: 5",
1273
- },
1274
- },
1275
- required: ["backgroundSessionId"],
1276
- },
1277
- },
1278
- ];
1279
549
 
1280
550
  // ── Tool Execution ──────────────────────────────────────────────────────────
1281
551
 
@@ -2284,7 +1554,8 @@ const TOOL_HANDLERS = {
2284
1554
  "Return strict JSON with keys: name, description, enabled, variables, nodes, edges, triggers, metadata. " +
2285
1555
  "Do not include markdown fences.\n\n" +
2286
1556
  `User request:\n${prompt}`;
2287
- const result = await execPooledPrompt(generationPrompt, {
1557
+ const pool = await getAgentPool();
1558
+ const result = await pool.execPooledPrompt(generationPrompt, {
2288
1559
  sdk: executor,
2289
1560
  mode: "agent",
2290
1561
  sessionId: context?.sessionId || `voice-workflow-generate-${Date.now()}`,
@@ -2918,26 +2189,9 @@ const TOOL_HANDLERS = {
2918
2189
  const rawCmd = String(args.command || "").trim();
2919
2190
  if (!rawCmd) return "{RESPONSE}: command is required.";
2920
2191
 
2921
- // Only execute obviously read-only commands directly.
2922
- const SAFE_PATTERNS = [
2923
- /^git\s+(status|log|diff\s|show\s|branch|remote|tag|stash\s+list|ls-files|rev-parse|shortlog|describe)\b/i,
2924
- /^git\s+diff$/i,
2925
- /^git\s+log$/i,
2926
- /^npm\s+(test|run\s+(test|lint|build|check)|ls|audit)\b/i,
2927
- /^node\s+(--version|-v)$/i,
2928
- /^npm\s+(--version|-v)$/i,
2929
- /^ls(\s|$)/i,
2930
- /^cat\s+/i,
2931
- /^head\s+/i,
2932
- /^tail(\s|$)/i,
2933
- /^wc(\s|$)/i,
2934
- /^find\s+/i,
2935
- /^grep(\s|$)/i,
2936
- /^echo\s+/i,
2937
- /^pwd$/i,
2938
- /^which\s+/i,
2939
- /^type\s+/i,
2940
- ];
2192
+ // Only execute obviously read-only commands directly, based on the
2193
+ // shared SAFE_WORKSPACE_COMMAND_PATTERNS defined at module scope.
2194
+ const SAFE_PATTERNS = SAFE_WORKSPACE_COMMAND_PATTERNS;
2941
2195
 
2942
2196
  const isSafe = SAFE_PATTERNS.some((p) => p.test(rawCmd));
2943
2197
  if (!isSafe) {
@@ -3067,3 +2321,5 @@ export { TOOL_DEFS as VOICE_TOOLS };
3067
2321
  export async function warmCodebaseContext(context = {}) {
3068
2322
  await TOOL_HANDLERS.warm_codebase_context({}, context).catch(() => {});
3069
2323
  }
2324
+
2325
+