arkaos 4.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +2 -2
  2. package/VERSION +1 -1
  3. package/arka/SKILL.md +16 -22
  4. package/arka/skills/flow/SKILL.md +108 -234
  5. package/config/claude-agents/eduardo-copy.md +53 -0
  6. package/config/claude-agents/francisca-tech.md +52 -0
  7. package/config/claude-agents/marta-cqo.md +52 -0
  8. package/config/claude-agents/paulo-tech-lead.md +50 -0
  9. package/config/cognition/schedules.yaml +15 -0
  10. package/config/constitution.yaml +17 -21
  11. package/config/hooks/_lib/workflow-classifier.sh +1 -1
  12. package/config/hooks/post-tool-use.sh +40 -539
  13. package/config/hooks/pre-tool-use.sh +32 -281
  14. package/config/hooks/session-start.sh +8 -10
  15. package/config/hooks/stop.sh +30 -425
  16. package/config/hooks/user-prompt-submit.sh +42 -459
  17. package/core/cognition/__pycache__/dreaming.cpython-313.pyc +0 -0
  18. package/core/cognition/dreaming.py +5 -0
  19. package/core/cognition/scheduler/__pycache__/daemon.cpython-313.pyc +0 -0
  20. package/core/cognition/scheduler/daemon.py +10 -0
  21. package/core/forge/__init__.py +2 -0
  22. package/core/forge/__pycache__/__init__.cpython-313.pyc +0 -0
  23. package/core/forge/__pycache__/orchestrator.cpython-313.pyc +0 -0
  24. package/core/forge/__pycache__/persistence.cpython-313.pyc +0 -0
  25. package/core/forge/__pycache__/renderer.cpython-313.pyc +0 -0
  26. package/core/forge/__pycache__/runtime_dispatcher.cpython-313.pyc +0 -0
  27. package/core/forge/__pycache__/schema.cpython-313.pyc +0 -0
  28. package/core/forge/orchestrator.py +50 -27
  29. package/core/forge/persistence.py +16 -0
  30. package/core/forge/renderer.py +16 -0
  31. package/core/forge/runtime_dispatcher.py +34 -19
  32. package/core/forge/schema.py +12 -0
  33. package/core/governance/__pycache__/__init__.cpython-312.pyc +0 -0
  34. package/core/governance/__pycache__/closing_marker_check.cpython-313.pyc +0 -0
  35. package/core/governance/__pycache__/compliance_telemetry.cpython-313.pyc +0 -0
  36. package/core/governance/__pycache__/constitution.cpython-312.pyc +0 -0
  37. package/core/governance/__pycache__/evidence_checks.cpython-313.pyc +0 -0
  38. package/core/governance/__pycache__/meta_tag_check.cpython-313.pyc +0 -0
  39. package/core/governance/__pycache__/qg_verdict.cpython-313.pyc +0 -0
  40. package/core/governance/__pycache__/review_workflow.cpython-313.pyc +0 -0
  41. package/core/governance/__pycache__/skill_proposer.cpython-313.pyc +0 -0
  42. package/core/governance/closing_marker_check.py +9 -5
  43. package/core/governance/compliance_telemetry.py +1 -1
  44. package/core/governance/compliance_telemetry_cli.py +1 -1
  45. package/core/governance/evidence_checks.py +421 -0
  46. package/core/governance/meta_tag_check.py +36 -0
  47. package/core/governance/qg_verdict.py +78 -0
  48. package/core/governance/review_workflow.py +34 -1
  49. package/core/governance/skill_proposer.py +2 -1
  50. package/core/hooks/__init__.py +12 -0
  51. package/core/hooks/__pycache__/__init__.cpython-312.pyc +0 -0
  52. package/core/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  53. package/core/hooks/__pycache__/_shared.cpython-312.pyc +0 -0
  54. package/core/hooks/__pycache__/_shared.cpython-313.pyc +0 -0
  55. package/core/hooks/__pycache__/post_tool_use.cpython-312.pyc +0 -0
  56. package/core/hooks/__pycache__/post_tool_use.cpython-313.pyc +0 -0
  57. package/core/hooks/__pycache__/pre_tool_use.cpython-312.pyc +0 -0
  58. package/core/hooks/__pycache__/pre_tool_use.cpython-313.pyc +0 -0
  59. package/core/hooks/__pycache__/stop.cpython-312.pyc +0 -0
  60. package/core/hooks/__pycache__/stop.cpython-313.pyc +0 -0
  61. package/core/hooks/__pycache__/user_prompt_submit.cpython-312.pyc +0 -0
  62. package/core/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  63. package/core/hooks/_shared.py +110 -0
  64. package/core/hooks/post_tool_use.py +689 -0
  65. package/core/hooks/pre_tool_use.py +267 -0
  66. package/core/hooks/stop.py +395 -0
  67. package/core/hooks/user_prompt_submit.py +600 -0
  68. package/core/jobs/__pycache__/__init__.cpython-312.pyc +0 -0
  69. package/core/jobs/__pycache__/auto_doc_worker.cpython-312.pyc +0 -0
  70. package/core/jobs/__pycache__/manager.cpython-312.pyc +0 -0
  71. package/core/knowledge/__pycache__/embedder.cpython-312.pyc +0 -0
  72. package/core/knowledge/__pycache__/embedder.cpython-313.pyc +0 -0
  73. package/core/knowledge/__pycache__/vector_store.cpython-312.pyc +0 -0
  74. package/core/knowledge/__pycache__/vector_store.cpython-313.pyc +0 -0
  75. package/core/knowledge/embedder.py +118 -11
  76. package/core/knowledge/vector_store.py +111 -14
  77. package/core/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
  78. package/core/runtime/__pycache__/__init__.cpython-313.pyc +0 -0
  79. package/core/runtime/__pycache__/base.cpython-312.pyc +0 -0
  80. package/core/runtime/__pycache__/base.cpython-313.pyc +0 -0
  81. package/core/runtime/__pycache__/capabilities_cli.cpython-312.pyc +0 -0
  82. package/core/runtime/__pycache__/capabilities_cli.cpython-313.pyc +0 -0
  83. package/core/runtime/__pycache__/claude_code.cpython-312.pyc +0 -0
  84. package/core/runtime/__pycache__/claude_code.cpython-313.pyc +0 -0
  85. package/core/runtime/__pycache__/codex_cli.cpython-312.pyc +0 -0
  86. package/core/runtime/__pycache__/codex_cli.cpython-313.pyc +0 -0
  87. package/core/runtime/__pycache__/cost_governor.cpython-312.pyc +0 -0
  88. package/core/runtime/__pycache__/cost_governor.cpython-313.pyc +0 -0
  89. package/core/runtime/__pycache__/cursor.cpython-312.pyc +0 -0
  90. package/core/runtime/__pycache__/cursor.cpython-313.pyc +0 -0
  91. package/core/runtime/__pycache__/gemini_cli.cpython-312.pyc +0 -0
  92. package/core/runtime/__pycache__/gemini_cli.cpython-313.pyc +0 -0
  93. package/core/runtime/__pycache__/llm_provider.cpython-312.pyc +0 -0
  94. package/core/runtime/__pycache__/llm_provider.cpython-313.pyc +0 -0
  95. package/core/runtime/__pycache__/llm_retry.cpython-312.pyc +0 -0
  96. package/core/runtime/__pycache__/llm_retry.cpython-313.pyc +0 -0
  97. package/core/runtime/__pycache__/native_usage.cpython-312.pyc +0 -0
  98. package/core/runtime/__pycache__/native_usage.cpython-313.pyc +0 -0
  99. package/core/runtime/__pycache__/path_resolver.cpython-312.pyc +0 -0
  100. package/core/runtime/__pycache__/pricing.cpython-312.pyc +0 -0
  101. package/core/runtime/__pycache__/pricing.cpython-313.pyc +0 -0
  102. package/core/runtime/__pycache__/registry.cpython-312.pyc +0 -0
  103. package/core/runtime/__pycache__/registry.cpython-313.pyc +0 -0
  104. package/core/runtime/__pycache__/squad_orchestrator.cpython-312.pyc +0 -0
  105. package/core/runtime/base.py +29 -0
  106. package/core/runtime/capabilities_cli.py +74 -0
  107. package/core/runtime/claude_code.py +8 -0
  108. package/core/runtime/codex_cli.py +205 -37
  109. package/core/runtime/cost_governor.py +184 -0
  110. package/core/runtime/cursor.py +8 -0
  111. package/core/runtime/gemini_cli.py +8 -0
  112. package/core/runtime/llm_provider.py +32 -13
  113. package/core/runtime/llm_retry.py +233 -0
  114. package/core/runtime/native_usage.py +186 -0
  115. package/core/runtime/pricing.py +9 -0
  116. package/core/runtime/registry.py +3 -0
  117. package/core/shared/__pycache__/test_evidence.cpython-313-pytest-9.1.1.pyc +0 -0
  118. package/core/shared/__pycache__/test_evidence.cpython-313.pyc +0 -0
  119. package/core/shared/test_evidence.py +34 -0
  120. package/core/synapse/__pycache__/agent_experiences_layer.cpython-312.pyc +0 -0
  121. package/core/synapse/__pycache__/engine.cpython-312.pyc +0 -0
  122. package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
  123. package/core/synapse/__pycache__/graph_context_layer.cpython-313.pyc +0 -0
  124. package/core/synapse/__pycache__/kb_cache.cpython-312.pyc +0 -0
  125. package/core/synapse/__pycache__/kb_cache.cpython-313.pyc +0 -0
  126. package/core/synapse/__pycache__/layers.cpython-312.pyc +0 -0
  127. package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
  128. package/core/synapse/engine.py +7 -0
  129. package/core/synapse/graph_context_layer.py +212 -0
  130. package/core/synapse/kb_cache.py +3 -0
  131. package/core/synapse/layers.py +104 -20
  132. package/core/workflow/__pycache__/enforcer.cpython-313.pyc +0 -0
  133. package/core/workflow/__pycache__/flow_enforcer.cpython-312.pyc +0 -0
  134. package/core/workflow/__pycache__/flow_enforcer.cpython-313.pyc +0 -0
  135. package/core/workflow/__pycache__/gate_checkpoint.cpython-312.pyc +0 -0
  136. package/core/workflow/__pycache__/gate_checkpoint.cpython-313.pyc +0 -0
  137. package/core/workflow/__pycache__/research_gate.cpython-313.pyc +0 -0
  138. package/core/workflow/__pycache__/rules_registry.cpython-313.pyc +0 -0
  139. package/core/workflow/__pycache__/specialist_enforcer.cpython-312.pyc +0 -0
  140. package/core/workflow/__pycache__/specialist_enforcer.cpython-313.pyc +0 -0
  141. package/core/workflow/enforcer.py +5 -16
  142. package/core/workflow/flow_enforcer.py +36 -11
  143. package/core/workflow/gate_checkpoint.py +149 -0
  144. package/core/workflow/research_gate.py +15 -3
  145. package/core/workflow/rules_registry.py +137 -389
  146. package/core/workflow/specialist_enforcer.py +21 -4
  147. package/dashboard/app/pages/knowledge/index.vue +4 -1
  148. package/departments/ops/skills/n8n-flow/SKILL.md +13 -0
  149. package/departments/ops/skills/workflow-automate/SKILL.md +16 -0
  150. package/departments/quality/SKILL.md +45 -5
  151. package/departments/quality/agents/copy-director.yaml +3 -1
  152. package/departments/quality/agents/tech-director.yaml +3 -1
  153. package/installer/adapters/claude-code.js +46 -2
  154. package/installer/config-seed.js +39 -14
  155. package/installer/doctor.js +8 -0
  156. package/installer/graphify.js +153 -0
  157. package/installer/index.js +20 -0
  158. package/installer/init.js +11 -0
  159. package/installer/update.js +19 -0
  160. package/package.json +1 -1
  161. package/pyproject.toml +1 -1
  162. package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
  163. package/scripts/__pycache__/knowledge-index.cpython-313.pyc +0 -0
  164. package/scripts/__pycache__/synapse-bridge.cpython-312.pyc +0 -0
  165. package/scripts/__pycache__/synapse-bridge.cpython-313.pyc +0 -0
  166. package/scripts/knowledge-index.py +8 -2
  167. package/scripts/synapse-bridge.py +102 -26
@@ -1,475 +1,58 @@
1
1
  #!/usr/bin/env bash
2
2
  # ============================================================================
3
- # ArkaOS v2 — UserPromptSubmit Hook (Synapse v2 Bridge)
4
- # Calls Python Synapse engine for 8-layer context injection
3
+ # ArkaOS v2 — UserPromptSubmit Hook (thin wrapper, PR-6 v4.1.0 hook hygiene)
4
+ #
5
+ # Delegates the ENTIRE event to ONE python process:
6
+ # python3 -m core.hooks.user_prompt_submit
7
+ #
8
+ # The entrypoint preserves the behavior contract of the previous
9
+ # ~31-spawn-site version:
10
+ # - V1 migration + sync-version detection ([arka:update-available]).
11
+ # - Per-turn cache reset: marker_cache.invalidate_marker +
12
+ # kb_cache.invalidate_obsidian_query.
13
+ # - Synapse bridge (scripts/synapse-bridge.py::run_bridge) called
14
+ # in-process — 12-layer context injection, no second interpreter.
15
+ # - Workflow-state / forge tags, KB auto-inject, bash-parity fallback
16
+ # context (constitution L0, branch, workflow, forge).
17
+ # - Token hygiene (context %, topic drift, large paste, vague ref).
18
+ # - Persistent [ARKA:ROUTE] reminder + workflow classifier that marks
19
+ # the session flow-required ([ARKA:WORKFLOW-REQUIRED]).
20
+ # - One-shot nudges (KB-cite, meta-tag, closing-marker) gated by
21
+ # effort level; cognitive context injection.
22
+ #
5
23
  # Timeout: 10s | Output: JSON to stdout | Target: <100ms
6
24
  # ============================================================================
7
25
 
8
- input=$(cat)
9
-
10
- # ─── V1 Migration Detection ─────────────────────────────────────────────
11
- V1_PATHS=("$HOME/.claude/skills/arka-os" "$HOME/.claude/skills/arkaos")
12
- MIGRATION_MARKER="$HOME/.arkaos/migrated-from-v1"
13
-
14
- for v1_path in "${V1_PATHS[@]}"; do
15
- if [ -d "$v1_path" ] && [ ! -f "$MIGRATION_MARKER" ]; then
16
- echo "{\"additionalContext\": \"[MIGRATION] ArkaOS v1 detected at $v1_path. Run: npx arkaos migrate — This will backup v1, preserve your data, and install v2. See: https://github.com/andreagroferreira/arka-os#install\"}"
17
- exit 0
18
- fi
19
- done
20
-
21
- # ─── Sync Version Detection ────────────────────────────────────────────
22
- SYNC_STATE="$HOME/.arkaos/sync-state.json"
23
- ARKAOS_VERSION_FILE="$HOME/.arkaos/.repo-path"
24
-
25
- if [ -f "$ARKAOS_VERSION_FILE" ]; then
26
- _REPO_PATH=$(cat "$ARKAOS_VERSION_FILE")
27
- if [ -f "$_REPO_PATH/VERSION" ]; then
28
- _CURRENT_VERSION=$(cat "$_REPO_PATH/VERSION")
29
- elif [ -f "$_REPO_PATH/package.json" ]; then
30
- _CURRENT_VERSION=$(python3 -c "import json; print(json.load(open('$_REPO_PATH/package.json'))['version'])" 2>/dev/null || echo "")
31
- fi
32
-
33
- if [ -n "${_CURRENT_VERSION:-}" ]; then
34
- if [ -f "$SYNC_STATE" ]; then
35
- _SYNCED_VERSION=$(python3 -c "import json; print(json.load(open('$SYNC_STATE'))['version'])" 2>/dev/null || echo "none")
36
- else
37
- _SYNCED_VERSION="none"
38
- fi
39
-
40
- if [ "$_CURRENT_VERSION" != "$_SYNCED_VERSION" ]; then
41
- _SYNC_NOTICE="[arka:update-available] ArkaOS v${_CURRENT_VERSION} installed (synced: ${_SYNCED_VERSION}). Run /arka update to sync all projects. "
42
- fi
43
- if [ -n "${ARKAOS_DEBUG:-}" ]; then
44
- echo "[DEBUG] Detected version: ${_CURRENT_VERSION:-unknown}, synced: ${_SYNCED_VERSION:-none}" >&2
45
- fi
46
- fi
47
- fi
48
-
49
- # ─── Session Greeting (now handled by SessionStart hook via systemMessage) ──
50
- _ARKA_GREETING=""
51
-
52
- # ─── Performance Timing ──────────────────────────────────────────────────
53
- _HOOK_START_NS=$(date +%s%N 2>/dev/null || echo "0")
54
- _hook_ms() {
55
- local end_ns=$(date +%s%N 2>/dev/null || echo "0")
56
- if [ "$_HOOK_START_NS" != "0" ] && [ "$end_ns" != "0" ] && [ ${#end_ns} -gt 10 ]; then
57
- echo $(( (end_ns - _HOOK_START_NS) / 1000000 ))
26
+ if [ -z "${ARKAOS_ROOT:-}" ]; then
27
+ if [ -f "$HOME/.arkaos/.repo-path" ]; then
28
+ ARKAOS_ROOT=$(cat "$HOME/.arkaos/.repo-path")
29
+ elif [ -d "$HOME/.arkaos" ]; then
30
+ ARKAOS_ROOT="$HOME/.arkaos"
58
31
  else
59
- echo "0"
32
+ ARKAOS_ROOT="${ARKA_OS:-$HOME/.claude/skills/arkaos}"
60
33
  fi
61
- }
62
-
63
- # ─── Paths ───────────────────────────────────────────────────────────────
64
- # Resolve ARKAOS_ROOT: env var → .repo-path → $HOME/.arkaos → portable fallback
65
- if [ -n "${ARKAOS_ROOT:-}" ]; then
66
- : # already set
67
- elif [ -f "$HOME/.arkaos/.repo-path" ]; then
68
- ARKAOS_ROOT=$(cat "$HOME/.arkaos/.repo-path")
69
- elif [ -d "$HOME/.arkaos" ]; then
70
- ARKAOS_ROOT="$HOME/.arkaos"
71
- else
72
- # Portable fallback — matches user-prompt-submit-v2.sh. Power users can
73
- # override with the ARKA_OS env var. Reached only on corrupt/uninitialised
74
- # installs; synapse-bridge.py will fail gracefully if the path is wrong.
75
- ARKAOS_ROOT="${ARKA_OS:-$HOME/.claude/skills/arkaos}"
76
34
  fi
77
35
  export ARKAOS_ROOT
78
36
 
79
- CACHE_DIR="/tmp/arkaos-context-cache"
80
- CACHE_TTL=300 # Constitution cache: 5 minutes
81
-
82
- mkdir -p "$CACHE_DIR" 2>/dev/null
83
-
84
- # ─── Extract user input from hook JSON ───────────────────────────────────
85
- user_input=""
86
- SESSION_ID=""
87
- EFFORT_LEVEL=""
88
- if command -v jq &>/dev/null; then
89
- user_input=$(echo "$input" | jq -r '.userInput // .message // ""' 2>/dev/null)
90
- SESSION_ID=$(echo "$input" | jq -r '.session_id // ""' 2>/dev/null)
91
- # PR46 v2.65.0 — Claude Code W19 ships effort.level in hook stdin.
92
- # Soft-block nudges (KB-first + meta-tag) are gated by effort: only
93
- # surfaced at high|xhigh; low/medium skip the nudge to avoid forcing
94
- # the model to comply with full contracts during cheap exploratory
95
- # turns. Hard enforcement (PreToolUse flow_enforcer) runs regardless.
96
- EFFORT_LEVEL=$(echo "$input" | jq -r '.effort.level // ""' 2>/dev/null)
97
- fi
98
- [ -z "$EFFORT_LEVEL" ] && EFFORT_LEVEL="${CLAUDE_EFFORT:-}"
99
-
100
- # Decide whether soft-block nudges surface to the next turn.
101
- _ARKA_SURFACE_NUDGES="true"
102
- case "${EFFORT_LEVEL:-high}" in
103
- low|medium) _ARKA_SURFACE_NUDGES="false" ;;
104
- *) _ARKA_SURFACE_NUDGES="true" ;;
105
- esac
106
-
107
- # ─── Flow marker cache invalidation (v2 — new turn, reset ALLOW cache) ──
108
- # Cheap, non-blocking, runs before Synapse so a stuck Python later cannot
109
- # leave a stale marker alive across turns.
110
- if [ -n "$SESSION_ID" ] && command -v python3 &>/dev/null; then
111
- _INVALIDATE_ROOT="${ARKAOS_ROOT:-}"
112
- if [ -z "$_INVALIDATE_ROOT" ] && [ -f "$HOME/.arkaos/.repo-path" ]; then
113
- _INVALIDATE_ROOT=$(cat "$HOME/.arkaos/.repo-path" 2>/dev/null)
114
- fi
115
- [ -z "$_INVALIDATE_ROOT" ] && _INVALIDATE_ROOT="$HOME/.arkaos"
116
- SESSION_ID="$SESSION_ID" PYTHONPATH="$_INVALIDATE_ROOT" python3 -c "
117
- import os
118
- try:
119
- from core.workflow.marker_cache import invalidate_marker
120
- invalidate_marker(os.environ.get('SESSION_ID', ''))
121
- except Exception:
122
- pass
123
- try:
124
- from core.synapse.kb_cache import invalidate_obsidian_query
125
- invalidate_obsidian_query(os.environ.get('SESSION_ID', ''))
126
- except Exception:
127
- pass
128
- " 2>/dev/null || true
129
- fi
130
- # Fallback: try to get the raw text
131
- if [ -z "$user_input" ]; then
132
- user_input=$(echo "$input" | head -c 2000)
133
- fi
134
-
135
- # ─── Load shared workflow classifier ─────────────────────────────────────
136
- _CLASSIFIER_LIB="$(dirname "$0")/_lib/workflow-classifier.sh"
137
- if [ -f "$_CLASSIFIER_LIB" ]; then
138
- # shellcheck disable=SC1090
139
- . "$_CLASSIFIER_LIB"
140
- fi
141
-
142
- # ─── Try Python Synapse bridge first ────────────────────────────────────
143
- python_result=""
144
- BRIDGE_SCRIPT="${ARKAOS_ROOT}/scripts/synapse-bridge.py"
145
-
146
- # Determine which path we're using for debug output
147
- if [ -n "${ARKAOS_DEBUG:-}" ]; then
148
- echo "[DEBUG] ARKAOS_ROOT=${ARKAOS_ROOT}" >&2
149
- echo "[DEBUG] BRIDGE_SCRIPT=${BRIDGE_SCRIPT}" >&2
150
- fi
151
-
152
- if command -v python3 &>/dev/null && [ -f "$BRIDGE_SCRIPT" ]; then
153
- # Validate ARKAOS_ROOT before calling bridge
154
- if [ ! -d "$ARKAOS_ROOT" ]; then
155
- if [ -n "${ARKAOS_DEBUG:-}" ]; then
156
- echo "[DEBUG] ARKAOS_ROOT is not a valid directory, skipping Python bridge" >&2
157
- fi
158
- else
159
- _bridge_start=$(date +%s%N 2>/dev/null || echo "0")
160
- bridge_output=$(echo "{\"user_input\":$(echo "$user_input" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))" 2>/dev/null || echo '""')}" \
161
- | ARKAOS_ROOT="$ARKAOS_ROOT" python3 "$BRIDGE_SCRIPT" --root "$ARKAOS_ROOT" 2>/dev/null)
162
- _bridge_status=$?
163
-
164
- if [ -n "${ARKAOS_DEBUG:-}" ]; then
165
- if [ "$_bridge_start" != "0" ] && [ $(date +%s%N 2>/dev/null || echo "0") != "0" ]; then
166
- _bridge_ms=$(( ($(date +%s%N) - _bridge_start) / 1000000 ))
167
- echo "[DEBUG] bridge completed in ${_bridge_ms}ms, exit=$_bridge_status" >&2
168
- fi
169
- echo "[DEBUG] bridge_output length=${#bridge_output}" >&2
170
- fi
171
-
172
- if [ -n "$bridge_output" ] && [ $_bridge_status -eq 0 ]; then
173
- python_result=$(echo "$bridge_output" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('context_string',''))" 2>/dev/null)
174
- if [ -n "${ARKAOS_DEBUG:-}" ]; then
175
- echo "[DEBUG] python_result length=${#python_result}" >&2
176
- fi
177
- elif [ -n "${ARKAOS_DEBUG:-}" ]; then
178
- echo "[DEBUG] bridge failed or returned empty, exit=$_bridge_status" >&2
179
- fi
180
- fi
181
-
182
- # Append workflow state to synapse context (always, if python result was set)
183
- if [ -n "$python_result" ]; then
184
- _WF_READER="$ARKAOS_ROOT/core/workflow/state_reader.sh"
185
- if [ -f "$_WF_READER" ] && bash "$_WF_READER" active 2>/dev/null; then
186
- _WF_SUM=$(bash "$_WF_READER" summary 2>/dev/null)
187
- _WF_N=$(echo "$_WF_SUM" | cut -d'|' -f1)
188
- _WF_P=$(echo "$_WF_SUM" | cut -d'|' -f2)
189
- _WF_B=$(echo "$_WF_SUM" | cut -d'|' -f4)
190
- _WF_V=$(echo "$_WF_SUM" | cut -d'|' -f5)
191
- _WF_TAG="[workflow:${_WF_N}] [phase:${_WF_P}] [branch:${_WF_B}] [violations:${_WF_V}]"
192
- [ "$_WF_V" != "0" ] && _WF_TAG="WARNING: ${_WF_V} workflow violation(s). $_WF_TAG"
193
- python_result="${python_result} ${_WF_TAG}"
194
- fi
195
-
196
- # --- Forge Context Injection ---
197
- _FORGE_ACTIVE="$HOME/.arkaos/plans/active.yaml"
198
- if [ -f "$_FORGE_ACTIVE" ]; then
199
- _FORGE_ID=$(cat "$_FORGE_ACTIVE" 2>/dev/null)
200
- _FORGE_FILE="$HOME/.arkaos/plans/${_FORGE_ID}.yaml"
201
- if [ -f "$_FORGE_FILE" ] && command -v python3 &>/dev/null; then
202
- _FORGE_STATUS=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('status',''))" 2>/dev/null)
203
- _FORGE_TAG="[forge:${_FORGE_ID}] [forge-status:${_FORGE_STATUS}]"
204
- python_result="${python_result} ${_FORGE_TAG}"
205
- fi
206
- fi
207
-
208
- # --- Knowledge Auto-Inject (On-Demand via Session Cache) ---
209
- if [ -n "$python_result" ] && [[ "$python_result" == *"[knowledge:"* ]]; then
210
- _KB_SESSION_ID="${ARKAOS_SESSION_ID:-${CLAUDE_SESSION_ID:-bridge-$$}}"
211
- _KB_PROJECT_HASH=$(echo "$ARKAOS_ROOT" | md5sum 2>/dev/null | cut -c1-12 || echo "default")
212
- _KB_CACHE_DIR="/tmp/arkaos-kb-${_KB_PROJECT_HASH}"
213
-
214
- if [ -n "${ARKAOS_DEBUG:-}" ]; then
215
- echo "[DEBUG] KB session_id=${_KB_SESSION_ID}, project_hash=${_KB_PROJECT_HASH}" >&2
216
- fi
217
-
218
- if [ -d "$_KB_CACHE_DIR" ] && command -v python3 &>/dev/null; then
219
- _KB_CONTENT=$(python3 -c "
220
- import sys
221
- sys.path.insert(0, '$ARKAOS_ROOT')
222
- from core.synapse.kb_cache import KBSessionCache
223
- cache = KBSessionCache(session_id='$_KB_SESSION_ID', project_path='$ARKAOS_ROOT')
224
- results = cache.get_overlap('''$user_input''', threshold=0.3)
225
- if results:
226
- snippets = []
227
- for r in results[:3]:
228
- src = r.get('source', '').split('/')[-1] if r.get('source') else ''
229
- txt = r.get('text', '')[:200].replace('\n', ' ')
230
- snippets.append(f'{src}: {txt}' if src else txt)
231
- print(' | '.join(snippets))
232
- " 2>/dev/null)
37
+ _ARKA_L0_FALLBACK='{"additionalContext": "[Constitution] NON-NEGOTIABLE: branch-isolation, obsidian-output, authority-boundaries, security-gate, context-first, solid-clean-code, spec-driven, human-writing, squad-routing, full-visibility, sequential-validation, mandatory-qa, arka-supremacy | QUALITY-GATE: marta-cqo, eduardo-copy, francisca-tech-ux | MUST: conventional-commits, test-coverage, pattern-matching, actionable-output, memory-persistence"}'
233
38
 
234
- if [ -n "$_KB_CONTENT" ]; then
235
- if [ -n "${ARKAOS_DEBUG:-}" ]; then
236
- echo "[DEBUG] KB auto-inject: ${#_KB_CONTENT} chars of knowledge" >&2
237
- fi
238
- python_result="${_KB_CONTENT} ${python_result}"
239
- fi
240
- fi
241
- fi
242
- fi
39
+ if ! command -v python3 &>/dev/null; then
40
+ echo "$_ARKA_L0_FALLBACK"
41
+ exit 0
243
42
  fi
244
-
245
- # ─── Fallback: Bash-only context (if Python unavailable) ────────────────
246
- if [ -z "$python_result" ]; then
247
- if [ -n "${ARKAOS_DEBUG:-}" ]; then
248
- echo "[DEBUG] Using bash fallback (python_result was empty)" >&2
249
- fi
250
- # L0: Constitution (cached)
251
- L0=""
252
- L0_CACHE="$CACHE_DIR/l0-constitution"
253
- if [ -f "$L0_CACHE" ] && [ $(($(date +%s) - $(stat -f%m "$L0_CACHE" 2>/dev/null || stat -c%Y "$L0_CACHE" 2>/dev/null || echo 0))) -lt $CACHE_TTL ]; then
254
- L0=$(cat "$L0_CACHE")
43
+ # Self-root fallback: the wrapper ships next to its python entrypoint, so
44
+ # when ARKAOS_ROOT points at a pre-PR-6 install without core/hooks/, use
45
+ # the root this script lives in.
46
+ if [ ! -f "$ARKAOS_ROOT/core/hooks/user_prompt_submit.py" ]; then
47
+ _SELF_ROOT="$(cd "$(dirname "$0")/../.." 2>/dev/null && pwd)"
48
+ if [ -n "$_SELF_ROOT" ] && [ -f "$_SELF_ROOT/core/hooks/user_prompt_submit.py" ]; then
49
+ ARKAOS_ROOT="$_SELF_ROOT"
50
+ export ARKAOS_ROOT
255
51
  else
256
- L0="[Constitution] NON-NEGOTIABLE: branch-isolation, obsidian-output, authority-boundaries, security-gate, context-first, solid-clean-code, spec-driven, human-writing, squad-routing, full-visibility, sequential-validation, mandatory-qa, arka-supremacy | QUALITY-GATE: marta-cqo, eduardo-copy, francisca-tech-ux | MUST: conventional-commits, test-coverage, pattern-matching, actionable-output, memory-persistence"
257
- echo "$L0" > "$L0_CACHE" 2>/dev/null
258
- fi
259
-
260
- # L4: Git branch
261
- L4=""
262
- branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
263
- if [ -n "$branch" ] && [ "$branch" != "main" ] && [ "$branch" != "master" ] && [ "$branch" != "dev" ]; then
264
- L4="[branch:$branch]"
265
- fi
266
-
267
- # L7: Time — intentionally skipped in bash fallback.
268
- # Low-signal tag that changed every prompt and invalidated prompt cache.
269
- # The Python TimeLayer (cache_ttl=3600) is authoritative when available.
270
- L7=""
271
-
272
- # L8: Workflow state
273
- L8=""
274
- _WF_READER="$ARKAOS_ROOT/core/workflow/state_reader.sh"
275
- if [ -f "$_WF_READER" ] && bash "$_WF_READER" active 2>/dev/null; then
276
- _WF_SUM=$(bash "$_WF_READER" summary 2>/dev/null)
277
- _WF_N=$(echo "$_WF_SUM" | cut -d'|' -f1)
278
- _WF_P=$(echo "$_WF_SUM" | cut -d'|' -f2)
279
- _WF_B=$(echo "$_WF_SUM" | cut -d'|' -f4)
280
- _WF_V=$(echo "$_WF_SUM" | cut -d'|' -f5)
281
- L8="[workflow:${_WF_N}] [phase:${_WF_P}] [branch:${_WF_B}] [violations:${_WF_V}]"
282
- [ "$_WF_V" != "0" ] && L8="WARNING: ${_WF_V} workflow violation(s). $L8"
283
- fi
284
-
285
- # L9: Forge state
286
- L9=""
287
- _FORGE_ACTIVE_FB="$HOME/.arkaos/plans/active.yaml"
288
- if [ -f "$_FORGE_ACTIVE_FB" ]; then
289
- _FORGE_ID_FB=$(cat "$_FORGE_ACTIVE_FB" 2>/dev/null)
290
- _FORGE_FILE_FB="$HOME/.arkaos/plans/${_FORGE_ID_FB}.yaml"
291
- if [ -f "$_FORGE_FILE_FB" ] && command -v python3 &>/dev/null; then
292
- _FORGE_STATUS_FB=$(FORGE_FILE="$_FORGE_FILE_FB" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('status',''))" 2>/dev/null)
293
- L9="[forge:${_FORGE_ID_FB}] [forge-status:${_FORGE_STATUS_FB}]"
294
- fi
295
- fi
296
-
297
- python_result="$L0 $L4 $L7 $L8 $L9"
298
- fi
299
-
300
- # ─── Token Hygiene suggestions (non-blocking) ───────────────────────────
301
- _HYGIENE=""
302
- _HYGIENE_SCRIPT="$(dirname "$0")/token-hygiene.sh"
303
- if [ -f "$_HYGIENE_SCRIPT" ]; then
304
- # Extract transcript path from input JSON if present
305
- _TRANSCRIPT=""
306
- if command -v jq &>/dev/null; then
307
- _TRANSCRIPT=$(echo "$input" | jq -r '.transcript_path // ""' 2>/dev/null)
308
- fi
309
- _HYGIENE=$(ARKA_PROMPT="$user_input" \
310
- ARKA_TRANSCRIPT_PATH="$_TRANSCRIPT" \
311
- CLAUDE_CONTEXT_USED="${CLAUDE_CONTEXT_USED:-}" \
312
- bash "$_HYGIENE_SCRIPT" 2>/dev/null)
313
- fi
314
-
315
- # ─── Persistent Routing Reminder ────────────────────────────────────────
316
- # High-salience tag — ensures squad routing persists across conversation turns,
317
- # not just on turn 1 when /arka skill content is fresh. See spec:
318
- # docs/superpowers/specs/2026-04-14-persistent-routing-reminder-design.md
319
- _ROUTE_REMINDER="
320
- [ARKA:ROUTE]
321
- EVERY response MUST route through a department squad.
322
- NO generic assistant replies. Announce the squad before responding.
323
- When [knowledge:N chunks] is present, cite at least one source.
324
- If [knowledge:N chunks] is absent on a non-trivial ArkaOS topic, query Obsidian first."
325
-
326
- # ─── Workflow Classifier (hard enforcement for creation/implementation) ──
327
- # Uses the shared _lib/workflow-classifier.sh. When a creation/implementation
328
- # verb is detected, the session is marked as flow-required so PreToolUse
329
- # can block Write/Edit/MultiEdit until the agent emits [arka:routing] or
330
- # [arka:trivial]. Explicit slash commands and bang shells pass through.
331
- _WORKFLOW_DIRECTIVE=""
332
- if [ -n "$user_input" ] && command -v arka_wf_classify &>/dev/null; then
333
- if [ "$(arka_wf_classify "$user_input")" = "true" ]; then
334
- # Mark session as flow-required (consumed by pre-tool-use.sh and stop.sh)
335
- if command -v arka_wf_mark_required &>/dev/null; then
336
- arka_wf_mark_required "$SESSION_ID"
337
- fi
338
- _WORKFLOW_DIRECTIVE="
339
- [ARKA:WORKFLOW-REQUIRED] Your user request matched a CREATION/IMPLEMENTATION pattern.
340
- The ArkaOS mandatory 13-phase flow applies. It is NON-NEGOTIABLE (constitution rule
341
- mandatory-flow). You MUST walk every phase, in order, emitting a [arka:phase:N] tag
342
- before each:
343
- 1. Input — restate the request verbatim.
344
- 2. Get context — profile, repo, git, cwd tag, session digests.
345
- 3. Decide route — emit [arka:routing] <dept> -> <lead>.
346
- 4. Call hierarchy — escalate to Tier 0 if strategic/cross-dept/security/financial.
347
- 5. Research — query Obsidian + vector DB, cite sources or declare the gap.
348
- 6. Call team — dispatch specialists via Agent tool.
349
- 7. Plan — run six parallel reviewers: positive, devil's advocate, Q&A, KB research,
350
- best-solution validator, pessimistic. Synthesise into a spec.
351
- 8. Present plan — save to Obsidian + vector DB + ~/.arkaos/plans/, print inline.
352
- 9. Wait approval — EXPLICIT user go. Silence is NOT approval.
353
- 10. TODO list — atomic, ordered, independently verifiable.
354
- 11. Per-todo loop — team call -> complete -> QA (all tests, E2E, Playwright) ->
355
- Security review -> Quality Gate (Marta + Eduardo + Francisca, Opus) -> Document.
356
- Each step loops back on fail. No compound gates.
357
- 12. Loop until TODO is exhausted.
358
- 13. Detailed summary — what was done, where, how to verify, what is still open.
359
-
360
- No Write, Edit, Bash-with-side-effects, or Agent dispatch before Phase 7 completes
361
- for the affected item. No advancing a todo until QA AND Security AND Quality Gate
362
- all pass for it. Phase 5 and Phase 8 require Obsidian/KB writes, not just reads.
363
-
364
- Trivial override: single-file edit under 10 lines with imperative verb
365
- (rename X, fix typo in Y). Emit [arka:trivial] <reason> as first line and proceed.
366
- Anything else runs the full 13 phases. Source: arka/skills/flow/SKILL.md.
367
-
368
- This is enforced by the hook and the session-start systemMessage, not by convention.
369
- Skipping violates: mandatory-flow, squad-routing, spec-driven, mandatory-qa,
370
- sequential-validation, full-visibility, arka-supremacy."
371
- fi
372
- fi
373
-
374
- # ─── Cognitive context injection (PR4 v2.26.0 hooks-as-retrieval) ───────
375
- # Read the context cache populated by the previous PostToolUse turn and
376
- # inject any KB hits as an `[arka:context]` advisory. The Python helper
377
- # has internal TTL filtering (10 min) and returns empty when there is no
378
- # fresh cache, so this stays a no-op until the cognitive layer has data
379
- # to share.
380
- _ARKA_CONTEXT_HITS=""
381
- if [ -n "$SESSION_ID" ]; then
382
- _ARKAOS_REPO=$(cat "$HOME/.arkaos/.repo-path" 2>/dev/null || echo "")
383
- if [ -n "$_ARKAOS_REPO" ] && [ -d "$_ARKAOS_REPO" ]; then
384
- _ARKA_CONTEXT_HITS=$(PYTHONPATH="$_ARKAOS_REPO" python3 -m core.cognition.retrieval inject "$SESSION_ID" 2>/dev/null)
385
- fi
386
- fi
387
-
388
- # ─── KB citation nudge (PR18 v2.40.0) ────────────────────────────────────
389
- # Read the cite-check result written by the previous Stop hook. If the
390
- # last assistant turn was on an ArkaOS topic without any citation, surface
391
- # the suggestion to the model in this turn's additionalContext. One-shot:
392
- # the file is deleted after read so the nudge does not repeat across turns.
393
- _KB_CITE_NUDGE=""
394
- if [ -n "$SESSION_ID" ] && [ "$_ARKA_SURFACE_NUDGES" = "true" ]; then
395
- _CITE_FILE="/tmp/arkaos-cite/${SESSION_ID}.json"
396
- if [ -f "$_CITE_FILE" ]; then
397
- if command -v jq &>/dev/null; then
398
- # NOTE: do not use `// true` here — jq's `//` treats false as needing
399
- # the default, which would suppress the nudge in the exact case we
400
- # care about. Read .passed raw and compare to the literal "false".
401
- _CITE_PASSED=$(jq -r '.passed' "$_CITE_FILE" 2>/dev/null)
402
- _CITE_SUGGEST=$(jq -r '.suggestion // ""' "$_CITE_FILE" 2>/dev/null)
403
- if [ "$_CITE_PASSED" = "false" ] && [ -n "$_CITE_SUGGEST" ] && [ "$_CITE_SUGGEST" != "null" ]; then
404
- _KB_CITE_NUDGE="[arka:suggest] ${_CITE_SUGGEST}"
405
- fi
406
- fi
407
- rm -f "$_CITE_FILE" 2>/dev/null
408
- fi
409
- fi
410
-
411
- # ─── Meta-tag nudge (PR30 v2.49.0) ───────────────────────────────────────
412
- # Mirror of the KB citation nudge but for the [arka:meta] one-liner
413
- # contract. One-shot; deleted after read.
414
- _META_TAG_NUDGE=""
415
- if [ -n "$SESSION_ID" ] && [ "$_ARKA_SURFACE_NUDGES" = "true" ]; then
416
- _META_FILE="/tmp/arkaos-meta/${SESSION_ID}.json"
417
- if [ -f "$_META_FILE" ]; then
418
- if command -v jq &>/dev/null; then
419
- _META_PASSED=$(jq -r '.passed' "$_META_FILE" 2>/dev/null)
420
- _META_SUGGEST=$(jq -r '.suggestion // ""' "$_META_FILE" 2>/dev/null)
421
- if [ "$_META_PASSED" = "false" ] && [ -n "$_META_SUGGEST" ] && [ "$_META_SUGGEST" != "null" ]; then
422
- _META_TAG_NUDGE="[arka:suggest] ${_META_SUGGEST}"
423
- fi
424
- fi
425
- rm -f "$_META_FILE" 2>/dev/null
426
- fi
427
- fi
428
-
429
- # ─── Closing-marker nudge (PR59 v2.76.0) ─────────────────────────────────
430
- # Mirror of meta-tag nudge but for [arka:phase:13] / [arka:trivial]
431
- # closing markers. One-shot; deleted after read.
432
- _CLOSING_MARKER_NUDGE=""
433
- if [ -n "$SESSION_ID" ] && [ "$_ARKA_SURFACE_NUDGES" = "true" ]; then
434
- _CLOSING_FILE="/tmp/arkaos-closing/${SESSION_ID}.json"
435
- if [ -f "$_CLOSING_FILE" ]; then
436
- if command -v jq &>/dev/null; then
437
- _CLOSING_PASSED=$(jq -r '.passed' "$_CLOSING_FILE" 2>/dev/null)
438
- _CLOSING_SUGGEST=$(jq -r '.suggestion // ""' "$_CLOSING_FILE" 2>/dev/null)
439
- if [ "$_CLOSING_PASSED" = "false" ] && [ -n "$_CLOSING_SUGGEST" ] && [ "$_CLOSING_SUGGEST" != "null" ]; then
440
- _CLOSING_MARKER_NUDGE="[arka:suggest] ${_CLOSING_SUGGEST}"
441
- fi
442
- fi
443
- rm -f "$_CLOSING_FILE" 2>/dev/null
52
+ echo "$_ARKA_L0_FALLBACK"
53
+ exit 0
444
54
  fi
445
55
  fi
446
56
 
447
- # ─── Output ──────────────────────────────────────────────────────────────
448
- _OUT_CONTEXT="${_ARKA_GREETING:-}${_SYNC_NOTICE:-}${_ROUTE_REMINDER}${_WORKFLOW_DIRECTIVE} $python_result"
449
- [ -n "$_HYGIENE" ] && _OUT_CONTEXT="$_OUT_CONTEXT $_HYGIENE"
450
- [ -n "$_KB_CITE_NUDGE" ] && _OUT_CONTEXT="$_OUT_CONTEXT
451
- $_KB_CITE_NUDGE"
452
- [ -n "$_META_TAG_NUDGE" ] && _OUT_CONTEXT="$_OUT_CONTEXT
453
- $_META_TAG_NUDGE"
454
- [ -n "$_CLOSING_MARKER_NUDGE" ] && _OUT_CONTEXT="$_OUT_CONTEXT
455
- $_CLOSING_MARKER_NUDGE"
456
- [ -n "$_ARKA_CONTEXT_HITS" ] && _OUT_CONTEXT="$_OUT_CONTEXT
457
- $_ARKA_CONTEXT_HITS"
458
- # Escape for JSON
459
- _OUT_JSON=$(python3 -c "import json,sys; print(json.dumps(sys.stdin.read()))" <<< "$_OUT_CONTEXT" 2>/dev/null)
460
- if [ -n "$_OUT_JSON" ]; then
461
- echo "{\"additionalContext\": $_OUT_JSON}"
462
- else
463
- echo "{\"additionalContext\": \"${_ARKA_GREETING:-}${_SYNC_NOTICE:-}$python_result\"}"
464
- fi
465
-
466
- # ─── Metrics ─────────────────────────────────────────────────────────────
467
- # Count @-mentions in the prompt for at_mention_frequency telemetry. Mirrors
468
- # the claude_code.at_mention OpenTelemetry event introduced in Claude Code
469
- # 2.1.122, but works on any version because we count from the same input
470
- # the hook already receives.
471
- at_mentions=$(printf '%s' "$user_input" | grep -oE '(^|[[:space:]])@[A-Za-z0-9_./-]+' | wc -l | tr -d ' ')
472
- elapsed=$(_hook_ms)
473
- if [ "$elapsed" -gt 0 ] 2>/dev/null; then
474
- echo "{\"hook\":\"user-prompt-submit-v2\",\"ms\":$elapsed,\"at_mentions\":${at_mentions:-0}}" >> "$CACHE_DIR/hook-metrics.jsonl" 2>/dev/null
475
- fi
57
+ PYTHONPATH="$ARKAOS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
58
+ exec python3 -m core.hooks.user_prompt_submit
@@ -54,6 +54,11 @@ class Insight:
54
54
  "date": date_str,
55
55
  "status": "surfaced",
56
56
  "confidence": self.confidence,
57
+ # Grounding quarantine (PR-3 v4.1): Dreaming output is
58
+ # LLM-inferred, not extracted from authoritative sources.
59
+ # Synapse L2.5 reads this marker and excludes (or explicitly
60
+ # labels) these notes so they never masquerade as grounded KB.
61
+ "grounding": "inferred",
57
62
  "sources": [f"[[{s}]]" for s in self.sources],
58
63
  "tags": ["arkaos-dream", *self.tags],
59
64
  "plugin_compat_version": "1.0",
@@ -2,6 +2,16 @@
2
2
 
3
3
  Reads a YAML schedule config, acquires a file lock to prevent duplicate runs,
4
4
  and executes Claude CLI commands with logging per task.
5
+
6
+ Goal mode (opt-in, v4.1.0): a schedule may pair `goal_condition` with
7
+ `task_budget` to append `--goal <condition> --task-budget <N>` to the
8
+ Claude CLI argv, so Research/Dreaming cycles run until the condition is
9
+ met instead of stopping when the prompt's phases run out. The pairing is
10
+ mandatory (`_goal_argv` raises otherwise) and only applies to the
11
+ prompt_file path — `python_module` entries ignore it. Commented-out
12
+ examples live in the installer-seeded template `config/cognition/
13
+ schedules.yaml` (deployed to `~/.arkaos/schedules.yaml`); nothing
14
+ goal-based auto-runs without the operator uncommenting it.
5
15
  """
6
16
 
7
17
  import os
@@ -67,6 +67,7 @@ from core.forge.orchestrator import (
67
67
 
68
68
  from core.forge.runtime_dispatcher import (
69
69
  ForgeTaskDispatcher,
70
+ ForgeDispatchUnavailableError,
70
71
  ClaudeCodeForgeDispatcher,
71
72
  ExplorerDispatchRequest,
72
73
  CriticDispatchRequest,
@@ -131,6 +132,7 @@ __all__ = [
131
132
  "CONSTITUTION_PHASES",
132
133
  # runtime dispatcher
133
134
  "ForgeTaskDispatcher",
135
+ "ForgeDispatchUnavailableError",
134
136
  "ClaudeCodeForgeDispatcher",
135
137
  "ExplorerDispatchRequest",
136
138
  "CriticDispatchRequest",