aether-colony 5.3.2 → 5.4.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 (281) hide show
  1. package/.aether/aether-utils.sh +181 -5
  2. package/.aether/commands/archaeology.yaml +3 -3
  3. package/.aether/commands/build.yaml +80 -45
  4. package/.aether/commands/chaos.yaml +7 -7
  5. package/.aether/commands/colonize.yaml +17 -17
  6. package/.aether/commands/continue.yaml +40 -40
  7. package/.aether/commands/council.yaml +6 -6
  8. package/.aether/commands/data-clean.yaml +3 -3
  9. package/.aether/commands/dream.yaml +2 -2
  10. package/.aether/commands/entomb.yaml +12 -12
  11. package/.aether/commands/export-signals.yaml +2 -2
  12. package/.aether/commands/feedback.yaml +6 -6
  13. package/.aether/commands/flag.yaml +2 -2
  14. package/.aether/commands/flags.yaml +4 -4
  15. package/.aether/commands/focus.yaml +6 -6
  16. package/.aether/commands/help.yaml +1 -1
  17. package/.aether/commands/history.yaml +1 -1
  18. package/.aether/commands/import-signals.yaml +2 -2
  19. package/.aether/commands/init.yaml +44 -27
  20. package/.aether/commands/insert-phase.yaml +1 -1
  21. package/.aether/commands/interpret.yaml +2 -2
  22. package/.aether/commands/lay-eggs.yaml +3 -3
  23. package/.aether/commands/maturity.yaml +2 -2
  24. package/.aether/commands/memory-details.yaml +1 -1
  25. package/.aether/commands/migrate-state.yaml +1 -1
  26. package/.aether/commands/oracle.yaml +147 -82
  27. package/.aether/commands/organize.yaml +5 -5
  28. package/.aether/commands/patrol.yaml +8 -8
  29. package/.aether/commands/pause-colony.yaml +7 -7
  30. package/.aether/commands/phase.yaml +1 -1
  31. package/.aether/commands/pheromones.yaml +1 -1
  32. package/.aether/commands/plan.yaml +14 -14
  33. package/.aether/commands/quick.yaml +4 -4
  34. package/.aether/commands/redirect.yaml +6 -6
  35. package/.aether/commands/resume-colony.yaml +9 -9
  36. package/.aether/commands/resume.yaml +5 -38
  37. package/.aether/commands/run.yaml +10 -10
  38. package/.aether/commands/seal.yaml +33 -33
  39. package/.aether/commands/skill-create.yaml +4 -4
  40. package/.aether/commands/status.yaml +14 -14
  41. package/.aether/commands/swarm.yaml +14 -14
  42. package/.aether/commands/tunnels.yaml +7 -7
  43. package/.aether/commands/update.yaml +1 -1
  44. package/.aether/commands/verify-castes.yaml +3 -3
  45. package/.aether/commands/watch.yaml +15 -15
  46. package/.aether/docs/command-playbooks/build-complete.md +48 -15
  47. package/.aether/docs/command-playbooks/build-context.md +11 -11
  48. package/.aether/docs/command-playbooks/build-full.md +76 -76
  49. package/.aether/docs/command-playbooks/build-prep.md +10 -10
  50. package/.aether/docs/command-playbooks/build-verify.md +27 -27
  51. package/.aether/docs/command-playbooks/build-wave.md +38 -38
  52. package/.aether/docs/command-playbooks/continue-advance.md +60 -27
  53. package/.aether/docs/command-playbooks/continue-finalize.md +25 -11
  54. package/.aether/docs/command-playbooks/continue-full.md +60 -46
  55. package/.aether/docs/command-playbooks/continue-gates.md +18 -18
  56. package/.aether/docs/command-playbooks/continue-verify.md +10 -10
  57. package/.aether/docs/source-of-truth-map.md +10 -10
  58. package/.aether/docs/structural-learning-stack.md +283 -0
  59. package/.aether/templates/colony-state-template.json +1 -0
  60. package/.aether/utils/consolidation-seal.sh +196 -0
  61. package/.aether/utils/consolidation.sh +127 -0
  62. package/.aether/utils/curation-ants/archivist.sh +97 -0
  63. package/.aether/utils/curation-ants/critic.sh +214 -0
  64. package/.aether/utils/curation-ants/herald.sh +102 -0
  65. package/.aether/utils/curation-ants/janitor.sh +121 -0
  66. package/.aether/utils/curation-ants/librarian.sh +99 -0
  67. package/.aether/utils/curation-ants/nurse.sh +153 -0
  68. package/.aether/utils/curation-ants/orchestrator.sh +181 -0
  69. package/.aether/utils/curation-ants/scribe.sh +164 -0
  70. package/.aether/utils/curation-ants/sentinel.sh +119 -0
  71. package/.aether/utils/event-bus.sh +301 -0
  72. package/.aether/utils/graph.sh +559 -0
  73. package/.aether/utils/instinct-store.sh +401 -0
  74. package/.aether/utils/learning.sh +79 -7
  75. package/.aether/utils/oracle/oracle-stop-hook.sh +896 -0
  76. package/.aether/utils/session.sh +13 -0
  77. package/.aether/utils/state-api.sh +1 -1
  78. package/.aether/utils/trust-scoring.sh +347 -0
  79. package/.aether/utils/worktree.sh +97 -0
  80. package/.claude/commands/ant/archaeology.md +2 -2
  81. package/.claude/commands/ant/chaos.md +4 -4
  82. package/.claude/commands/ant/colonize.md +9 -9
  83. package/.claude/commands/ant/council.md +6 -6
  84. package/.claude/commands/ant/data-clean.md +3 -3
  85. package/.claude/commands/ant/dream.md +2 -2
  86. package/.claude/commands/ant/entomb.md +9 -9
  87. package/.claude/commands/ant/export-signals.md +2 -2
  88. package/.claude/commands/ant/feedback.md +4 -4
  89. package/.claude/commands/ant/flag.md +2 -2
  90. package/.claude/commands/ant/flags.md +4 -4
  91. package/.claude/commands/ant/focus.md +4 -4
  92. package/.claude/commands/ant/help.md +1 -1
  93. package/.claude/commands/ant/history.md +1 -1
  94. package/.claude/commands/ant/import-signals.md +2 -2
  95. package/.claude/commands/ant/init.md +44 -27
  96. package/.claude/commands/ant/insert-phase.md +1 -1
  97. package/.claude/commands/ant/interpret.md +2 -2
  98. package/.claude/commands/ant/lay-eggs.md +2 -2
  99. package/.claude/commands/ant/maturity.md +2 -2
  100. package/.claude/commands/ant/memory-details.md +1 -1
  101. package/.claude/commands/ant/migrate-state.md +1 -1
  102. package/.claude/commands/ant/oracle.md +78 -42
  103. package/.claude/commands/ant/organize.md +3 -3
  104. package/.claude/commands/ant/patrol.md +8 -8
  105. package/.claude/commands/ant/pause-colony.md +5 -5
  106. package/.claude/commands/ant/phase.md +1 -1
  107. package/.claude/commands/ant/pheromones.md +1 -1
  108. package/.claude/commands/ant/plan.md +8 -8
  109. package/.claude/commands/ant/quick.md +4 -4
  110. package/.claude/commands/ant/redirect.md +4 -4
  111. package/.claude/commands/ant/resume-colony.md +5 -5
  112. package/.claude/commands/ant/resume.md +17 -29
  113. package/.claude/commands/ant/run.md +10 -10
  114. package/.claude/commands/ant/seal.md +25 -25
  115. package/.claude/commands/ant/skill-create.md +2 -2
  116. package/.claude/commands/ant/status.md +14 -14
  117. package/.claude/commands/ant/swarm.md +14 -14
  118. package/.claude/commands/ant/tunnels.md +4 -4
  119. package/.claude/commands/ant/update.md +1 -1
  120. package/.claude/commands/ant/verify-castes.md +2 -2
  121. package/.claude/commands/ant/watch.md +8 -8
  122. package/.opencode/commands/ant/archaeology.md +1 -1
  123. package/.opencode/commands/ant/build.md +80 -45
  124. package/.opencode/commands/ant/chaos.md +3 -3
  125. package/.opencode/commands/ant/colonize.md +8 -8
  126. package/.opencode/commands/ant/continue.md +40 -40
  127. package/.opencode/commands/ant/council.md +5 -5
  128. package/.opencode/commands/ant/data-clean.md +2 -2
  129. package/.opencode/commands/ant/dream.md +1 -1
  130. package/.opencode/commands/ant/entomb.md +3 -3
  131. package/.opencode/commands/ant/export-signals.md +1 -1
  132. package/.opencode/commands/ant/feedback.md +2 -2
  133. package/.opencode/commands/ant/flag.md +1 -1
  134. package/.opencode/commands/ant/flags.md +3 -3
  135. package/.opencode/commands/ant/focus.md +2 -2
  136. package/.opencode/commands/ant/import-signals.md +1 -1
  137. package/.opencode/commands/ant/init.md +44 -27
  138. package/.opencode/commands/ant/insert-phase.md +1 -1
  139. package/.opencode/commands/ant/interpret.md +1 -1
  140. package/.opencode/commands/ant/lay-eggs.md +2 -2
  141. package/.opencode/commands/ant/maturity.md +1 -1
  142. package/.opencode/commands/ant/memory-details.md +1 -1
  143. package/.opencode/commands/ant/oracle.md +69 -40
  144. package/.opencode/commands/ant/organize.md +2 -2
  145. package/.opencode/commands/ant/patrol.md +8 -8
  146. package/.opencode/commands/ant/pause-colony.md +2 -2
  147. package/.opencode/commands/ant/pheromones.md +1 -1
  148. package/.opencode/commands/ant/plan.md +6 -6
  149. package/.opencode/commands/ant/quick.md +4 -4
  150. package/.opencode/commands/ant/redirect.md +2 -2
  151. package/.opencode/commands/ant/resume-colony.md +4 -4
  152. package/.opencode/commands/ant/resume.md +5 -17
  153. package/.opencode/commands/ant/run.md +10 -10
  154. package/.opencode/commands/ant/seal.md +8 -8
  155. package/.opencode/commands/ant/skill-create.md +2 -2
  156. package/.opencode/commands/ant/status.md +10 -10
  157. package/.opencode/commands/ant/tunnels.md +3 -3
  158. package/.opencode/commands/ant/verify-castes.md +1 -1
  159. package/.opencode/commands/ant/watch.md +7 -7
  160. package/CHANGELOG.md +83 -0
  161. package/README.md +22 -9
  162. package/bin/cli.js +118 -3
  163. package/bin/lib/binary-downloader.js +267 -0
  164. package/bin/lib/update-transaction.js +27 -3
  165. package/bin/lib/version-gate.js +179 -0
  166. package/bin/npx-entry.js +0 -0
  167. package/package.json +1 -1
  168. package/.aether/agents/aether-ambassador.md +0 -140
  169. package/.aether/agents/aether-archaeologist.md +0 -108
  170. package/.aether/agents/aether-architect.md +0 -133
  171. package/.aether/agents/aether-auditor.md +0 -144
  172. package/.aether/agents/aether-builder.md +0 -184
  173. package/.aether/agents/aether-chaos.md +0 -115
  174. package/.aether/agents/aether-chronicler.md +0 -122
  175. package/.aether/agents/aether-gatekeeper.md +0 -116
  176. package/.aether/agents/aether-includer.md +0 -117
  177. package/.aether/agents/aether-keeper.md +0 -177
  178. package/.aether/agents/aether-measurer.md +0 -128
  179. package/.aether/agents/aether-oracle.md +0 -137
  180. package/.aether/agents/aether-probe.md +0 -133
  181. package/.aether/agents/aether-queen.md +0 -286
  182. package/.aether/agents/aether-route-setter.md +0 -130
  183. package/.aether/agents/aether-sage.md +0 -106
  184. package/.aether/agents/aether-scout.md +0 -101
  185. package/.aether/agents/aether-surveyor-disciplines.md +0 -391
  186. package/.aether/agents/aether-surveyor-nest.md +0 -329
  187. package/.aether/agents/aether-surveyor-pathogens.md +0 -264
  188. package/.aether/agents/aether-surveyor-provisions.md +0 -334
  189. package/.aether/agents/aether-tracker.md +0 -137
  190. package/.aether/agents/aether-watcher.md +0 -174
  191. package/.aether/agents/aether-weaver.md +0 -130
  192. package/.aether/commands/claude/archaeology.md +0 -334
  193. package/.aether/commands/claude/build.md +0 -65
  194. package/.aether/commands/claude/chaos.md +0 -336
  195. package/.aether/commands/claude/colonize.md +0 -259
  196. package/.aether/commands/claude/continue.md +0 -60
  197. package/.aether/commands/claude/council.md +0 -507
  198. package/.aether/commands/claude/data-clean.md +0 -81
  199. package/.aether/commands/claude/dream.md +0 -268
  200. package/.aether/commands/claude/entomb.md +0 -498
  201. package/.aether/commands/claude/export-signals.md +0 -57
  202. package/.aether/commands/claude/feedback.md +0 -96
  203. package/.aether/commands/claude/flag.md +0 -151
  204. package/.aether/commands/claude/flags.md +0 -169
  205. package/.aether/commands/claude/focus.md +0 -76
  206. package/.aether/commands/claude/help.md +0 -154
  207. package/.aether/commands/claude/history.md +0 -140
  208. package/.aether/commands/claude/import-signals.md +0 -71
  209. package/.aether/commands/claude/init.md +0 -505
  210. package/.aether/commands/claude/insert-phase.md +0 -105
  211. package/.aether/commands/claude/interpret.md +0 -278
  212. package/.aether/commands/claude/lay-eggs.md +0 -210
  213. package/.aether/commands/claude/maturity.md +0 -113
  214. package/.aether/commands/claude/memory-details.md +0 -77
  215. package/.aether/commands/claude/migrate-state.md +0 -171
  216. package/.aether/commands/claude/oracle.md +0 -642
  217. package/.aether/commands/claude/organize.md +0 -232
  218. package/.aether/commands/claude/patrol.md +0 -620
  219. package/.aether/commands/claude/pause-colony.md +0 -233
  220. package/.aether/commands/claude/phase.md +0 -115
  221. package/.aether/commands/claude/pheromones.md +0 -156
  222. package/.aether/commands/claude/plan.md +0 -693
  223. package/.aether/commands/claude/preferences.md +0 -65
  224. package/.aether/commands/claude/quick.md +0 -100
  225. package/.aether/commands/claude/redirect.md +0 -76
  226. package/.aether/commands/claude/resume-colony.md +0 -197
  227. package/.aether/commands/claude/resume.md +0 -388
  228. package/.aether/commands/claude/run.md +0 -231
  229. package/.aether/commands/claude/seal.md +0 -774
  230. package/.aether/commands/claude/skill-create.md +0 -286
  231. package/.aether/commands/claude/status.md +0 -410
  232. package/.aether/commands/claude/swarm.md +0 -349
  233. package/.aether/commands/claude/tunnels.md +0 -426
  234. package/.aether/commands/claude/update.md +0 -132
  235. package/.aether/commands/claude/verify-castes.md +0 -143
  236. package/.aether/commands/claude/watch.md +0 -239
  237. package/.aether/commands/opencode/archaeology.md +0 -331
  238. package/.aether/commands/opencode/build.md +0 -1168
  239. package/.aether/commands/opencode/chaos.md +0 -329
  240. package/.aether/commands/opencode/colonize.md +0 -195
  241. package/.aether/commands/opencode/continue.md +0 -1436
  242. package/.aether/commands/opencode/council.md +0 -437
  243. package/.aether/commands/opencode/data-clean.md +0 -77
  244. package/.aether/commands/opencode/dream.md +0 -260
  245. package/.aether/commands/opencode/entomb.md +0 -377
  246. package/.aether/commands/opencode/export-signals.md +0 -54
  247. package/.aether/commands/opencode/feedback.md +0 -99
  248. package/.aether/commands/opencode/flag.md +0 -149
  249. package/.aether/commands/opencode/flags.md +0 -167
  250. package/.aether/commands/opencode/focus.md +0 -73
  251. package/.aether/commands/opencode/help.md +0 -157
  252. package/.aether/commands/opencode/history.md +0 -136
  253. package/.aether/commands/opencode/import-signals.md +0 -68
  254. package/.aether/commands/opencode/init.md +0 -518
  255. package/.aether/commands/opencode/insert-phase.md +0 -111
  256. package/.aether/commands/opencode/interpret.md +0 -272
  257. package/.aether/commands/opencode/lay-eggs.md +0 -213
  258. package/.aether/commands/opencode/maturity.md +0 -108
  259. package/.aether/commands/opencode/memory-details.md +0 -83
  260. package/.aether/commands/opencode/migrate-state.md +0 -165
  261. package/.aether/commands/opencode/oracle.md +0 -593
  262. package/.aether/commands/opencode/organize.md +0 -226
  263. package/.aether/commands/opencode/patrol.md +0 -626
  264. package/.aether/commands/opencode/pause-colony.md +0 -203
  265. package/.aether/commands/opencode/phase.md +0 -113
  266. package/.aether/commands/opencode/pheromones.md +0 -162
  267. package/.aether/commands/opencode/plan.md +0 -684
  268. package/.aether/commands/opencode/preferences.md +0 -71
  269. package/.aether/commands/opencode/quick.md +0 -91
  270. package/.aether/commands/opencode/redirect.md +0 -84
  271. package/.aether/commands/opencode/resume-colony.md +0 -190
  272. package/.aether/commands/opencode/resume.md +0 -394
  273. package/.aether/commands/opencode/run.md +0 -237
  274. package/.aether/commands/opencode/seal.md +0 -452
  275. package/.aether/commands/opencode/skill-create.md +0 -63
  276. package/.aether/commands/opencode/status.md +0 -307
  277. package/.aether/commands/opencode/swarm.md +0 -15
  278. package/.aether/commands/opencode/tunnels.md +0 -400
  279. package/.aether/commands/opencode/update.md +0 -127
  280. package/.aether/commands/opencode/verify-castes.md +0 -139
  281. package/.aether/commands/opencode/watch.md +0 -227
@@ -0,0 +1,301 @@
1
+ #!/bin/bash
2
+ # Event bus utility functions for the Aether Structural Learning Stack
3
+ # Provides: _event_publish, _event_subscribe, _event_cleanup, _event_replay
4
+ #
5
+ # These functions are sourced by aether-utils.sh at startup.
6
+ # All shared infrastructure (json_ok, json_err, json_warn, atomic_write, acquire_lock,
7
+ # release_lock, feature_enabled, LOCK_DIR, DATA_DIR, SCRIPT_DIR, error constants) is available.
8
+
9
+ # Default TTL for events in days
10
+ _EVENT_BUS_DEFAULT_TTL=30
11
+ _EVENT_BUS_DEFAULT_LIMIT=50
12
+
13
+ # ============================================================================
14
+ # _event_publish
15
+ # Publish an event to the JSONL event bus.
16
+ # Usage: event-publish --topic <topic> --payload <json> [--source <src>] [--ttl <days>]
17
+ # ============================================================================
18
+ _event_publish() {
19
+ local ep_topic=""
20
+ local ep_payload=""
21
+ local ep_source="system"
22
+ local ep_ttl="$_EVENT_BUS_DEFAULT_TTL"
23
+
24
+ # Parse arguments
25
+ while [[ $# -gt 0 ]]; do
26
+ case "$1" in
27
+ --topic)
28
+ ep_topic="${2:-}"
29
+ shift 2
30
+ ;;
31
+ --payload)
32
+ ep_payload="${2:-}"
33
+ shift 2
34
+ ;;
35
+ --source)
36
+ ep_source="${2:-system}"
37
+ shift 2
38
+ ;;
39
+ --ttl)
40
+ ep_ttl="${2:-$_EVENT_BUS_DEFAULT_TTL}"
41
+ shift 2
42
+ ;;
43
+ *)
44
+ shift
45
+ ;;
46
+ esac
47
+ done
48
+
49
+ [[ -z "$ep_topic" ]] && json_err "$E_VALIDATION_FAILED" "event-publish requires --topic"
50
+ [[ -z "$ep_payload" ]] && json_err "$E_VALIDATION_FAILED" "event-publish requires --payload"
51
+
52
+ # Validate payload is valid JSON
53
+ echo "$ep_payload" | jq empty 2>/dev/null \
54
+ || json_err "$E_JSON_INVALID" "event-publish --payload must be valid JSON"
55
+
56
+ mkdir -p "$COLONY_DATA_DIR"
57
+ local bus_file="$COLONY_DATA_DIR/event-bus.jsonl"
58
+
59
+ # Generate unique ID and timestamps
60
+ local ep_id
61
+ ep_id="evt_$(date +%s)_$(head -c 2 /dev/urandom | od -An -tx1 | tr -d ' \n')"
62
+ local ep_ts
63
+ ep_ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
64
+ local ep_expires
65
+ ep_expires=$(date -u -v+"${ep_ttl}"d +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
66
+ || date -u -d "+${ep_ttl} days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
67
+ || echo "2099-01-01T00:00:00Z")
68
+
69
+ # Build event JSON line
70
+ local ep_line
71
+ ep_line=$(jq -nc \
72
+ --arg id "$ep_id" \
73
+ --arg topic "$ep_topic" \
74
+ --argjson payload "$ep_payload" \
75
+ --arg source "$ep_source" \
76
+ --arg ts "$ep_ts" \
77
+ --argjson ttl_days "$ep_ttl" \
78
+ --arg expires_at "$ep_expires" \
79
+ '{id:$id,topic:$topic,payload:$payload,source:$source,timestamp:$ts,ttl_days:$ttl_days,expires_at:$expires_at}')
80
+
81
+ # Acquire lock for safe concurrent append
82
+ acquire_lock "event-bus" 5 2>/dev/null \
83
+ || json_err "$E_LOCK_FAILED" "event-publish: failed to acquire lock"
84
+ trap 'release_lock "event-bus" 2>/dev/null || true' EXIT
85
+
86
+ echo "$ep_line" >> "$bus_file"
87
+
88
+ release_lock "event-bus" 2>/dev/null || true
89
+
90
+ json_ok "$(jq -nc \
91
+ --arg event_id "$ep_id" \
92
+ --arg topic "$ep_topic" \
93
+ --argjson ttl_days "$ep_ttl" \
94
+ '{event_id:$event_id,topic:$topic,ttl_days:$ttl_days}')"
95
+ }
96
+
97
+ # ============================================================================
98
+ # _event_subscribe
99
+ # Read events matching a topic pattern from the JSONL bus.
100
+ # Usage: event-subscribe --topic <pattern> [--since <ISO-8601>] [--limit <N>]
101
+ # Pattern supports exact match or prefix with trailing '*' (e.g., "learning.*")
102
+ # ============================================================================
103
+ _event_subscribe() {
104
+ local es_topic=""
105
+ local es_since=""
106
+ local es_limit="$_EVENT_BUS_DEFAULT_LIMIT"
107
+
108
+ while [[ $# -gt 0 ]]; do
109
+ case "$1" in
110
+ --topic)
111
+ es_topic="${2:-}"
112
+ shift 2
113
+ ;;
114
+ --since)
115
+ es_since="${2:-}"
116
+ shift 2
117
+ ;;
118
+ --limit)
119
+ es_limit="${2:-$_EVENT_BUS_DEFAULT_LIMIT}"
120
+ shift 2
121
+ ;;
122
+ *)
123
+ shift
124
+ ;;
125
+ esac
126
+ done
127
+
128
+ [[ -z "$es_topic" ]] && json_err "$E_VALIDATION_FAILED" "event-subscribe requires --topic"
129
+
130
+ local bus_file="$COLONY_DATA_DIR/event-bus.jsonl"
131
+ local now_ts
132
+ now_ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
133
+
134
+ # If bus file does not exist, return empty result
135
+ if [[ ! -f "$bus_file" ]]; then
136
+ json_ok "$(jq -nc \
137
+ --arg pattern "$es_topic" \
138
+ '{events:[],count:0,topic_pattern:$pattern}')"
139
+ return 0
140
+ fi
141
+
142
+ # Determine if pattern is prefix match (ends with *) or exact match
143
+ local es_jq_filter
144
+ if [[ "$es_topic" == *"*" ]]; then
145
+ local es_prefix="${es_topic%\*}"
146
+ es_jq_filter="startswith(\"$es_prefix\")"
147
+ else
148
+ es_jq_filter=". == \"$es_topic\""
149
+ fi
150
+
151
+ # Build jq filter: topic match + not expired + since filter
152
+ local es_jq_expr
153
+ es_jq_expr=". | select(.topic | $es_jq_filter) | select(.expires_at > \"$now_ts\")"
154
+ if [[ -n "$es_since" ]]; then
155
+ es_jq_expr="$es_jq_expr | select(.timestamp >= \"$es_since\")"
156
+ fi
157
+
158
+ local es_events
159
+ es_events=$(jq -sc \
160
+ --argjson limit "$es_limit" \
161
+ "[.[] | $es_jq_expr] | .[:(\$limit)]" \
162
+ "$bus_file" 2>/dev/null || echo "[]")
163
+
164
+ local es_count
165
+ es_count=$(echo "$es_events" | jq 'length')
166
+
167
+ json_ok "$(jq -nc \
168
+ --argjson events "$es_events" \
169
+ --argjson count "$es_count" \
170
+ --arg topic_pattern "$es_topic" \
171
+ '{events:$events,count:$count,topic_pattern:$topic_pattern}')"
172
+ }
173
+
174
+ # ============================================================================
175
+ # _event_cleanup
176
+ # Remove expired events from the JSONL bus.
177
+ # Usage: event-cleanup [--dry-run]
178
+ # ============================================================================
179
+ _event_cleanup() {
180
+ local ec_dry_run="false"
181
+
182
+ while [[ $# -gt 0 ]]; do
183
+ case "$1" in
184
+ --dry-run)
185
+ ec_dry_run="true"
186
+ shift
187
+ ;;
188
+ *)
189
+ shift
190
+ ;;
191
+ esac
192
+ done
193
+
194
+ local bus_file="$COLONY_DATA_DIR/event-bus.jsonl"
195
+ local now_ts
196
+ now_ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
197
+
198
+ # If bus file does not exist, nothing to clean
199
+ if [[ ! -f "$bus_file" ]]; then
200
+ json_ok "$(jq -nc \
201
+ --argjson dry_run "$ec_dry_run" \
202
+ '{removed:0,remaining:0,dry_run:$dry_run}')"
203
+ return 0
204
+ fi
205
+
206
+ local ec_total
207
+ ec_total=$(wc -l < "$bus_file" | tr -d ' ')
208
+
209
+ local ec_kept
210
+ ec_kept=$(jq -c "select(.expires_at > \"$now_ts\")" "$bus_file" 2>/dev/null || true)
211
+
212
+ local ec_kept_count=0
213
+ [[ -n "$ec_kept" ]] && ec_kept_count=$(echo "$ec_kept" | wc -l | tr -d ' ')
214
+
215
+ local ec_removed=$(( ec_total - ec_kept_count ))
216
+
217
+ if [[ "$ec_dry_run" == "false" ]]; then
218
+ # Acquire lock for safe atomic rewrite
219
+ acquire_lock "event-bus" 5 2>/dev/null \
220
+ || json_err "$E_LOCK_FAILED" "event-cleanup: failed to acquire lock"
221
+ trap 'release_lock "event-bus" 2>/dev/null || true' EXIT
222
+
223
+ if [[ -n "$ec_kept" ]]; then
224
+ atomic_write "$bus_file" "$ec_kept"
225
+ else
226
+ # All events expired — write empty file (touch creates it, or truncate)
227
+ : > "$bus_file"
228
+ fi
229
+
230
+ release_lock "event-bus" 2>/dev/null || true
231
+ fi
232
+
233
+ json_ok "$(jq -nc \
234
+ --argjson removed "$ec_removed" \
235
+ --argjson remaining "$ec_kept_count" \
236
+ --argjson dry_run "$ec_dry_run" \
237
+ '{removed:$removed,remaining:$remaining,dry_run:$dry_run}')"
238
+ }
239
+
240
+ # ============================================================================
241
+ # _event_replay
242
+ # Replay events for a topic from a given timestamp.
243
+ # Usage: event-replay --topic <topic> --since <ISO-8601> [--limit <N>]
244
+ # ============================================================================
245
+ _event_replay() {
246
+ local er_topic=""
247
+ local er_since=""
248
+ local er_limit="$_EVENT_BUS_DEFAULT_LIMIT"
249
+
250
+ while [[ $# -gt 0 ]]; do
251
+ case "$1" in
252
+ --topic)
253
+ er_topic="${2:-}"
254
+ shift 2
255
+ ;;
256
+ --since)
257
+ er_since="${2:-}"
258
+ shift 2
259
+ ;;
260
+ --limit)
261
+ er_limit="${2:-$_EVENT_BUS_DEFAULT_LIMIT}"
262
+ shift 2
263
+ ;;
264
+ *)
265
+ shift
266
+ ;;
267
+ esac
268
+ done
269
+
270
+ [[ -z "$er_topic" ]] && json_err "$E_VALIDATION_FAILED" "event-replay requires --topic"
271
+ [[ -z "$er_since" ]] && json_err "$E_VALIDATION_FAILED" "event-replay requires --since"
272
+
273
+ local bus_file="$COLONY_DATA_DIR/event-bus.jsonl"
274
+ local now_ts
275
+ now_ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
276
+
277
+ if [[ ! -f "$bus_file" ]]; then
278
+ json_ok "$(jq -nc \
279
+ --arg replayed_from "$er_since" \
280
+ '{events:[],count:0,replayed_from:$replayed_from}')"
281
+ return 0
282
+ fi
283
+
284
+ local er_events
285
+ er_events=$(jq -sc \
286
+ --arg topic "$er_topic" \
287
+ --arg since "$er_since" \
288
+ --arg now "$now_ts" \
289
+ --argjson limit "$er_limit" \
290
+ '[.[] | select(.topic == $topic) | select(.expires_at > $now) | select(.timestamp >= $since)] | sort_by(.timestamp) | .[:$limit]' \
291
+ "$bus_file" 2>/dev/null || echo "[]")
292
+
293
+ local er_count
294
+ er_count=$(echo "$er_events" | jq 'length')
295
+
296
+ json_ok "$(jq -nc \
297
+ --argjson events "$er_events" \
298
+ --argjson count "$er_count" \
299
+ --arg replayed_from "$er_since" \
300
+ '{events:$events,count:$count,replayed_from:$replayed_from}')"
301
+ }