agentk8 1.0.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.
package/lib/spawn.sh ADDED
@@ -0,0 +1,398 @@
1
+ #!/usr/bin/env bash
2
+ # AGENT-K Spawn Library
3
+ # Claude subprocess spawning and management
4
+
5
+ set -euo pipefail
6
+
7
+ # Source dependencies
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ source "$SCRIPT_DIR/core.sh"
10
+ source "$SCRIPT_DIR/ipc.sh"
11
+
12
+ # =============================================================================
13
+ # GLOBALS
14
+ # =============================================================================
15
+
16
+ # Track spawned agent PIDs
17
+ declare -A AGENT_PIDS=()
18
+ declare -A AGENT_TASKS=()
19
+
20
+ # =============================================================================
21
+ # AGENT PROMPT LOADING
22
+ # =============================================================================
23
+
24
+ get_agent_prompt_file() {
25
+ local mode="$1"
26
+ local agent="$2"
27
+
28
+ local prompt_file
29
+
30
+ # Check for shared agents first
31
+ if [[ -f "$AGENTK_ROOT/modes/shared/${agent}.md" ]]; then
32
+ prompt_file="$AGENTK_ROOT/modes/shared/${agent}.md"
33
+ else
34
+ prompt_file="$AGENTK_ROOT/modes/${mode}/${agent}.md"
35
+ fi
36
+
37
+ if [[ -f "$prompt_file" ]]; then
38
+ echo "$prompt_file"
39
+ else
40
+ log_error "Agent prompt not found: $prompt_file"
41
+ return 1
42
+ fi
43
+ }
44
+
45
+ load_agent_prompt() {
46
+ local mode="$1"
47
+ local agent="$2"
48
+
49
+ local prompt_file
50
+ prompt_file=$(get_agent_prompt_file "$mode" "$agent")
51
+
52
+ if [[ -f "$prompt_file" ]]; then
53
+ cat "$prompt_file"
54
+ else
55
+ return 1
56
+ fi
57
+ }
58
+
59
+ build_agent_system_prompt() {
60
+ local mode="$1"
61
+ local agent="$2"
62
+ local task_context="${3:-}"
63
+
64
+ local date_context
65
+ date_context=$(get_date_context)
66
+
67
+ local agent_prompt
68
+ agent_prompt=$(load_agent_prompt "$mode" "$agent")
69
+
70
+ cat <<EOF
71
+ $date_context
72
+
73
+ $agent_prompt
74
+
75
+ ---
76
+ CURRENT TASK CONTEXT:
77
+ $task_context
78
+ EOF
79
+ }
80
+
81
+ # =============================================================================
82
+ # AGENT SPAWNING
83
+ # =============================================================================
84
+
85
+ spawn_agent() {
86
+ local agent="$1"
87
+ local task_id="$2"
88
+ local mode="${3:-dev}"
89
+
90
+ local task_file
91
+ task_file=$(get_task_file "$task_id")
92
+
93
+ if [[ ! -f "$task_file" ]]; then
94
+ log_error "Task not found: $task_id"
95
+ return 1
96
+ fi
97
+
98
+ # Read task details
99
+ local prompt
100
+ prompt=$(jq -r '.prompt' "$task_file")
101
+ local context_files
102
+ context_files=$(jq -r '.context.files | join(", ")' "$task_file")
103
+
104
+ local task_context="Task ID: $task_id
105
+ Prompt: $prompt
106
+ Context Files: $context_files"
107
+
108
+ # Build system prompt
109
+ local system_prompt
110
+ system_prompt=$(build_agent_system_prompt "$mode" "$agent" "$task_context")
111
+
112
+ # Update task status
113
+ update_task_status "$task_id" "$STATUS_IN_PROGRESS"
114
+ update_session_agent "$agent" "working" "Processing task $task_id"
115
+
116
+ # Get agent log file
117
+ local log_file
118
+ log_file=$(get_agent_log "$agent")
119
+
120
+ log_info "Spawning agent: $agent for task: $task_id"
121
+
122
+ # Spawn claude in background
123
+ (
124
+ # Write system prompt to temp file for claude to read
125
+ local prompt_file
126
+ prompt_file=$(mktemp)
127
+ echo "$system_prompt" > "$prompt_file"
128
+
129
+ # Run claude with the task
130
+ local output
131
+ if output=$(claude --print "$prompt" --system-prompt "$prompt_file" 2>&1); then
132
+ # Success - create result
133
+ create_result "$task_id" "$agent" "$STATUS_COMPLETED" "$output" "[]" "[]"
134
+ update_session_agent "$agent" "done" "Completed task $task_id"
135
+ else
136
+ # Failed
137
+ create_result "$task_id" "$agent" "$STATUS_FAILED" "$output" "[]" "[]"
138
+ update_session_agent "$agent" "failed" "Failed task $task_id"
139
+ fi
140
+
141
+ # Cleanup
142
+ rm -f "$prompt_file"
143
+
144
+ ) >> "$log_file" 2>&1 &
145
+
146
+ local pid=$!
147
+ AGENT_PIDS[$agent]=$pid
148
+ AGENT_TASKS[$agent]=$task_id
149
+
150
+ log_debug "Agent $agent spawned with PID: $pid"
151
+ echo "$pid"
152
+ }
153
+
154
+ spawn_agent_interactive() {
155
+ local agent="$1"
156
+ local mode="${2:-dev}"
157
+ local initial_prompt="${3:-}"
158
+
159
+ # Build system prompt
160
+ local system_prompt
161
+ system_prompt=$(build_agent_system_prompt "$mode" "$agent" "Interactive session")
162
+
163
+ # Write system prompt to temp file
164
+ local prompt_file
165
+ prompt_file=$(mktemp)
166
+ echo "$system_prompt" > "$prompt_file"
167
+
168
+ log_info "Starting interactive session with agent: $agent"
169
+
170
+ # Run claude interactively
171
+ if [[ -n "$initial_prompt" ]]; then
172
+ claude --system-prompt "$prompt_file" "$initial_prompt"
173
+ else
174
+ claude --system-prompt "$prompt_file"
175
+ fi
176
+
177
+ # Cleanup
178
+ rm -f "$prompt_file"
179
+ }
180
+
181
+ # =============================================================================
182
+ # AGENT MANAGEMENT
183
+ # =============================================================================
184
+
185
+ is_agent_running() {
186
+ local agent="$1"
187
+
188
+ if [[ -z "${AGENT_PIDS[$agent]:-}" ]]; then
189
+ return 1
190
+ fi
191
+
192
+ local pid="${AGENT_PIDS[$agent]}"
193
+ if kill -0 "$pid" 2>/dev/null; then
194
+ return 0
195
+ else
196
+ # Agent finished, clean up
197
+ unset AGENT_PIDS[$agent]
198
+ return 1
199
+ fi
200
+ }
201
+
202
+ wait_agent() {
203
+ local agent="$1"
204
+ local timeout="${2:-300}"
205
+
206
+ if [[ -z "${AGENT_PIDS[$agent]:-}" ]]; then
207
+ log_warn "Agent not running: $agent"
208
+ return 1
209
+ fi
210
+
211
+ local pid="${AGENT_PIDS[$agent]}"
212
+ local elapsed=0
213
+
214
+ while kill -0 "$pid" 2>/dev/null && [[ $elapsed -lt $timeout ]]; do
215
+ sleep 1
216
+ elapsed=$((elapsed + 1))
217
+ done
218
+
219
+ if kill -0 "$pid" 2>/dev/null; then
220
+ log_warn "Timeout waiting for agent: $agent"
221
+ return 1
222
+ fi
223
+
224
+ # Get exit status
225
+ wait "$pid" 2>/dev/null || true
226
+ unset AGENT_PIDS[$agent]
227
+
228
+ log_debug "Agent $agent finished"
229
+ return 0
230
+ }
231
+
232
+ kill_agent() {
233
+ local agent="$1"
234
+
235
+ if [[ -z "${AGENT_PIDS[$agent]:-}" ]]; then
236
+ log_warn "Agent not running: $agent"
237
+ return 0
238
+ fi
239
+
240
+ local pid="${AGENT_PIDS[$agent]}"
241
+
242
+ # Try graceful shutdown first
243
+ kill -TERM "$pid" 2>/dev/null || true
244
+ sleep 1
245
+
246
+ # Force kill if still running
247
+ if kill -0 "$pid" 2>/dev/null; then
248
+ kill -KILL "$pid" 2>/dev/null || true
249
+ fi
250
+
251
+ # Cancel any active task
252
+ if [[ -n "${AGENT_TASKS[$agent]:-}" ]]; then
253
+ cancel_task "${AGENT_TASKS[$agent]}"
254
+ unset AGENT_TASKS[$agent]
255
+ fi
256
+
257
+ unset AGENT_PIDS[$agent]
258
+ update_session_agent "$agent" "stopped" "Killed by user"
259
+
260
+ log_info "Killed agent: $agent"
261
+ }
262
+
263
+ kill_all_agents() {
264
+ for agent in "${!AGENT_PIDS[@]}"; do
265
+ kill_agent "$agent"
266
+ done
267
+ }
268
+
269
+ # =============================================================================
270
+ # AGENT STATUS
271
+ # =============================================================================
272
+
273
+ get_agent_status() {
274
+ local agent="$1"
275
+
276
+ if is_agent_running "$agent"; then
277
+ echo "running"
278
+ elif [[ -n "${AGENT_TASKS[$agent]:-}" ]]; then
279
+ local task_status
280
+ task_status=$(get_task_status "${AGENT_TASKS[$agent]}")
281
+ echo "$task_status"
282
+ else
283
+ echo "idle"
284
+ fi
285
+ }
286
+
287
+ get_all_agent_status() {
288
+ local mode="${1:-dev}"
289
+ local agents=()
290
+
291
+ case "$mode" in
292
+ dev) agents=("orchestrator" "engineer" "tester" "security" "scout") ;;
293
+ ml) agents=("orchestrator" "researcher" "ml-engineer" "data-engineer" "evaluator" "scout") ;;
294
+ esac
295
+
296
+ echo "{"
297
+ local first=true
298
+ for agent in "${agents[@]}"; do
299
+ local status
300
+ status=$(get_agent_status "$agent")
301
+ local message=""
302
+
303
+ if [[ -n "${AGENT_TASKS[$agent]:-}" ]]; then
304
+ message=$(get_task_field "${AGENT_TASKS[$agent]}" "prompt" | head -c 50)
305
+ fi
306
+
307
+ if [[ "$first" == "true" ]]; then
308
+ first=false
309
+ else
310
+ echo ","
311
+ fi
312
+
313
+ printf ' "%s": {"status": "%s", "message": "%s"}' "$agent" "$status" "$message"
314
+ done
315
+ echo
316
+ echo "}"
317
+ }
318
+
319
+ # =============================================================================
320
+ # AGENT LOG VIEWING
321
+ # =============================================================================
322
+
323
+ view_agent_log() {
324
+ local agent="$1"
325
+ local lines="${2:-50}"
326
+
327
+ local log_file
328
+ log_file=$(get_agent_log "$agent")
329
+
330
+ if [[ -f "$log_file" ]]; then
331
+ tail -n "$lines" "$log_file"
332
+ else
333
+ echo "No logs found for agent: $agent"
334
+ fi
335
+ }
336
+
337
+ follow_agent_log() {
338
+ local agent="$1"
339
+
340
+ local log_file
341
+ log_file=$(get_agent_log "$agent")
342
+
343
+ if [[ -f "$log_file" ]]; then
344
+ tail -f "$log_file"
345
+ else
346
+ echo "No logs found for agent: $agent"
347
+ fi
348
+ }
349
+
350
+ clear_agent_log() {
351
+ local agent="$1"
352
+
353
+ local log_file
354
+ log_file=$(get_agent_log "$agent")
355
+
356
+ if [[ -f "$log_file" ]]; then
357
+ > "$log_file"
358
+ log_debug "Cleared log for agent: $agent"
359
+ fi
360
+ }
361
+
362
+ # =============================================================================
363
+ # PARALLEL SPAWNING
364
+ # =============================================================================
365
+
366
+ spawn_agents_parallel() {
367
+ local mode="$1"
368
+ shift
369
+ local task_ids=("$@")
370
+
371
+ local pids=()
372
+
373
+ for task_id in "${task_ids[@]}"; do
374
+ local assigned_to
375
+ assigned_to=$(get_task_field "$task_id" "assigned_to")
376
+
377
+ spawn_agent "$assigned_to" "$task_id" "$mode" &
378
+ pids+=($!)
379
+ done
380
+
381
+ # Wait for all spawns to complete
382
+ for pid in "${pids[@]}"; do
383
+ wait "$pid" 2>/dev/null || true
384
+ done
385
+ }
386
+
387
+ # =============================================================================
388
+ # CLEANUP
389
+ # =============================================================================
390
+
391
+ cleanup_agents() {
392
+ log_info "Cleaning up agents..."
393
+ kill_all_agents
394
+ cancel_all_tasks
395
+ }
396
+
397
+ # Trap for cleanup on exit
398
+ trap cleanup_agents EXIT INT TERM