aether-colony 5.3.1 → 5.3.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.
- package/.aether/aether-utils.sh +181 -5
- package/.aether/commands/build.yaml +35 -0
- package/.aether/commands/entomb.yaml +1 -1
- package/.aether/commands/init.yaml +29 -12
- package/.aether/commands/oracle.yaml +70 -0
- package/.aether/commands/patrol.yaml +2 -2
- package/.aether/commands/run.yaml +3 -3
- package/.aether/commands/swarm.yaml +1 -1
- package/.aether/docs/command-playbooks/build-complete.md +41 -8
- package/.aether/docs/command-playbooks/build-full.md +7 -7
- package/.aether/docs/command-playbooks/build-prep.md +1 -1
- package/.aether/docs/command-playbooks/continue-advance.md +33 -0
- package/.aether/docs/command-playbooks/continue-finalize.md +15 -1
- package/.aether/docs/command-playbooks/continue-full.md +15 -1
- package/.aether/docs/source-of-truth-map.md +10 -10
- package/.aether/docs/structural-learning-stack.md +283 -0
- package/.aether/utils/consolidation-seal.sh +196 -0
- package/.aether/utils/consolidation.sh +127 -0
- package/.aether/utils/curation-ants/archivist.sh +97 -0
- package/.aether/utils/curation-ants/critic.sh +214 -0
- package/.aether/utils/curation-ants/herald.sh +102 -0
- package/.aether/utils/curation-ants/janitor.sh +121 -0
- package/.aether/utils/curation-ants/librarian.sh +99 -0
- package/.aether/utils/curation-ants/nurse.sh +153 -0
- package/.aether/utils/curation-ants/orchestrator.sh +181 -0
- package/.aether/utils/curation-ants/scribe.sh +164 -0
- package/.aether/utils/curation-ants/sentinel.sh +119 -0
- package/.aether/utils/event-bus.sh +301 -0
- package/.aether/utils/graph.sh +559 -0
- package/.aether/utils/instinct-store.sh +401 -0
- package/.aether/utils/learning.sh +79 -7
- package/.aether/utils/session.sh +13 -0
- package/.aether/utils/state-api.sh +1 -1
- package/.aether/utils/trust-scoring.sh +347 -0
- package/.aether/utils/worktree.sh +97 -0
- package/.claude/commands/ant/entomb.md +1 -1
- package/.claude/commands/ant/init.md +29 -12
- package/.claude/commands/ant/oracle.md +35 -0
- package/.claude/commands/ant/patrol.md +2 -2
- package/.claude/commands/ant/run.md +3 -3
- package/.claude/commands/ant/swarm.md +1 -1
- package/.opencode/commands/ant/build.md +35 -0
- package/.opencode/commands/ant/init.md +29 -12
- package/.opencode/commands/ant/oracle.md +35 -0
- package/.opencode/commands/ant/patrol.md +2 -2
- package/.opencode/commands/ant/run.md +3 -3
- package/CHANGELOG.md +83 -0
- package/README.md +34 -37
- package/bin/lib/update-transaction.js +8 -3
- package/bin/npx-entry.js +0 -0
- package/package.json +1 -1
- package/.aether/agents/aether-ambassador.md +0 -140
- package/.aether/agents/aether-archaeologist.md +0 -108
- package/.aether/agents/aether-architect.md +0 -133
- package/.aether/agents/aether-auditor.md +0 -144
- package/.aether/agents/aether-builder.md +0 -184
- package/.aether/agents/aether-chaos.md +0 -115
- package/.aether/agents/aether-chronicler.md +0 -122
- package/.aether/agents/aether-gatekeeper.md +0 -116
- package/.aether/agents/aether-includer.md +0 -117
- package/.aether/agents/aether-keeper.md +0 -177
- package/.aether/agents/aether-measurer.md +0 -128
- package/.aether/agents/aether-oracle.md +0 -137
- package/.aether/agents/aether-probe.md +0 -133
- package/.aether/agents/aether-queen.md +0 -286
- package/.aether/agents/aether-route-setter.md +0 -130
- package/.aether/agents/aether-sage.md +0 -106
- package/.aether/agents/aether-scout.md +0 -101
- package/.aether/agents/aether-surveyor-disciplines.md +0 -391
- package/.aether/agents/aether-surveyor-nest.md +0 -329
- package/.aether/agents/aether-surveyor-pathogens.md +0 -264
- package/.aether/agents/aether-surveyor-provisions.md +0 -334
- package/.aether/agents/aether-tracker.md +0 -137
- package/.aether/agents/aether-watcher.md +0 -174
- package/.aether/agents/aether-weaver.md +0 -130
- package/.aether/commands/claude/archaeology.md +0 -334
- package/.aether/commands/claude/build.md +0 -65
- package/.aether/commands/claude/chaos.md +0 -336
- package/.aether/commands/claude/colonize.md +0 -259
- package/.aether/commands/claude/continue.md +0 -60
- package/.aether/commands/claude/council.md +0 -507
- package/.aether/commands/claude/data-clean.md +0 -81
- package/.aether/commands/claude/dream.md +0 -268
- package/.aether/commands/claude/entomb.md +0 -498
- package/.aether/commands/claude/export-signals.md +0 -57
- package/.aether/commands/claude/feedback.md +0 -96
- package/.aether/commands/claude/flag.md +0 -151
- package/.aether/commands/claude/flags.md +0 -169
- package/.aether/commands/claude/focus.md +0 -76
- package/.aether/commands/claude/help.md +0 -154
- package/.aether/commands/claude/history.md +0 -140
- package/.aether/commands/claude/import-signals.md +0 -71
- package/.aether/commands/claude/init.md +0 -505
- package/.aether/commands/claude/insert-phase.md +0 -105
- package/.aether/commands/claude/interpret.md +0 -278
- package/.aether/commands/claude/lay-eggs.md +0 -210
- package/.aether/commands/claude/maturity.md +0 -113
- package/.aether/commands/claude/memory-details.md +0 -77
- package/.aether/commands/claude/migrate-state.md +0 -171
- package/.aether/commands/claude/oracle.md +0 -642
- package/.aether/commands/claude/organize.md +0 -232
- package/.aether/commands/claude/patrol.md +0 -620
- package/.aether/commands/claude/pause-colony.md +0 -233
- package/.aether/commands/claude/phase.md +0 -115
- package/.aether/commands/claude/pheromones.md +0 -156
- package/.aether/commands/claude/plan.md +0 -693
- package/.aether/commands/claude/preferences.md +0 -65
- package/.aether/commands/claude/quick.md +0 -100
- package/.aether/commands/claude/redirect.md +0 -76
- package/.aether/commands/claude/resume-colony.md +0 -197
- package/.aether/commands/claude/resume.md +0 -388
- package/.aether/commands/claude/run.md +0 -231
- package/.aether/commands/claude/seal.md +0 -774
- package/.aether/commands/claude/skill-create.md +0 -286
- package/.aether/commands/claude/status.md +0 -410
- package/.aether/commands/claude/swarm.md +0 -349
- package/.aether/commands/claude/tunnels.md +0 -426
- package/.aether/commands/claude/update.md +0 -132
- package/.aether/commands/claude/verify-castes.md +0 -143
- package/.aether/commands/claude/watch.md +0 -239
- package/.aether/commands/opencode/archaeology.md +0 -331
- package/.aether/commands/opencode/build.md +0 -1168
- package/.aether/commands/opencode/chaos.md +0 -329
- package/.aether/commands/opencode/colonize.md +0 -195
- package/.aether/commands/opencode/continue.md +0 -1436
- package/.aether/commands/opencode/council.md +0 -437
- package/.aether/commands/opencode/data-clean.md +0 -77
- package/.aether/commands/opencode/dream.md +0 -260
- package/.aether/commands/opencode/entomb.md +0 -377
- package/.aether/commands/opencode/export-signals.md +0 -54
- package/.aether/commands/opencode/feedback.md +0 -99
- package/.aether/commands/opencode/flag.md +0 -149
- package/.aether/commands/opencode/flags.md +0 -167
- package/.aether/commands/opencode/focus.md +0 -73
- package/.aether/commands/opencode/help.md +0 -157
- package/.aether/commands/opencode/history.md +0 -136
- package/.aether/commands/opencode/import-signals.md +0 -68
- package/.aether/commands/opencode/init.md +0 -518
- package/.aether/commands/opencode/insert-phase.md +0 -111
- package/.aether/commands/opencode/interpret.md +0 -272
- package/.aether/commands/opencode/lay-eggs.md +0 -213
- package/.aether/commands/opencode/maturity.md +0 -108
- package/.aether/commands/opencode/memory-details.md +0 -83
- package/.aether/commands/opencode/migrate-state.md +0 -165
- package/.aether/commands/opencode/oracle.md +0 -593
- package/.aether/commands/opencode/organize.md +0 -226
- package/.aether/commands/opencode/patrol.md +0 -626
- package/.aether/commands/opencode/pause-colony.md +0 -203
- package/.aether/commands/opencode/phase.md +0 -113
- package/.aether/commands/opencode/pheromones.md +0 -162
- package/.aether/commands/opencode/plan.md +0 -684
- package/.aether/commands/opencode/preferences.md +0 -71
- package/.aether/commands/opencode/quick.md +0 -91
- package/.aether/commands/opencode/redirect.md +0 -84
- package/.aether/commands/opencode/resume-colony.md +0 -190
- package/.aether/commands/opencode/resume.md +0 -394
- package/.aether/commands/opencode/run.md +0 -237
- package/.aether/commands/opencode/seal.md +0 -452
- package/.aether/commands/opencode/skill-create.md +0 -63
- package/.aether/commands/opencode/status.md +0 -307
- package/.aether/commands/opencode/swarm.md +0 -15
- package/.aether/commands/opencode/tunnels.md +0 -400
- package/.aether/commands/opencode/update.md +0 -127
- package/.aether/commands/opencode/verify-castes.md +0 -139
- package/.aether/commands/opencode/watch.md +0 -227
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Instinct Store utility functions — standalone instinct storage with trust scoring
|
|
3
|
+
# Provides: _instinct_store, _instinct_read_trusted, _instinct_decay_all, _instinct_archive
|
|
4
|
+
#
|
|
5
|
+
# These functions are sourced by aether-utils.sh at startup.
|
|
6
|
+
# All shared infrastructure (json_ok, json_err, atomic_write, acquire_lock,
|
|
7
|
+
# release_lock, COLONY_DATA_DIR, SCRIPT_DIR, error constants) is available.
|
|
8
|
+
# Depends on trust-scoring.sh for trust score computation.
|
|
9
|
+
#
|
|
10
|
+
# State file: $COLONY_DATA_DIR/instincts.json
|
|
11
|
+
# Schema version: 1.0
|
|
12
|
+
# Cap: 50 instincts (lowest trust archived on overflow)
|
|
13
|
+
|
|
14
|
+
# ============================================================================
|
|
15
|
+
# _instinct_store
|
|
16
|
+
# Store a new instinct or reinforce an existing one (dedup by trigger prefix).
|
|
17
|
+
#
|
|
18
|
+
# Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f>
|
|
19
|
+
# --source <s> --evidence <e> [--source-type <type>]
|
|
20
|
+
#
|
|
21
|
+
# Deduplication: first 50 chars of trigger matched against existing entries.
|
|
22
|
+
# On match: boost confidence to max of existing/new, recompute trust score.
|
|
23
|
+
# Cap: 50 instincts. When exceeded, archive the entry with lowest trust_score.
|
|
24
|
+
# ============================================================================
|
|
25
|
+
_instinct_store() {
|
|
26
|
+
local trigger=""
|
|
27
|
+
local action=""
|
|
28
|
+
local domain=""
|
|
29
|
+
local confidence=""
|
|
30
|
+
local source=""
|
|
31
|
+
local evidence=""
|
|
32
|
+
local source_type="observation"
|
|
33
|
+
|
|
34
|
+
while [[ $# -gt 0 ]]; do
|
|
35
|
+
case "$1" in
|
|
36
|
+
--trigger) trigger="${2:-}"; shift 2 ;;
|
|
37
|
+
--action) action="${2:-}"; shift 2 ;;
|
|
38
|
+
--domain) domain="${2:-}"; shift 2 ;;
|
|
39
|
+
--confidence) confidence="${2:-}"; shift 2 ;;
|
|
40
|
+
--source) source="${2:-}"; shift 2 ;;
|
|
41
|
+
--evidence) evidence="${2:-}"; shift 2 ;;
|
|
42
|
+
--source-type) source_type="${2:-}"; shift 2 ;;
|
|
43
|
+
*) shift ;;
|
|
44
|
+
esac
|
|
45
|
+
done
|
|
46
|
+
|
|
47
|
+
[[ -z "$trigger" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
|
|
48
|
+
[[ -z "$action" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
|
|
49
|
+
[[ -z "$domain" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
|
|
50
|
+
[[ -z "$confidence" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
|
|
51
|
+
[[ -z "$source" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
|
|
52
|
+
[[ -z "$evidence" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
|
|
53
|
+
|
|
54
|
+
# Validate confidence is a number
|
|
55
|
+
if ! [[ "$confidence" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
|
|
56
|
+
json_err "$E_VALIDATION_FAILED" "--confidence must be a number between 0 and 1, got: $confidence"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
mkdir -p "$COLONY_DATA_DIR"
|
|
60
|
+
local instincts_file="$COLONY_DATA_DIR/instincts.json"
|
|
61
|
+
|
|
62
|
+
# Initialize file if missing
|
|
63
|
+
if [[ ! -f "$instincts_file" ]]; then
|
|
64
|
+
atomic_write "$instincts_file" '{"version":"1.0","instincts":[]}'
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
local ts
|
|
68
|
+
ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
69
|
+
|
|
70
|
+
# Compute trust score via trust-calculate
|
|
71
|
+
local trust_result trust_score trust_tier
|
|
72
|
+
trust_result=$(_trust_calculate --source "$source_type" --evidence "$evidence" --days-since 0 2>/dev/null) || true
|
|
73
|
+
if echo "$trust_result" | jq -e '.ok == true' > /dev/null 2>&1; then
|
|
74
|
+
trust_score=$(echo "$trust_result" | jq -r '.result.score')
|
|
75
|
+
trust_tier=$(echo "$trust_result" | jq -r '.result.tier')
|
|
76
|
+
else
|
|
77
|
+
# Fallback: use confidence as trust score, derive tier
|
|
78
|
+
trust_score="$confidence"
|
|
79
|
+
trust_tier=$(_trust_score_to_tier "$confidence" 2>/dev/null || echo "provisional")
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# Acquire lock for atomic update
|
|
83
|
+
acquire_lock "$instincts_file" || json_err "$E_LOCK_FAILED" "Failed to acquire lock on instincts.json"
|
|
84
|
+
trap 'release_lock 2>/dev/null || true' EXIT # SUPPRESS:OK -- cleanup: lock may not be held
|
|
85
|
+
|
|
86
|
+
# Dedup: check if a matching trigger prefix (first 50 chars) already exists
|
|
87
|
+
local trigger_prefix
|
|
88
|
+
trigger_prefix=$(echo "$trigger" | cut -c1-50)
|
|
89
|
+
|
|
90
|
+
local existing_id
|
|
91
|
+
existing_id=$(jq -r --arg prefix "$trigger_prefix" '
|
|
92
|
+
.instincts[]
|
|
93
|
+
| select(.archived == false)
|
|
94
|
+
| select((.trigger | .[0:50]) == $prefix)
|
|
95
|
+
| .id
|
|
96
|
+
| select(. != null)
|
|
97
|
+
' "$instincts_file" 2>/dev/null | head -1)
|
|
98
|
+
|
|
99
|
+
local updated
|
|
100
|
+
if [[ -n "$existing_id" ]]; then
|
|
101
|
+
# Reinforce: boost confidence to max, recompute trust score
|
|
102
|
+
local new_confidence
|
|
103
|
+
new_confidence=$(jq -r --arg id "$existing_id" \
|
|
104
|
+
'.instincts[] | select(.id == $id) | .confidence' "$instincts_file")
|
|
105
|
+
local boosted_confidence
|
|
106
|
+
boosted_confidence=$(awk "BEGIN{
|
|
107
|
+
a=$new_confidence; b=$confidence;
|
|
108
|
+
printf \"%.4f\", (a > b ? a : b)
|
|
109
|
+
}")
|
|
110
|
+
|
|
111
|
+
# Recompute trust with boosted confidence
|
|
112
|
+
local boost_trust_result
|
|
113
|
+
boost_trust_result=$(_trust_calculate --source "$source_type" --evidence "$evidence" --days-since 0 2>/dev/null) || true
|
|
114
|
+
if echo "$boost_trust_result" | jq -e '.ok == true' > /dev/null 2>&1; then
|
|
115
|
+
trust_score=$(echo "$boost_trust_result" | jq -r '.result.score')
|
|
116
|
+
trust_tier=$(echo "$boost_trust_result" | jq -r '.result.tier')
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
updated=$(jq \
|
|
120
|
+
--arg id "$existing_id" \
|
|
121
|
+
--argjson conf "$boosted_confidence" \
|
|
122
|
+
--argjson ts_score "$trust_score" \
|
|
123
|
+
--arg ts_tier "$trust_tier" \
|
|
124
|
+
--arg ts "$ts" '
|
|
125
|
+
.instincts = [.instincts[] | if .id == $id then
|
|
126
|
+
.confidence = $conf |
|
|
127
|
+
.trust_score = $ts_score |
|
|
128
|
+
.trust_tier = $ts_tier |
|
|
129
|
+
.provenance.last_applied = $ts |
|
|
130
|
+
.provenance.application_count += 1
|
|
131
|
+
else . end]
|
|
132
|
+
' "$instincts_file") || json_err "$E_JSON_INVALID" "Failed to reinforce instinct"
|
|
133
|
+
atomic_write "$instincts_file" "$updated"
|
|
134
|
+
trap - EXIT
|
|
135
|
+
release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
|
|
136
|
+
json_ok "$(jq -n --arg id "$existing_id" --arg action "reinforced" \
|
|
137
|
+
'{id: $id, action: $action}')"
|
|
138
|
+
return
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# New instinct: generate id and build entry
|
|
142
|
+
local id
|
|
143
|
+
id="inst_$(date -u +%s)_$(head -c 3 /dev/urandom | od -An -tx1 | tr -d ' \n' | cut -c1-6)"
|
|
144
|
+
|
|
145
|
+
local new_entry
|
|
146
|
+
new_entry=$(jq -n \
|
|
147
|
+
--arg id "$id" \
|
|
148
|
+
--arg trigger "$trigger" \
|
|
149
|
+
--arg action "$action" \
|
|
150
|
+
--arg domain "$domain" \
|
|
151
|
+
--argjson trust_score "$trust_score" \
|
|
152
|
+
--arg trust_tier "$trust_tier" \
|
|
153
|
+
--argjson confidence "$confidence" \
|
|
154
|
+
--arg source "$source" \
|
|
155
|
+
--arg source_type "$source_type" \
|
|
156
|
+
--arg evidence "$evidence" \
|
|
157
|
+
--arg ts "$ts" \
|
|
158
|
+
'{
|
|
159
|
+
id: $id,
|
|
160
|
+
trigger: $trigger,
|
|
161
|
+
action: $action,
|
|
162
|
+
domain: $domain,
|
|
163
|
+
trust_score: $trust_score,
|
|
164
|
+
trust_tier: $trust_tier,
|
|
165
|
+
confidence: $confidence,
|
|
166
|
+
provenance: {
|
|
167
|
+
source: $source,
|
|
168
|
+
source_type: $source_type,
|
|
169
|
+
evidence: $evidence,
|
|
170
|
+
created_at: $ts,
|
|
171
|
+
last_applied: null,
|
|
172
|
+
application_count: 0
|
|
173
|
+
},
|
|
174
|
+
application_history: [],
|
|
175
|
+
related_instincts: [],
|
|
176
|
+
archived: false
|
|
177
|
+
}')
|
|
178
|
+
|
|
179
|
+
# Append entry
|
|
180
|
+
updated=$(jq --argjson entry "$new_entry" '.instincts += [$entry]' "$instincts_file") \
|
|
181
|
+
|| json_err "$E_JSON_INVALID" "Failed to append instinct"
|
|
182
|
+
|
|
183
|
+
# Enforce 50-entry cap: archive lowest-trust non-archived instinct if over limit
|
|
184
|
+
local active_count
|
|
185
|
+
active_count=$(echo "$updated" | jq '[.instincts[] | select(.archived == false)] | length')
|
|
186
|
+
if [[ "$active_count" -gt 50 ]]; then
|
|
187
|
+
updated=$(echo "$updated" | jq '
|
|
188
|
+
# Find the id of the lowest-trust active instinct
|
|
189
|
+
(
|
|
190
|
+
[.instincts[] | select(.archived == false)]
|
|
191
|
+
| sort_by(.trust_score)
|
|
192
|
+
| .[0].id
|
|
193
|
+
) as $lowest_id
|
|
194
|
+
|
|
|
195
|
+
.instincts = [.instincts[] | if .id == $lowest_id then .archived = true else . end]
|
|
196
|
+
') || json_err "$E_JSON_INVALID" "Failed to enforce instinct cap"
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
atomic_write "$instincts_file" "$updated"
|
|
200
|
+
trap - EXIT
|
|
201
|
+
release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
|
|
202
|
+
json_ok "$(jq -n --arg id "$id" --arg action "stored" '{id: $id, action: $action}')"
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
# ============================================================================
|
|
206
|
+
# _instinct_read_trusted
|
|
207
|
+
# Read trusted instincts, sorted by trust_score descending.
|
|
208
|
+
#
|
|
209
|
+
# Usage: instinct-read-trusted [--min-score <f>] [--domain <d>] [--limit <N>]
|
|
210
|
+
#
|
|
211
|
+
# Defaults: min-score=0.5, limit=20. Excludes archived entries.
|
|
212
|
+
# ============================================================================
|
|
213
|
+
_instinct_read_trusted() {
|
|
214
|
+
local min_score="0.5"
|
|
215
|
+
local domain=""
|
|
216
|
+
local limit="20"
|
|
217
|
+
|
|
218
|
+
while [[ $# -gt 0 ]]; do
|
|
219
|
+
case "$1" in
|
|
220
|
+
--min-score) min_score="${2:-0.5}"; shift 2 ;;
|
|
221
|
+
--domain) domain="${2:-}"; shift 2 ;;
|
|
222
|
+
--limit) limit="${2:-20}"; shift 2 ;;
|
|
223
|
+
*) shift ;;
|
|
224
|
+
esac
|
|
225
|
+
done
|
|
226
|
+
|
|
227
|
+
local instincts_file="$COLONY_DATA_DIR/instincts.json"
|
|
228
|
+
if [[ ! -f "$instincts_file" ]]; then
|
|
229
|
+
json_ok '{"instincts":[],"count":0}'
|
|
230
|
+
return
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
local result
|
|
234
|
+
result=$(jq -n \
|
|
235
|
+
--argjson min_score "$min_score" \
|
|
236
|
+
--arg domain "$domain" \
|
|
237
|
+
--argjson limit "$limit" \
|
|
238
|
+
--slurpfile data "$instincts_file" '
|
|
239
|
+
$data[0].instincts
|
|
240
|
+
| [.[] | select(.archived == false)]
|
|
241
|
+
| [.[] | select(.trust_score >= $min_score)]
|
|
242
|
+
| (if $domain != "" then [.[] | select(.domain == $domain)] else . end)
|
|
243
|
+
| sort_by(-.trust_score)
|
|
244
|
+
| .[0:$limit]
|
|
245
|
+
| {instincts: ., count: length}
|
|
246
|
+
') || json_err "$E_JSON_INVALID" "Failed to read trusted instincts"
|
|
247
|
+
|
|
248
|
+
json_ok "$result"
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
# ============================================================================
|
|
252
|
+
# _instinct_decay_all
|
|
253
|
+
# Apply trust-based time decay to all non-archived instincts.
|
|
254
|
+
#
|
|
255
|
+
# Usage: instinct-decay-all [--days <N>] [--dry-run]
|
|
256
|
+
#
|
|
257
|
+
# Archives instincts whose decayed score falls below 0.25.
|
|
258
|
+
# Updates trust_tier for all processed entries.
|
|
259
|
+
# ============================================================================
|
|
260
|
+
_instinct_decay_all() {
|
|
261
|
+
local days="30"
|
|
262
|
+
local dry_run="false"
|
|
263
|
+
|
|
264
|
+
while [[ $# -gt 0 ]]; do
|
|
265
|
+
case "$1" in
|
|
266
|
+
--days) days="${2:-30}"; shift 2 ;;
|
|
267
|
+
--dry-run) dry_run="true"; shift ;;
|
|
268
|
+
*) shift ;;
|
|
269
|
+
esac
|
|
270
|
+
done
|
|
271
|
+
|
|
272
|
+
local instincts_file="$COLONY_DATA_DIR/instincts.json"
|
|
273
|
+
if [[ ! -f "$instincts_file" ]]; then
|
|
274
|
+
json_ok '{"processed":0,"archived":0,"dry_run":false}'
|
|
275
|
+
return
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
# Read all active instincts and apply decay
|
|
279
|
+
local current_data
|
|
280
|
+
current_data=$(cat "$instincts_file")
|
|
281
|
+
|
|
282
|
+
local active_count
|
|
283
|
+
active_count=$(echo "$current_data" | jq '[.instincts[] | select(.archived == false)] | length')
|
|
284
|
+
|
|
285
|
+
if [[ "$active_count" -eq 0 ]]; then
|
|
286
|
+
json_ok "$(jq -n --argjson days "$days" '{"processed":0,"archived":0,"dry_run":false}')"
|
|
287
|
+
return
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
# Build updated instincts array: apply decay to each active entry
|
|
291
|
+
local updated_instincts="[]"
|
|
292
|
+
local archived_count=0
|
|
293
|
+
|
|
294
|
+
while IFS= read -r instinct_json; do
|
|
295
|
+
local current_score
|
|
296
|
+
current_score=$(echo "$instinct_json" | jq -r '.trust_score')
|
|
297
|
+
|
|
298
|
+
local decay_result decayed_score new_tier
|
|
299
|
+
decay_result=$(_trust_decay --score "$current_score" --days "$days" 2>/dev/null) || true
|
|
300
|
+
|
|
301
|
+
if echo "$decay_result" | jq -e '.ok == true' > /dev/null 2>&1; then
|
|
302
|
+
decayed_score=$(echo "$decay_result" | jq -r '.result.decayed')
|
|
303
|
+
else
|
|
304
|
+
decayed_score="$current_score"
|
|
305
|
+
fi
|
|
306
|
+
|
|
307
|
+
new_tier=$(_trust_score_to_tier "$decayed_score" 2>/dev/null || echo "dormant")
|
|
308
|
+
|
|
309
|
+
local should_archive="false"
|
|
310
|
+
local below_threshold
|
|
311
|
+
below_threshold=$(awk "BEGIN{print ($decayed_score < 0.25)}" 2>/dev/null || echo "0")
|
|
312
|
+
if [[ "$below_threshold" == "1" ]]; then
|
|
313
|
+
should_archive="true"
|
|
314
|
+
archived_count=$((archived_count + 1))
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
local updated_entry
|
|
318
|
+
updated_entry=$(echo "$instinct_json" | jq \
|
|
319
|
+
--argjson score "$decayed_score" \
|
|
320
|
+
--arg tier "$new_tier" \
|
|
321
|
+
--argjson archive "$should_archive" \
|
|
322
|
+
'.trust_score = $score | .trust_tier = $tier | .archived = $archive')
|
|
323
|
+
|
|
324
|
+
updated_instincts=$(echo "$updated_instincts" | jq \
|
|
325
|
+
--argjson entry "$updated_entry" \
|
|
326
|
+
'. += [$entry]')
|
|
327
|
+
done < <(echo "$current_data" | jq -c '.instincts[] | select(.archived == false)')
|
|
328
|
+
|
|
329
|
+
# Merge: keep archived entries as-is, replace active entries with updated versions
|
|
330
|
+
local merged
|
|
331
|
+
merged=$(jq -n \
|
|
332
|
+
--argjson updated "$updated_instincts" \
|
|
333
|
+
--argjson original "$(echo "$current_data" | jq '.instincts')" \
|
|
334
|
+
'
|
|
335
|
+
# Build a lookup of updated entries by id
|
|
336
|
+
($updated | map({(.id): .}) | add // {}) as $lookup
|
|
337
|
+
|
|
|
338
|
+
[
|
|
339
|
+
$original[] | if .archived == true then
|
|
340
|
+
.
|
|
341
|
+
elif ($lookup[.id] != null) then
|
|
342
|
+
$lookup[.id]
|
|
343
|
+
else
|
|
344
|
+
.
|
|
345
|
+
end
|
|
346
|
+
]
|
|
347
|
+
')
|
|
348
|
+
|
|
349
|
+
local final_data
|
|
350
|
+
final_data=$(echo "$current_data" | jq --argjson instincts "$merged" '.instincts = $instincts')
|
|
351
|
+
|
|
352
|
+
if [[ "$dry_run" != "true" ]]; then
|
|
353
|
+
acquire_lock "$instincts_file" || json_err "$E_LOCK_FAILED" "Failed to acquire lock on instincts.json"
|
|
354
|
+
trap 'release_lock 2>/dev/null || true' EXIT # SUPPRESS:OK -- cleanup: lock may not be held
|
|
355
|
+
atomic_write "$instincts_file" "$final_data"
|
|
356
|
+
trap - EXIT
|
|
357
|
+
release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
|
|
358
|
+
fi
|
|
359
|
+
|
|
360
|
+
json_ok "$(jq -n \
|
|
361
|
+
--argjson processed "$active_count" \
|
|
362
|
+
--argjson archived "$archived_count" \
|
|
363
|
+
--argjson days "$days" \
|
|
364
|
+
--argjson dry_run "$([ "$dry_run" == "true" ] && echo true || echo false)" \
|
|
365
|
+
'{processed: $processed, archived: $archived, days: $days, dry_run: $dry_run}')"
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
# ============================================================================
|
|
369
|
+
# _instinct_archive
|
|
370
|
+
# Soft-delete an instinct by id (sets archived: true).
|
|
371
|
+
#
|
|
372
|
+
# Usage: instinct-archive --id <id>
|
|
373
|
+
# ============================================================================
|
|
374
|
+
_instinct_archive() {
|
|
375
|
+
local id=""
|
|
376
|
+
|
|
377
|
+
while [[ $# -gt 0 ]]; do
|
|
378
|
+
case "$1" in
|
|
379
|
+
--id) id="${2:-}"; shift 2 ;;
|
|
380
|
+
*) shift ;;
|
|
381
|
+
esac
|
|
382
|
+
done
|
|
383
|
+
|
|
384
|
+
[[ -z "$id" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-archive --id <id>"
|
|
385
|
+
|
|
386
|
+
local instincts_file="$COLONY_DATA_DIR/instincts.json"
|
|
387
|
+
[[ ! -f "$instincts_file" ]] && json_err "$E_FILE_NOT_FOUND" "No instincts.json found"
|
|
388
|
+
|
|
389
|
+
acquire_lock "$instincts_file" || json_err "$E_LOCK_FAILED" "Failed to acquire lock on instincts.json"
|
|
390
|
+
trap 'release_lock 2>/dev/null || true' EXIT # SUPPRESS:OK -- cleanup: lock may not be held
|
|
391
|
+
|
|
392
|
+
local updated
|
|
393
|
+
updated=$(jq --arg id "$id" '
|
|
394
|
+
.instincts = [.instincts[] | if .id == $id then .archived = true else . end]
|
|
395
|
+
' "$instincts_file") || json_err "$E_JSON_INVALID" "Failed to archive instinct"
|
|
396
|
+
|
|
397
|
+
atomic_write "$instincts_file" "$updated"
|
|
398
|
+
trap - EXIT
|
|
399
|
+
release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
|
|
400
|
+
json_ok "$(jq -n --arg id "$id" '{archived: $id}')"
|
|
401
|
+
}
|
|
@@ -91,16 +91,18 @@ _learning_inject() {
|
|
|
91
91
|
# ============================================================================
|
|
92
92
|
# _learning_observe
|
|
93
93
|
# Record observation of a learning across colonies
|
|
94
|
-
# Usage: learning-observe <content> <wisdom_type> [colony_name]
|
|
95
|
-
# Returns: JSON with observation_count, threshold status,
|
|
94
|
+
# Usage: learning-observe <content> <wisdom_type> [colony_name] [source_type] [evidence_type]
|
|
95
|
+
# Returns: JSON with observation_count, threshold status, colonies list, and trust_score
|
|
96
96
|
# ============================================================================
|
|
97
97
|
_learning_observe() {
|
|
98
98
|
# Record observation of a learning across colonies
|
|
99
|
-
# Usage: learning-observe <content> <wisdom_type> [colony_name]
|
|
100
|
-
# Returns: JSON with observation_count, threshold status,
|
|
99
|
+
# Usage: learning-observe <content> <wisdom_type> [colony_name] [source_type] [evidence_type]
|
|
100
|
+
# Returns: JSON with observation_count, threshold status, colonies list, and trust_score
|
|
101
101
|
content="${1:-}"
|
|
102
102
|
wisdom_type="${2:-}"
|
|
103
103
|
colony_name="${3:-unknown}"
|
|
104
|
+
local source_type="${4:-observation}"
|
|
105
|
+
local evidence_type="${5:-anecdotal}"
|
|
104
106
|
|
|
105
107
|
# Validate required arguments
|
|
106
108
|
[[ -z "$content" ]] && json_err "$E_VALIDATION_FAILED" "Usage: learning-observe <content> <wisdom_type> [colony_name]" '{"missing":"content"}'
|
|
@@ -178,11 +180,64 @@ _learning_observe() {
|
|
|
178
180
|
# Get current timestamp
|
|
179
181
|
ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
180
182
|
|
|
183
|
+
# Initialize trust_score (set for new observations; left as default for existing)
|
|
184
|
+
local trust_score="0.490000"
|
|
185
|
+
|
|
186
|
+
# --- Legacy migration: backfill missing fields on any entries that lack trust_score ---
|
|
187
|
+
local has_legacy
|
|
188
|
+
has_legacy=$(jq '[.observations[] | select(.trust_score == null)] | length' "$observations_file")
|
|
189
|
+
if [[ "$has_legacy" -gt 0 ]]; then
|
|
190
|
+
jq '.observations |= map(
|
|
191
|
+
if .trust_score == null then
|
|
192
|
+
.trust_score = 0.49 |
|
|
193
|
+
.source_type = "legacy" |
|
|
194
|
+
.evidence_type = "indirect" |
|
|
195
|
+
.compression_level = 0
|
|
196
|
+
else . end
|
|
197
|
+
)' "$observations_file" > "${observations_file}.migrate.$$" && \
|
|
198
|
+
mv "${observations_file}.migrate.$$" "$observations_file" 2>/dev/null || {
|
|
199
|
+
rm -f "${observations_file}.migrate.$$"
|
|
200
|
+
_aether_log_error "Could not backfill legacy observation fields"
|
|
201
|
+
}
|
|
202
|
+
fi
|
|
203
|
+
|
|
181
204
|
# Check if observation with same hash already exists
|
|
182
205
|
existing_index=$(jq -r --arg hash "$content_hash" '.observations | to_entries[] | select(.value.content_hash == $hash) | .key' "$observations_file" | head -1)
|
|
183
206
|
|
|
184
207
|
if [[ -n "$existing_index" ]]; then
|
|
185
208
|
# Existing observation: increment count, update last_seen, add colony if new
|
|
209
|
+
# Recalculate trust_score based on existing source/evidence types and days since first_seen
|
|
210
|
+
local existing_source existing_evidence existing_first_seen days_since
|
|
211
|
+
existing_source=$(jq -r --arg hash "$content_hash" '.observations[] | select(.content_hash == $hash) | .source_type // "observation"' "$observations_file")
|
|
212
|
+
existing_evidence=$(jq -r --arg hash "$content_hash" '.observations[] | select(.content_hash == $hash) | .evidence_type // "anecdotal"' "$observations_file")
|
|
213
|
+
|
|
214
|
+
# Map legacy/invalid types to valid trust-calculate types
|
|
215
|
+
case "$existing_source" in
|
|
216
|
+
user_feedback|error_resolution|success_pattern|observation|heuristic) ;;
|
|
217
|
+
*) existing_source="observation" ;;
|
|
218
|
+
esac
|
|
219
|
+
case "$existing_evidence" in
|
|
220
|
+
test_verified|multi_phase|single_phase|anecdotal) ;;
|
|
221
|
+
*) existing_evidence="anecdotal" ;;
|
|
222
|
+
esac
|
|
223
|
+
existing_first_seen=$(jq -r --arg hash "$content_hash" '.observations[] | select(.content_hash == $hash) | .first_seen' "$observations_file")
|
|
224
|
+
|
|
225
|
+
# Compute days since first_seen
|
|
226
|
+
if [[ -n "$existing_first_seen" ]]; then
|
|
227
|
+
local epoch_first epoch_now
|
|
228
|
+
epoch_first=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$existing_first_seen" "+%s" 2>/dev/null || echo "0")
|
|
229
|
+
epoch_now=$(date -u +%s)
|
|
230
|
+
days_since=$(( (epoch_now - epoch_first) / 86400 ))
|
|
231
|
+
[[ "$days_since" -lt 0 ]] && days_since=0
|
|
232
|
+
else
|
|
233
|
+
days_since=0
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
# Calculate trust score for the re-observation
|
|
237
|
+
local trust_result
|
|
238
|
+
trust_result=$(bash "$0" trust-calculate --source "$existing_source" --evidence "$existing_evidence" --days-since "$days_since" 2>/dev/null)
|
|
239
|
+
trust_score=$(echo "$trust_result" | jq -r '.result.score // "0.490000"' 2>/dev/null)
|
|
240
|
+
|
|
186
241
|
# Rotate backups before write (uses .bak.N naming)
|
|
187
242
|
if [[ -f "$observations_file" ]]; then
|
|
188
243
|
cp -f "${observations_file}.bak.2" "${observations_file}.bak.3" 2>/dev/null || _aether_log_error "Could not rotate observations backup .bak.2 to .bak.3"
|
|
@@ -194,12 +249,14 @@ _learning_observe() {
|
|
|
194
249
|
jq --arg hash "$content_hash" \
|
|
195
250
|
--arg colony "$colony_name" \
|
|
196
251
|
--arg ts "$ts" \
|
|
252
|
+
--argjson trust_score "$trust_score" \
|
|
197
253
|
'
|
|
198
254
|
.observations |= map(
|
|
199
255
|
if .content_hash == $hash then
|
|
200
256
|
.observation_count += 1 |
|
|
201
257
|
.last_seen = $ts |
|
|
202
|
-
.colonies = ((.colonies + [$colony]) | unique)
|
|
258
|
+
.colonies = ((.colonies + [$colony]) | unique) |
|
|
259
|
+
.trust_score = ($trust_score | tonumber)
|
|
203
260
|
else
|
|
204
261
|
.
|
|
205
262
|
end
|
|
@@ -235,11 +292,20 @@ _learning_observe() {
|
|
|
235
292
|
fi
|
|
236
293
|
tmp_file="${observations_file}.tmp.$$"
|
|
237
294
|
|
|
295
|
+
# Compute trust score for new observation
|
|
296
|
+
local trust_result
|
|
297
|
+
trust_result=$(bash "$0" trust-calculate --source "$source_type" --evidence "$evidence_type" --days-since 0 2>/dev/null)
|
|
298
|
+
local trust_score
|
|
299
|
+
trust_score=$(echo "$trust_result" | jq -r '.result.score // "0.490000"' 2>/dev/null)
|
|
300
|
+
|
|
238
301
|
jq --arg hash "$content_hash" \
|
|
239
302
|
--arg content "$content" \
|
|
240
303
|
--arg type "$wisdom_type" \
|
|
241
304
|
--arg colony "$colony_name" \
|
|
242
305
|
--arg ts "$ts" \
|
|
306
|
+
--arg trust_score "$trust_score" \
|
|
307
|
+
--arg source_type "$source_type" \
|
|
308
|
+
--arg evidence_type "$evidence_type" \
|
|
243
309
|
'.observations += [{
|
|
244
310
|
"content_hash": $hash,
|
|
245
311
|
"content": $content,
|
|
@@ -247,7 +313,11 @@ _learning_observe() {
|
|
|
247
313
|
"observation_count": 1,
|
|
248
314
|
"first_seen": $ts,
|
|
249
315
|
"last_seen": $ts,
|
|
250
|
-
"colonies": [$colony]
|
|
316
|
+
"colonies": [$colony],
|
|
317
|
+
"trust_score": ($trust_score | tonumber),
|
|
318
|
+
"source_type": $source_type,
|
|
319
|
+
"evidence_type": $evidence_type,
|
|
320
|
+
"compression_level": 0
|
|
251
321
|
}]' "$observations_file" > "$tmp_file" || {
|
|
252
322
|
_aether_log_error "Could not create new observation entry"
|
|
253
323
|
rm -f "$tmp_file"
|
|
@@ -294,6 +364,7 @@ _learning_observe() {
|
|
|
294
364
|
--argjson threshold_met "$threshold_met" \
|
|
295
365
|
--argjson colonies "$colonies" \
|
|
296
366
|
--argjson is_new "$is_new" \
|
|
367
|
+
--arg trust_score "$trust_score" \
|
|
297
368
|
'{
|
|
298
369
|
content_hash: $hash,
|
|
299
370
|
content: $content,
|
|
@@ -302,7 +373,8 @@ _learning_observe() {
|
|
|
302
373
|
threshold: $threshold,
|
|
303
374
|
threshold_met: $threshold_met,
|
|
304
375
|
colonies: $colonies,
|
|
305
|
-
is_new: $is_new
|
|
376
|
+
is_new: $is_new,
|
|
377
|
+
trust_score: ($trust_score | tonumber)
|
|
306
378
|
}')
|
|
307
379
|
|
|
308
380
|
json_ok "$result"
|
package/.aether/utils/session.sh
CHANGED
|
@@ -275,6 +275,13 @@ _session_init() {
|
|
|
275
275
|
summary: "Session initialized"
|
|
276
276
|
}' > "$session_file.tmp"
|
|
277
277
|
mv "$session_file.tmp" "$session_file"
|
|
278
|
+
|
|
279
|
+
# Mirror to legacy path so session recovery (which checks .aether/data/session.json) works
|
|
280
|
+
local legacy_session="$DATA_DIR/session.json"
|
|
281
|
+
if [[ "$session_file" != "$legacy_session" ]]; then
|
|
282
|
+
cp "$session_file" "$legacy_session" 2>/dev/null || true
|
|
283
|
+
fi
|
|
284
|
+
|
|
278
285
|
json_ok "$(jq -n --arg sid "$session_id" --arg goal "$goal" --arg file "$session_file" \
|
|
279
286
|
'{session_id: $sid, goal: $goal, file: $file}')"
|
|
280
287
|
}
|
|
@@ -361,6 +368,12 @@ _session_update() {
|
|
|
361
368
|
json_err "$E_UNKNOWN" "Failed to rename temporary session file"
|
|
362
369
|
}
|
|
363
370
|
|
|
371
|
+
# Mirror to legacy path so session recovery (which checks .aether/data/session.json) works
|
|
372
|
+
local legacy_session="$DATA_DIR/session.json"
|
|
373
|
+
if [[ "$session_file" != "$legacy_session" ]]; then
|
|
374
|
+
cp "$session_file" "$legacy_session" 2>/dev/null || true
|
|
375
|
+
fi
|
|
376
|
+
|
|
364
377
|
json_ok "$(jq -n --arg cmd "$cmd_run" '{updated: true, command: $cmd}')"
|
|
365
378
|
}
|
|
366
379
|
|
|
@@ -229,7 +229,7 @@ _state_migrate() {
|
|
|
229
229
|
# Gracefully degrades: missing files produce zero/default values
|
|
230
230
|
# ============================================================================
|
|
231
231
|
_colony_vital_signs() {
|
|
232
|
-
local cvs_state_file="$
|
|
232
|
+
local cvs_state_file="$DATA_DIR/COLONY_STATE.json"
|
|
233
233
|
local cvs_midden_file="$COLONY_DATA_DIR/midden/midden.json"
|
|
234
234
|
local cvs_phero_file="$COLONY_DATA_DIR/pheromones.json"
|
|
235
235
|
local cvs_session_file="$COLONY_DATA_DIR/session.json"
|