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/ipc.sh ADDED
@@ -0,0 +1,501 @@
1
+ #!/usr/bin/env bash
2
+ # AGENT-K IPC Library
3
+ # File-based inter-process communication between agents
4
+
5
+ set -euo pipefail
6
+
7
+ # Source core for utilities
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ source "$SCRIPT_DIR/core.sh"
10
+
11
+ # =============================================================================
12
+ # TASK STATUS CONSTANTS
13
+ # =============================================================================
14
+
15
+ STATUS_PENDING="pending"
16
+ STATUS_IN_PROGRESS="in_progress"
17
+ STATUS_COMPLETED="completed"
18
+ STATUS_FAILED="failed"
19
+ STATUS_CANCELLED="cancelled"
20
+
21
+ # Task types
22
+ TYPE_IMPLEMENT="implement"
23
+ TYPE_TEST="test"
24
+ TYPE_REVIEW="review"
25
+ TYPE_RESEARCH="research"
26
+ TYPE_EVALUATE="evaluate"
27
+
28
+ # =============================================================================
29
+ # TASK CREATION
30
+ # =============================================================================
31
+
32
+ create_task() {
33
+ local task_id="${1:-$(generate_task_id)}"
34
+ local task_type="$2"
35
+ local assigned_to="$3"
36
+ local prompt="$4"
37
+ local priority="${5:-1}"
38
+ local dependencies="${6:-[]}"
39
+ local context_files="${7:-[]}"
40
+
41
+ ensure_workspace
42
+
43
+ local task_file
44
+ task_file=$(get_task_file "$task_id")
45
+ local created_at
46
+ created_at=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
47
+
48
+ cat > "$task_file" <<EOF
49
+ {
50
+ "id": "$task_id",
51
+ "type": "$task_type",
52
+ "status": "$STATUS_PENDING",
53
+ "assigned_to": "$assigned_to",
54
+ "priority": $priority,
55
+ "created_at": "$created_at",
56
+ "started_at": null,
57
+ "completed_at": null,
58
+ "prompt": $(echo "$prompt" | jq -Rs .),
59
+ "context": {
60
+ "files": $context_files,
61
+ "dependencies": $dependencies
62
+ },
63
+ "result": null,
64
+ "error": null
65
+ }
66
+ EOF
67
+
68
+ log_debug "Created task: $task_id (assigned to: $assigned_to)"
69
+ echo "$task_id"
70
+ }
71
+
72
+ # =============================================================================
73
+ # TASK READING
74
+ # =============================================================================
75
+
76
+ read_task() {
77
+ local task_id="$1"
78
+ local task_file
79
+ task_file=$(get_task_file "$task_id")
80
+
81
+ if [[ -f "$task_file" ]]; then
82
+ cat "$task_file"
83
+ else
84
+ log_error "Task not found: $task_id"
85
+ return 1
86
+ fi
87
+ }
88
+
89
+ get_task_status() {
90
+ local task_id="$1"
91
+ local task_file
92
+ task_file=$(get_task_file "$task_id")
93
+
94
+ if [[ -f "$task_file" ]]; then
95
+ jq -r '.status' "$task_file"
96
+ else
97
+ echo "unknown"
98
+ fi
99
+ }
100
+
101
+ get_task_field() {
102
+ local task_id="$1"
103
+ local field="$2"
104
+ local task_file
105
+ task_file=$(get_task_file "$task_id")
106
+
107
+ if [[ -f "$task_file" ]]; then
108
+ jq -r ".$field" "$task_file"
109
+ else
110
+ echo ""
111
+ fi
112
+ }
113
+
114
+ # =============================================================================
115
+ # TASK UPDATING
116
+ # =============================================================================
117
+
118
+ update_task_status() {
119
+ local task_id="$1"
120
+ local new_status="$2"
121
+ local task_file
122
+ task_file=$(get_task_file "$task_id")
123
+
124
+ if [[ ! -f "$task_file" ]]; then
125
+ log_error "Task not found: $task_id"
126
+ return 1
127
+ fi
128
+
129
+ local tmp
130
+ tmp=$(mktemp)
131
+ local timestamp
132
+ timestamp=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
133
+
134
+ case "$new_status" in
135
+ "$STATUS_IN_PROGRESS")
136
+ jq ".status = \"$new_status\" | .started_at = \"$timestamp\"" "$task_file" > "$tmp"
137
+ ;;
138
+ "$STATUS_COMPLETED"|"$STATUS_FAILED"|"$STATUS_CANCELLED")
139
+ jq ".status = \"$new_status\" | .completed_at = \"$timestamp\"" "$task_file" > "$tmp"
140
+ ;;
141
+ *)
142
+ jq ".status = \"$new_status\"" "$task_file" > "$tmp"
143
+ ;;
144
+ esac
145
+
146
+ mv "$tmp" "$task_file"
147
+ log_debug "Updated task $task_id status to: $new_status"
148
+ }
149
+
150
+ update_task_field() {
151
+ local task_id="$1"
152
+ local field="$2"
153
+ local value="$3"
154
+ local task_file
155
+ task_file=$(get_task_file "$task_id")
156
+
157
+ if [[ ! -f "$task_file" ]]; then
158
+ log_error "Task not found: $task_id"
159
+ return 1
160
+ fi
161
+
162
+ local tmp
163
+ tmp=$(mktemp)
164
+ jq ".$field = $value" "$task_file" > "$tmp"
165
+ mv "$tmp" "$task_file"
166
+ }
167
+
168
+ # =============================================================================
169
+ # RESULT HANDLING
170
+ # =============================================================================
171
+
172
+ create_result() {
173
+ local task_id="$1"
174
+ local agent="$2"
175
+ local status="$3"
176
+ local output="$4"
177
+ local files_modified="${5:-[]}"
178
+ local next_steps="${6:-[]}"
179
+
180
+ ensure_workspace
181
+
182
+ local result_file
183
+ result_file=$(get_result_file "$task_id")
184
+ local completed_at
185
+ completed_at=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
186
+
187
+ cat > "$result_file" <<EOF
188
+ {
189
+ "task_id": "$task_id",
190
+ "agent": "$agent",
191
+ "status": "$status",
192
+ "output": $(echo "$output" | jq -Rs .),
193
+ "files_modified": $files_modified,
194
+ "next_steps": $next_steps,
195
+ "completed_at": "$completed_at"
196
+ }
197
+ EOF
198
+
199
+ # Also update the task with the result reference
200
+ update_task_field "$task_id" "result" "\"$result_file\""
201
+ update_task_status "$task_id" "$status"
202
+
203
+ log_debug "Created result for task: $task_id"
204
+ echo "$result_file"
205
+ }
206
+
207
+ read_result() {
208
+ local task_id="$1"
209
+ local result_file
210
+ result_file=$(get_result_file "$task_id")
211
+
212
+ if [[ -f "$result_file" ]]; then
213
+ cat "$result_file"
214
+ else
215
+ log_error "Result not found for task: $task_id"
216
+ return 1
217
+ fi
218
+ }
219
+
220
+ # =============================================================================
221
+ # TASK QUERIES
222
+ # =============================================================================
223
+
224
+ list_tasks() {
225
+ local status_filter="${1:-}"
226
+ local task_dir="$AGENTK_WORKSPACE/tasks"
227
+
228
+ if [[ ! -d "$task_dir" ]]; then
229
+ echo "[]"
230
+ return
231
+ fi
232
+
233
+ local tasks="[]"
234
+
235
+ for task_file in "$task_dir"/*.json; do
236
+ [[ -f "$task_file" ]] || continue
237
+
238
+ if [[ -z "$status_filter" ]]; then
239
+ tasks=$(echo "$tasks" | jq --slurpfile t "$task_file" '. + $t')
240
+ else
241
+ local task_status
242
+ task_status=$(jq -r '.status' "$task_file")
243
+ if [[ "$task_status" == "$status_filter" ]]; then
244
+ tasks=$(echo "$tasks" | jq --slurpfile t "$task_file" '. + $t')
245
+ fi
246
+ fi
247
+ done
248
+
249
+ echo "$tasks"
250
+ }
251
+
252
+ list_tasks_by_agent() {
253
+ local agent="$1"
254
+ local task_dir="$AGENTK_WORKSPACE/tasks"
255
+
256
+ if [[ ! -d "$task_dir" ]]; then
257
+ echo "[]"
258
+ return
259
+ fi
260
+
261
+ local tasks="[]"
262
+
263
+ for task_file in "$task_dir"/*.json; do
264
+ [[ -f "$task_file" ]] || continue
265
+
266
+ local assigned_to
267
+ assigned_to=$(jq -r '.assigned_to' "$task_file")
268
+ if [[ "$assigned_to" == "$agent" ]]; then
269
+ tasks=$(echo "$tasks" | jq --slurpfile t "$task_file" '. + $t')
270
+ fi
271
+ done
272
+
273
+ echo "$tasks"
274
+ }
275
+
276
+ get_pending_tasks() {
277
+ list_tasks "$STATUS_PENDING"
278
+ }
279
+
280
+ get_active_tasks() {
281
+ list_tasks "$STATUS_IN_PROGRESS"
282
+ }
283
+
284
+ get_completed_tasks() {
285
+ list_tasks "$STATUS_COMPLETED"
286
+ }
287
+
288
+ # =============================================================================
289
+ # DEPENDENCY CHECKING
290
+ # =============================================================================
291
+
292
+ check_dependencies_met() {
293
+ local task_id="$1"
294
+ local task_file
295
+ task_file=$(get_task_file "$task_id")
296
+
297
+ if [[ ! -f "$task_file" ]]; then
298
+ return 1
299
+ fi
300
+
301
+ local deps
302
+ deps=$(jq -r '.context.dependencies[]' "$task_file" 2>/dev/null || echo "")
303
+
304
+ if [[ -z "$deps" ]]; then
305
+ return 0 # No dependencies
306
+ fi
307
+
308
+ while IFS= read -r dep_id; do
309
+ [[ -z "$dep_id" ]] && continue
310
+
311
+ local dep_status
312
+ dep_status=$(get_task_status "$dep_id")
313
+ if [[ "$dep_status" != "$STATUS_COMPLETED" ]]; then
314
+ log_debug "Task $task_id waiting on dependency: $dep_id (status: $dep_status)"
315
+ return 1
316
+ fi
317
+ done <<< "$deps"
318
+
319
+ return 0
320
+ }
321
+
322
+ get_ready_tasks() {
323
+ local pending
324
+ pending=$(get_pending_tasks)
325
+
326
+ echo "$pending" | jq -c '.[]' | while read -r task; do
327
+ local task_id
328
+ task_id=$(echo "$task" | jq -r '.id')
329
+ if check_dependencies_met "$task_id"; then
330
+ echo "$task"
331
+ fi
332
+ done | jq -s '.'
333
+ }
334
+
335
+ # =============================================================================
336
+ # TASK WATCHING
337
+ # =============================================================================
338
+
339
+ watch_task() {
340
+ local task_id="$1"
341
+ local timeout="${2:-300}" # Default 5 minutes
342
+ local poll_interval="${3:-1}"
343
+
344
+ local elapsed=0
345
+ while [[ $elapsed -lt $timeout ]]; do
346
+ local status
347
+ status=$(get_task_status "$task_id")
348
+
349
+ case "$status" in
350
+ "$STATUS_COMPLETED"|"$STATUS_FAILED"|"$STATUS_CANCELLED")
351
+ echo "$status"
352
+ return 0
353
+ ;;
354
+ esac
355
+
356
+ sleep "$poll_interval"
357
+ elapsed=$((elapsed + poll_interval))
358
+ done
359
+
360
+ log_warn "Timeout waiting for task: $task_id"
361
+ echo "timeout"
362
+ return 1
363
+ }
364
+
365
+ watch_all_tasks() {
366
+ local callback="$1"
367
+ local poll_interval="${2:-1}"
368
+
369
+ while true; do
370
+ local active
371
+ active=$(get_active_tasks)
372
+ local count
373
+ count=$(echo "$active" | jq 'length')
374
+
375
+ if [[ "$count" -eq 0 ]]; then
376
+ # Check for pending tasks
377
+ local pending
378
+ pending=$(get_pending_tasks)
379
+ local pending_count
380
+ pending_count=$(echo "$pending" | jq 'length')
381
+
382
+ if [[ "$pending_count" -eq 0 ]]; then
383
+ log_debug "No active or pending tasks, stopping watch"
384
+ break
385
+ fi
386
+ fi
387
+
388
+ # Call the callback with current state
389
+ if [[ -n "$callback" ]] && type "$callback" &>/dev/null; then
390
+ "$callback" "$active"
391
+ fi
392
+
393
+ sleep "$poll_interval"
394
+ done
395
+ }
396
+
397
+ # =============================================================================
398
+ # CLEANUP
399
+ # =============================================================================
400
+
401
+ cancel_task() {
402
+ local task_id="$1"
403
+ update_task_status "$task_id" "$STATUS_CANCELLED"
404
+ log_info "Cancelled task: $task_id"
405
+ }
406
+
407
+ cancel_all_tasks() {
408
+ local task_dir="$AGENTK_WORKSPACE/tasks"
409
+
410
+ if [[ ! -d "$task_dir" ]]; then
411
+ return
412
+ fi
413
+
414
+ for task_file in "$task_dir"/*.json; do
415
+ [[ -f "$task_file" ]] || continue
416
+
417
+ local status
418
+ status=$(jq -r '.status' "$task_file")
419
+ if [[ "$status" == "$STATUS_PENDING" || "$status" == "$STATUS_IN_PROGRESS" ]]; then
420
+ local task_id
421
+ task_id=$(jq -r '.id' "$task_file")
422
+ cancel_task "$task_id"
423
+ fi
424
+ done
425
+ }
426
+
427
+ delete_task() {
428
+ local task_id="$1"
429
+ local task_file
430
+ task_file=$(get_task_file "$task_id")
431
+ local result_file
432
+ result_file=$(get_result_file "$task_id")
433
+
434
+ [[ -f "$task_file" ]] && rm "$task_file"
435
+ [[ -f "$result_file" ]] && rm "$result_file"
436
+
437
+ log_debug "Deleted task: $task_id"
438
+ }
439
+
440
+ # =============================================================================
441
+ # SESSION MANAGEMENT
442
+ # =============================================================================
443
+
444
+ create_session() {
445
+ local session_id
446
+ session_id=$(generate_session_id)
447
+ local session_file="$AGENTK_WORKSPACE/session.json"
448
+
449
+ ensure_workspace
450
+
451
+ cat > "$session_file" <<EOF
452
+ {
453
+ "id": "$session_id",
454
+ "started_at": "$(date -u '+%Y-%m-%dT%H:%M:%SZ')",
455
+ "mode": "${AGENTK_MODE:-dev}",
456
+ "agents": {},
457
+ "task_count": 0
458
+ }
459
+ EOF
460
+
461
+ echo "$session_id"
462
+ }
463
+
464
+ get_current_session() {
465
+ local session_file="$AGENTK_WORKSPACE/session.json"
466
+
467
+ if [[ -f "$session_file" ]]; then
468
+ cat "$session_file"
469
+ else
470
+ echo "{}"
471
+ fi
472
+ }
473
+
474
+ update_session_agent() {
475
+ local agent="$1"
476
+ local status="$2"
477
+ local message="${3:-}"
478
+ local session_file="$AGENTK_WORKSPACE/session.json"
479
+
480
+ if [[ ! -f "$session_file" ]]; then
481
+ return 1
482
+ fi
483
+
484
+ local tmp
485
+ tmp=$(mktemp)
486
+ jq ".agents[\"$agent\"] = {\"status\": \"$status\", \"message\": \"$message\", \"updated_at\": \"$(date -u '+%Y-%m-%dT%H:%M:%SZ')\"}" "$session_file" > "$tmp"
487
+ mv "$tmp" "$session_file"
488
+ }
489
+
490
+ end_session() {
491
+ local session_file="$AGENTK_WORKSPACE/session.json"
492
+
493
+ if [[ -f "$session_file" ]]; then
494
+ local tmp
495
+ tmp=$(mktemp)
496
+ jq ".ended_at = \"$(date -u '+%Y-%m-%dT%H:%M:%SZ')\"" "$session_file" > "$tmp"
497
+ mv "$tmp" "$session_file"
498
+ fi
499
+
500
+ cancel_all_tasks
501
+ }