instar 1.3.305 → 1.3.307
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/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +32 -30
- package/dist/commands/init.js.map +1 -1
- package/dist/core/Config.d.ts.map +1 -1
- package/dist/core/Config.js +2 -0
- package/dist/core/Config.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +6 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +3 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/types.d.ts +4 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/scheduler/JobScheduler.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.js +10 -6
- package/dist/scheduler/JobScheduler.js.map +1 -1
- package/package.json +1 -1
- package/scripts/instar-dev-precommit.js +48 -16
- package/src/data/builtin-manifest.json +58 -58
- package/src/scaffold/templates/jobs/instar/commitment-detection.md +2 -1
- package/src/scaffold/templates/jobs/instar/correction-analyzer.md +5 -1
- package/src/scaffold/templates/jobs/instar/evolution-overdue-check.md +1 -1
- package/src/scaffold/templates/jobs/instar/evolution-proposal-evaluate.md +1 -1
- package/src/scaffold/templates/jobs/instar/evolution-proposal-implement.md +1 -1
- package/src/scaffold/templates/jobs/instar/failure-analyzer.md +5 -1
- package/src/scaffold/templates/jobs/instar/identity-review.md +5 -4
- package/src/scaffold/templates/jobs/instar/initiative-digest-review.md +3 -1
- package/src/scaffold/templates/jobs/instar/insight-harvest.md +1 -1
- package/src/scaffold/templates/jobs/instar/mentor-onboarding.md +7 -4
- package/src/scaffold/templates/jobs/instar/org-intent-drift-audit.md +6 -4
- package/src/scaffold/templates/jobs/instar/overseer-development.md +4 -1
- package/src/scaffold/templates/jobs/instar/overseer-guardian.md +4 -1
- package/src/scaffold/templates/jobs/instar/overseer-infrastructure.md +4 -1
- package/src/scaffold/templates/jobs/instar/overseer-learning.md +4 -1
- package/src/scaffold/templates/jobs/instar/overseer-maintenance.md +4 -1
- package/src/scaffold/templates/jobs/instar/release-readiness-check.md +2 -1
- package/src/templates/hooks/compaction-recovery.sh +11 -6
- package/src/templates/hooks/session-start.sh +11 -3
- package/src/templates/hooks/slack-channel-context.sh +5 -0
- package/src/templates/hooks/telegram-topic-context.sh +6 -0
- package/src/templates/scripts/emit-session-clock.sh +3 -3
- package/src/templates/scripts/imessage-reply.sh +10 -0
- package/src/templates/scripts/instar-watchdog.sh +12 -6
- package/src/templates/scripts/secret-drop-retrieve.mjs +10 -2
- package/src/templates/scripts/slack-reply.sh +5 -0
- package/src/templates/scripts/whatsapp-reply.sh +5 -0
- package/upgrades/1.3.306.md +22 -0
- package/upgrades/1.3.307.md +20 -0
- package/upgrades/gate-blocked-audit-hygiene.eli16.md +11 -0
- package/upgrades/side-effects/gate-blocked-audit-hygiene.md +41 -0
- package/upgrades/side-effects/template-agent-id-header.md +48 -0
|
@@ -22,17 +22,20 @@ That keeps the structural two-hats enforcement in code, not in this prompt.
|
|
|
22
22
|
|
|
23
23
|
Do exactly this:
|
|
24
24
|
|
|
25
|
-
1.
|
|
26
|
-
`
|
|
25
|
+
1. Resolve auth context:
|
|
26
|
+
`AUTH="${INSTAR_AUTH_TOKEN:-$(python3 -c "import json; v=json.load(open('.instar/config.json')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)}"; AGENT_ID="${INSTAR_AGENT_ID:-$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('projectName',''))" 2>/dev/null)}"; PORT="${INSTAR_PORT:-4042}"`
|
|
27
27
|
|
|
28
|
-
2.
|
|
28
|
+
2. POST to `http://localhost:${PORT}/mentor/tick` with the bearer token:
|
|
29
|
+
`curl -s -X POST -H "Authorization: Bearer $AUTH" -H "X-Instar-AgentId: $AGENT_ID" http://localhost:${PORT}/mentor/tick`
|
|
30
|
+
|
|
31
|
+
3. Read the JSON result. It will be one of:
|
|
29
32
|
- `{"ran":false,"reason":"disabled"}` — the mentor is off (the default). Nothing to do; stop.
|
|
30
33
|
- `{"ran":false,"reason":"budget"|"unsafe-window"|"canary-failed"}` — it correctly skipped this
|
|
31
34
|
tick. Stop; the next heartbeat will try again.
|
|
32
35
|
- `{"ran":true,...}` — a mentor tick ran. Note `leakDetected` and `observationsWritten` briefly,
|
|
33
36
|
then stop.
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
4. Do NOT do any mentoring work yourself in this session, do NOT read the mentee's logs or code,
|
|
36
39
|
and do NOT send the mentee any message — the in-process tick owns all of that. You are only the
|
|
37
40
|
timer that pokes it. Then exit.
|
|
38
41
|
|
|
@@ -20,13 +20,15 @@ The job:
|
|
|
20
20
|
|
|
21
21
|
1. Verify the prerequisites. The drift audit only makes sense when `.instar/ORG-INTENT.md` is present AND the Coherence Gate has accumulated at least 5 review entries in the last 7 days. If either condition isn't met, send a short Telegram message saying so and exit — there's nothing to audit yet.
|
|
22
22
|
|
|
23
|
-
2.
|
|
23
|
+
2. Set auth context: `AUTH="${INSTAR_AUTH_TOKEN:-$(python3 -c "import json; v=json.load(open('.instar/config.json')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)}"; AGENT_ID="${INSTAR_AGENT_ID:-$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('projectName',''))" 2>/dev/null)}"; PORT="${INSTAR_PORT:-4042}"`
|
|
24
24
|
|
|
25
|
-
3.
|
|
25
|
+
3. Call the drift digest endpoint: `curl -H "Authorization: Bearer $AUTH" -H "X-Instar-AgentId: $AGENT_ID" "http://localhost:$PORT/intent/org/drift?lookbackDays=7"`. The response is a structured analysis: overall block rate, per-reviewer breakdown, half-window trend comparison (first half vs second half block rate), cross-reference against ORG-INTENT constraints / goals / values, and a single `trend` label (`stable` | `rising` | `concerning` | `insufficient-data` | `no-org-intent`).
|
|
26
26
|
|
|
27
|
-
4. If `trend === '
|
|
27
|
+
4. If `trend === 'stable'` or `trend === 'insufficient-data'` or `trend === 'no-org-intent'`, stay silent. Most weeks should be silent — that means the agent is staying aligned with the organizational intent. The `shouldSurface` field in the response will be `false` in these cases.
|
|
28
28
|
|
|
29
|
-
5.
|
|
29
|
+
5. If `trend === 'rising'` or `trend === 'concerning'`, surface the drift via Telegram. The response includes a `summary` field (plain-English one-paragraph framing) and a `suggestions` array (specific next actions). Compose a conversational message that leads with the trend and what changed: "Block rate climbed from 4% to 12% in the second half of the week — most flags came from value-alignment." Then list the most-flagged reviewer dimensions. Then surface the top 1-2 suggestions. Keep it under 600 characters. Plain English. No raw JSON.
|
|
30
|
+
|
|
31
|
+
6. After surfacing (or not), the audit is done. No state to persist — the gate's own review history is the canonical store.
|
|
30
32
|
|
|
31
33
|
IMPORTANT: This is a guardian job, not a doer. Surface the drift and where it might be coming from. Don't try to fix it autonomously. The fix work — if any — is a deliberate update to `.instar/ORG-INTENT.md` constraints, or a configuration change to the gate, or an operator-level decision about whether the agent's behavior is actually drifting or whether the constraints need updating. Surface; don't fix.
|
|
32
34
|
|
|
@@ -14,7 +14,10 @@ unrestrictedTools: true
|
|
|
14
14
|
---
|
|
15
15
|
You are a Category Overseer for the DEVELOPMENT category. Your job is to review development-focused jobs.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
AUTH="${INSTAR_AUTH_TOKEN:-}"
|
|
18
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
19
|
+
|
|
20
|
+
1. Fetch the category report: curl -H "Authorization: Bearer $AUTH" -H "X-Instar-AgentId: $AGENT_ID" http://localhost:${INSTAR_PORT:-4042}/jobs/category-report/development?sinceHours=48
|
|
18
21
|
2. Analyze:
|
|
19
22
|
- Are development jobs consuming appropriate resources for their value?
|
|
20
23
|
- Are there CI/testing patterns that could be automated?
|
|
@@ -14,7 +14,10 @@ unrestrictedTools: true
|
|
|
14
14
|
---
|
|
15
15
|
You are a Category Overseer for the GUARDIAN category. Your job is to review all guardian/monitoring jobs and assess the health of the monitoring system itself.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
AUTH="${INSTAR_AUTH_TOKEN:-}"
|
|
18
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
19
|
+
|
|
20
|
+
1. Fetch the category report: curl -H "Authorization: Bearer $AUTH" -H "X-Instar-AgentId: $AGENT_ID" http://localhost:${INSTAR_PORT:-4042}/jobs/category-report/guardian?sinceHours=24
|
|
18
21
|
2. Analyze the report for:
|
|
19
22
|
- Jobs with high failure rates or consecutive failures
|
|
20
23
|
- Jobs that are being skipped excessively (especially for quota reasons)
|
|
@@ -14,7 +14,10 @@ unrestrictedTools: true
|
|
|
14
14
|
---
|
|
15
15
|
You are a Category Overseer for the INFRASTRUCTURE category. Your job is to review infrastructure/plumbing jobs.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
AUTH="${INSTAR_AUTH_TOKEN:-}"
|
|
18
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
19
|
+
|
|
20
|
+
1. Fetch the category report: curl -H "Authorization: Bearer $AUTH" -H "X-Instar-AgentId: $AGENT_ID" http://localhost:${INSTAR_PORT:-4042}/jobs/category-report/infrastructure?sinceHours=48
|
|
18
21
|
2. Analyze:
|
|
19
22
|
- Is git-sync succeeding? Any merge conflicts or divergence?
|
|
20
23
|
- Is dashboard-link-refresh keeping links current? Could it run less often?
|
|
@@ -14,7 +14,10 @@ unrestrictedTools: true
|
|
|
14
14
|
---
|
|
15
15
|
You are a Category Overseer for the LEARNING category. Your job is to review all evolution/learning jobs and assess whether the learning pipeline is producing genuine value.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
AUTH="${INSTAR_AUTH_TOKEN:-}"
|
|
18
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
19
|
+
|
|
20
|
+
1. Fetch the category report: curl -H "Authorization: Bearer $AUTH" -H "X-Instar-AgentId: $AGENT_ID" http://localhost:${INSTAR_PORT:-4042}/jobs/category-report/learning?sinceHours=48
|
|
18
21
|
2. Analyze:
|
|
19
22
|
- Are evolution proposals being generated AND accepted? What's the accept/reject ratio?
|
|
20
23
|
- Is insight-harvest finding novel insights or recycling stale ones?
|
|
@@ -14,7 +14,10 @@ unrestrictedTools: true
|
|
|
14
14
|
---
|
|
15
15
|
You are a Category Overseer for the MAINTENANCE category. Your job is to review all housekeeping/maintenance jobs and ensure they're keeping the system clean.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
AUTH="${INSTAR_AUTH_TOKEN:-}"
|
|
18
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
19
|
+
|
|
20
|
+
1. Fetch the category report: curl -H "Authorization: Bearer $AUTH" -H "X-Instar-AgentId: $AGENT_ID" http://localhost:${INSTAR_PORT:-4042}/jobs/category-report/maintenance?sinceHours=48
|
|
18
21
|
2. Analyze:
|
|
19
22
|
- Is memory-hygiene actually reducing stale entries, or finding nothing each run?
|
|
20
23
|
- Is project-map-refresh keeping the map accurate? How often does it find drift?
|
|
@@ -18,8 +18,9 @@ mcpAccess: none
|
|
|
18
18
|
Run the release-readiness check once. This is a mechanical, near-silent watchdog — do NOT message the user.
|
|
19
19
|
|
|
20
20
|
AUTH=$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('authToken',''))" 2>/dev/null)
|
|
21
|
+
AGENT_ID="${INSTAR_AGENT_ID:-$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('projectName',''))" 2>/dev/null)}"
|
|
21
22
|
|
|
22
23
|
1. Trigger one evaluation tick:
|
|
23
|
-
curl -s -X POST http://localhost:${INSTAR_PORT:-4042}/release-readiness/tick -H "Authorization: Bearer $AUTH"
|
|
24
|
+
curl -s -X POST http://localhost:${INSTAR_PORT:-4042}/release-readiness/tick -H "Authorization: Bearer $AUTH" -H "X-Instar-AgentId: $AGENT_ID"
|
|
24
25
|
2. That endpoint runs the ReleaseReadinessSentinel: it fetches canonical main, checks whether unreleased feature/fix work is piling up while publishing is blocked, and (only above the age threshold) raises a single deduped Attention item. All transitions are written to logs/sentinel-events.jsonl.
|
|
25
26
|
3. Exit silently. The sentinel owns all signalling (Attention queue) — this job is just the cadence. Do not relay anything to Telegram; do not summarize. If the curl fails, that is itself recorded by the server; do not retry-flood.
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
# matched on "compaction".
|
|
15
15
|
|
|
16
16
|
INSTAR_DIR="${CLAUDE_PROJECT_DIR:-.}/.instar"
|
|
17
|
+
CONFIG_FILE="$INSTAR_DIR/config.json"
|
|
18
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
19
|
+
if [ -z "$AGENT_ID" ] && [ -f "$CONFIG_FILE" ]; then
|
|
20
|
+
AGENT_ID=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE')).get('projectName',''))" 2>/dev/null)
|
|
21
|
+
fi
|
|
17
22
|
|
|
18
23
|
echo "=== COMPACTION RECOVERY — IDENTITY RESTORATION ==="
|
|
19
24
|
echo ""
|
|
@@ -36,7 +41,7 @@ if [ -f "$INSTAR_DIR/soul.md" ]; then
|
|
|
36
41
|
# Check integrity via server if available
|
|
37
42
|
SOUL_INTEGRITY="valid"
|
|
38
43
|
if [ -n "$PORT" ] && [ -n "$AUTH_TOKEN" ]; then
|
|
39
|
-
INTEGRITY_CHECK=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
44
|
+
INTEGRITY_CHECK=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" -H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
40
45
|
"http://localhost:${PORT}/identity/soul/integrity" 2>/dev/null)
|
|
41
46
|
SOUL_INTEGRITY=$(echo "$INTEGRITY_CHECK" | python3 -c "
|
|
42
47
|
import sys, json
|
|
@@ -180,7 +185,7 @@ if [ -f "$CONFIG_FILE" ]; then
|
|
|
180
185
|
if [ -z "$AUTH_TOKEN" ]; then
|
|
181
186
|
AUTH_TOKEN=$(python3 -c "import json; v=json.load(open('$CONFIG_FILE')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)
|
|
182
187
|
fi
|
|
183
|
-
FEATURES_JSON=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
188
|
+
FEATURES_JSON=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" -H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
184
189
|
"http://localhost:${PORT}/features/summary" 2>/dev/null)
|
|
185
190
|
if [ -n "$FEATURES_JSON" ]; then
|
|
186
191
|
DISCOVERY_STATE=$(echo "$FEATURES_JSON" | python3 -c "
|
|
@@ -255,7 +260,7 @@ if [ -f "$CONFIG_FILE" ]; then
|
|
|
255
260
|
AUTH_TOKEN=$(python3 -c "import json; v=json.load(open('$CONFIG_FILE')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)
|
|
256
261
|
fi
|
|
257
262
|
SLACK_MSGS=$(curl -s \
|
|
258
|
-
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
263
|
+
-H "Authorization: Bearer ${AUTH_TOKEN}" -H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
259
264
|
"http://localhost:${PORT}/slack/channels/${SLACK_CHANNEL}/messages?limit=20" 2>/dev/null)
|
|
260
265
|
|
|
261
266
|
echo "$SLACK_MSGS" | python3 -c "
|
|
@@ -358,7 +363,7 @@ except Exception:
|
|
|
358
363
|
fi
|
|
359
364
|
if [ -n "$AUTH_TOKEN" ]; then
|
|
360
365
|
RECENT_MSGS=$(curl -s \
|
|
361
|
-
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
366
|
+
-H "Authorization: Bearer ${AUTH_TOKEN}" -H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
362
367
|
"http://localhost:${PORT}/telegram/topics/${TOPIC_FOR_CONTEXT}/messages?limit=15" 2>/dev/null)
|
|
363
368
|
else
|
|
364
369
|
RECENT_MSGS=$(curl -s \
|
|
@@ -433,7 +438,7 @@ if [ -f "$CONFIG_FILE" ]; then
|
|
|
433
438
|
if [ -z "$AUTH_TOKEN" ]; then
|
|
434
439
|
AUTH_TOKEN=$(python3 -c "import json; v=json.load(open('$CONFIG_FILE')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)
|
|
435
440
|
fi
|
|
436
|
-
WORKING_MEM=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
441
|
+
WORKING_MEM=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" -H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
437
442
|
"http://localhost:${PORT}/context/working-memory?prompt=compaction-recovery&limit=5" 2>/dev/null)
|
|
438
443
|
if [ -n "$WORKING_MEM" ]; then
|
|
439
444
|
CONTEXT=$(echo "$WORKING_MEM" | python3 -c "
|
|
@@ -472,7 +477,7 @@ if [ -f "$CONFIG_FILE" ] && command -v tmux >/dev/null 2>&1; then
|
|
|
472
477
|
AUTH_TOKEN=$(python3 -c "import json; v=json.load(open('$CONFIG_FILE')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)
|
|
473
478
|
fi
|
|
474
479
|
curl -s -m 2 -X POST \
|
|
475
|
-
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
480
|
+
-H "Authorization: Bearer ${AUTH_TOKEN}" -H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
476
481
|
-H "Content-Type: application/json" \
|
|
477
482
|
-d "{\"sessionName\":\"${TMUX_SESSION}\"}" \
|
|
478
483
|
"http://localhost:${PORT}/internal/compaction-resume" >/dev/null 2>&1 &
|
|
@@ -34,10 +34,15 @@ AUTH_TOKEN="${INSTAR_AUTH_TOKEN:-}"
|
|
|
34
34
|
if [ -z "$AUTH_TOKEN" ]; then
|
|
35
35
|
AUTH_TOKEN=$(python3 -c "import json; v=json.load(open('$CONFIG_FILE')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)
|
|
36
36
|
fi
|
|
37
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
38
|
+
if [ -z "$AGENT_ID" ]; then
|
|
39
|
+
AGENT_ID=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE')).get('projectName',''))" 2>/dev/null)
|
|
40
|
+
fi
|
|
37
41
|
|
|
38
42
|
# Check if server is alive
|
|
39
43
|
HEALTH=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
40
44
|
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
45
|
+
-H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
41
46
|
"http://localhost:${PORT}/health" 2>/dev/null)
|
|
42
47
|
if [ "$HEALTH" != "200" ]; then
|
|
43
48
|
exit 0
|
|
@@ -50,15 +55,17 @@ curl -s -X POST "http://localhost:${PORT}/scope-coherence/reset" -o /dev/null 2>
|
|
|
50
55
|
# Record session start for reflection metrics (usage-based reflection trigger)
|
|
51
56
|
curl -s -X POST "http://localhost:${PORT}/reflection/session-start" \
|
|
52
57
|
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
58
|
+
-H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
53
59
|
-H "Content-Type: application/json" -o /dev/null 2>/dev/null || true
|
|
54
60
|
|
|
55
61
|
# Reset homeostasis state for new session (work-velocity awareness)
|
|
56
62
|
curl -s -X POST "http://localhost:${PORT}/homeostasis/reset" \
|
|
57
63
|
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
64
|
+
-H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
58
65
|
-H "Content-Type: application/json" -o /dev/null 2>/dev/null || true
|
|
59
66
|
|
|
60
67
|
# Check reflection metrics — usage-based reflection suggestion
|
|
61
|
-
REFLECTION_CHECK=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
68
|
+
REFLECTION_CHECK=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" -H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
62
69
|
"http://localhost:${PORT}/reflection/metrics" 2>/dev/null)
|
|
63
70
|
if [ -n "$REFLECTION_CHECK" ]; then
|
|
64
71
|
SUGGESTED=$(echo "$REFLECTION_CHECK" | python3 -c "import sys,json; print(json.load(sys.stdin).get('suggested',False))" 2>/dev/null)
|
|
@@ -166,7 +173,7 @@ if [ -n "$JOB_SLUG" ]; then
|
|
|
166
173
|
QUERY_URL="${QUERY_URL}&jobSlug=${JOB_SLUG_ENCODED}"
|
|
167
174
|
fi
|
|
168
175
|
|
|
169
|
-
WORKING_MEM=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" "$QUERY_URL" 2>/dev/null)
|
|
176
|
+
WORKING_MEM=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" -H "X-Instar-AgentId: ${AGENT_ID}" "$QUERY_URL" 2>/dev/null)
|
|
170
177
|
|
|
171
178
|
if [ -z "$WORKING_MEM" ]; then
|
|
172
179
|
exit 0
|
|
@@ -199,7 +206,7 @@ except Exception:
|
|
|
199
206
|
# Active commitments injection (PROMISE-BEACON-SPEC Round 3 #7).
|
|
200
207
|
# Surfaces beacon-watched pending commitments so the agent can self-regulate
|
|
201
208
|
# pacing across compaction. Capped server-side at 20 entries.
|
|
202
|
-
ACTIVE_COMMITMENTS=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
209
|
+
ACTIVE_COMMITMENTS=$(curl -s -H "Authorization: Bearer ${AUTH_TOKEN}" -H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
203
210
|
"http://localhost:${PORT}/commitments/active-context" 2>/dev/null)
|
|
204
211
|
if [ -n "$ACTIVE_COMMITMENTS" ]; then
|
|
205
212
|
python3 -c "
|
|
@@ -326,6 +333,7 @@ if [ -n "$PORT" ] && [ -n "$AUTH_TOKEN" ]; then
|
|
|
326
333
|
|
|
327
334
|
EVAL_RESULT=$(curl -s --max-time 6 -X POST \
|
|
328
335
|
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
336
|
+
-H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
329
337
|
-H "Content-Type: application/json" \
|
|
330
338
|
"http://localhost:${PORT}/features/evaluate-context" \
|
|
331
339
|
-d "{\"topicCategory\":\"${TOPIC_LABEL}\",\"conversationIntent\":\"${INTENT}\",\"problemCategories\":[],\"autonomyProfile\":\"collaborative\",\"enabledFeatures\":[]}" \
|
|
@@ -23,6 +23,7 @@ fi
|
|
|
23
23
|
# Get port and auth from config
|
|
24
24
|
PORT="${INSTAR_PORT:-}"
|
|
25
25
|
AUTH=""
|
|
26
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
26
27
|
|
|
27
28
|
if [ -f ".instar/config.json" ]; then
|
|
28
29
|
if [ -z "$PORT" ]; then
|
|
@@ -37,6 +38,9 @@ if [ -f ".instar/config.json" ]; then
|
|
|
37
38
|
if [ -z "$AUTH" ]; then
|
|
38
39
|
AUTH=$(python3 -c "import json; v=json.load(open('.instar/config.json')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)
|
|
39
40
|
fi
|
|
41
|
+
if [ -z "$AGENT_ID" ]; then
|
|
42
|
+
AGENT_ID=$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('projectName',''))" 2>/dev/null)
|
|
43
|
+
fi
|
|
40
44
|
fi
|
|
41
45
|
|
|
42
46
|
PORT="${PORT:-4042}"
|
|
@@ -49,6 +53,7 @@ fi
|
|
|
49
53
|
# Fetch channel history from ring buffer cache
|
|
50
54
|
MESSAGES=$(curl -sf \
|
|
51
55
|
${AUTH:+-H "Authorization: Bearer $AUTH"} \
|
|
56
|
+
${AGENT_ID:+-H "X-Instar-AgentId: $AGENT_ID"} \
|
|
52
57
|
"http://localhost:${PORT}/slack/channels/${CHANNEL_ID}/messages?limit=30" 2>/dev/null) || exit 0
|
|
53
58
|
|
|
54
59
|
# Format thread history for session context
|
|
@@ -66,11 +66,16 @@ AUTH_TOKEN="${INSTAR_AUTH_TOKEN:-}"
|
|
|
66
66
|
if [ -z "$AUTH_TOKEN" ] && [ -f "$CONFIG_FILE" ]; then
|
|
67
67
|
AUTH_TOKEN=$(python3 -c "import json; v=json.load(open('$CONFIG_FILE')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)
|
|
68
68
|
fi
|
|
69
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
70
|
+
if [ -z "$AGENT_ID" ] && [ -f "$CONFIG_FILE" ]; then
|
|
71
|
+
AGENT_ID=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE')).get('projectName',''))" 2>/dev/null)
|
|
72
|
+
fi
|
|
69
73
|
|
|
70
74
|
# Fetch recent messages for this topic
|
|
71
75
|
if [ -n "$AUTH_TOKEN" ]; then
|
|
72
76
|
RECENT_MSGS=$(curl -s \
|
|
73
77
|
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
78
|
+
-H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
74
79
|
"http://localhost:${PORT}/telegram/topics/${TOPIC_ID}/messages?limit=30" 2>/dev/null)
|
|
75
80
|
else
|
|
76
81
|
RECENT_MSGS=$(curl -s \
|
|
@@ -85,6 +90,7 @@ TOPIC_BRIEFING=""
|
|
|
85
90
|
if [ -n "$AUTH_TOKEN" ]; then
|
|
86
91
|
TOPIC_BRIEFING=$(curl -s --max-time 2 \
|
|
87
92
|
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
93
|
+
-H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
88
94
|
"http://localhost:${PORT}/topic-intent/${TOPIC_ID}/briefing" 2>/dev/null)
|
|
89
95
|
else
|
|
90
96
|
TOPIC_BRIEFING=$(curl -s --max-time 2 \
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# computed) values. The caller (autonomous-stop-hook) has already resolved
|
|
14
14
|
# the record + computed elapsed/remaining, so there is NO re-resolution —
|
|
15
15
|
# the injected clock can never disagree with the hook's expiry verdict.
|
|
16
|
-
# query <topic> <port> <auth>
|
|
16
|
+
# query <topic> <port> <auth> [agent-id]
|
|
17
17
|
# Curls GET /session/clock?topic=<topic> and prints the SESSION CLOCK line
|
|
18
18
|
# for the first active session (or nothing if none / server unreachable).
|
|
19
19
|
|
|
@@ -42,11 +42,11 @@ case "$MODE" in
|
|
|
42
42
|
render_line "${1:-}" "${2:-}" "${3:-0}" "${4:-}" "${5:-}"
|
|
43
43
|
;;
|
|
44
44
|
query)
|
|
45
|
-
TOPIC="${1:-}"; PORT="${2:-}"; AUTH="${3:-}"
|
|
45
|
+
TOPIC="${1:-}"; PORT="${2:-}"; AUTH="${3:-}"; AGENT_ID="${4:-${INSTAR_AGENT_ID:-}}"
|
|
46
46
|
[ -z "$PORT" ] && exit 0
|
|
47
47
|
URL="http://localhost:${PORT}/session/clock"
|
|
48
48
|
[ -n "$TOPIC" ] && URL="${URL}?topic=${TOPIC}"
|
|
49
|
-
RESP=$(curl -s --max-time 3 -H "Authorization: Bearer ${AUTH}" "$URL" 2>/dev/null)
|
|
49
|
+
RESP=$(curl -s --max-time 3 -H "Authorization: Bearer ${AUTH}" -H "X-Instar-AgentId: ${AGENT_ID}" "$URL" 2>/dev/null)
|
|
50
50
|
[ -z "$RESP" ] && exit 0
|
|
51
51
|
printf '%s' "$RESP" | python3 -c "
|
|
52
52
|
import sys, json
|
|
@@ -47,9 +47,13 @@ PORT="${INSTAR_PORT:-4042}"
|
|
|
47
47
|
# string-type guard so the { "secret": true } placeholder produced by
|
|
48
48
|
# SecretMigrator cannot leak through as a bogus Bearer.
|
|
49
49
|
AUTH_TOKEN="${INSTAR_AUTH_TOKEN:-}"
|
|
50
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
50
51
|
if [ -z "$AUTH_TOKEN" ] && [ -f ".instar/config.json" ]; then
|
|
51
52
|
AUTH_TOKEN=$(python3 -c "import json; v=json.load(open('.instar/config.json')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)
|
|
52
53
|
fi
|
|
54
|
+
if [ -z "$AGENT_ID" ] && [ -f ".instar/config.json" ]; then
|
|
55
|
+
AGENT_ID=$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('projectName',''))" 2>/dev/null)
|
|
56
|
+
fi
|
|
53
57
|
|
|
54
58
|
# Escape message for JSON
|
|
55
59
|
JSON_MSG=$(printf '%s' "$MSG" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))' 2>/dev/null)
|
|
@@ -67,12 +71,17 @@ AUTH_HEADER=""
|
|
|
67
71
|
if [ -n "$AUTH_TOKEN" ]; then
|
|
68
72
|
AUTH_HEADER="Authorization: Bearer ${AUTH_TOKEN}"
|
|
69
73
|
fi
|
|
74
|
+
AGENT_HEADER=""
|
|
75
|
+
if [ -n "$AGENT_ID" ]; then
|
|
76
|
+
AGENT_HEADER="X-Instar-AgentId: ${AGENT_ID}"
|
|
77
|
+
fi
|
|
70
78
|
|
|
71
79
|
# ── Step 1: Validate with server BEFORE sending (get single-use token) ──
|
|
72
80
|
|
|
73
81
|
VALIDATE_RESPONSE=$(curl -s -w "\n%{http_code}" \
|
|
74
82
|
-X POST "http://localhost:${PORT}/imessage/validate-send/${ENCODED_RECIPIENT}" \
|
|
75
83
|
${AUTH_HEADER:+-H "$AUTH_HEADER"} \
|
|
84
|
+
${AGENT_HEADER:+-H "$AGENT_HEADER"} \
|
|
76
85
|
-H 'Content-Type: application/json' \
|
|
77
86
|
-d "{\"text\":${JSON_MSG}}" 2>/dev/null)
|
|
78
87
|
|
|
@@ -140,6 +149,7 @@ fi
|
|
|
140
149
|
|
|
141
150
|
curl -s -o /dev/null -w "" -X POST "http://localhost:${PORT}/imessage/reply/${ENCODED_RECIPIENT}" \
|
|
142
151
|
${AUTH_HEADER:+-H "$AUTH_HEADER"} \
|
|
152
|
+
${AGENT_HEADER:+-H "$AGENT_HEADER"} \
|
|
143
153
|
-H 'Content-Type: application/json' \
|
|
144
154
|
-d "{\"text\":${JSON_MSG},\"sendToken\":\"${SEND_TOKEN}\"}" 2>/dev/null || \
|
|
145
155
|
echo "Warning: server confirmation failed (message was sent)" >&2
|
|
@@ -328,9 +328,11 @@ escalate_via_peer() {
|
|
|
328
328
|
# placeholder that SecretMigrator writes after externalizing the auth token:
|
|
329
329
|
# `c.authToken || ''` would otherwise return the object itself (truthy)
|
|
330
330
|
# which serializes to "[object Object]" — never a valid Bearer.
|
|
331
|
-
peer_meta=$("$node_bin" -e "const c=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));const t=typeof c.authToken==='string'?c.authToken:'';process.stdout.write((c.port||'')+'\t'+t)" "$peer_dir/.instar/config.json" 2>/dev/null || true)
|
|
331
|
+
peer_meta=$("$node_bin" -e "const c=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));const t=typeof c.authToken==='string'?c.authToken:'';process.stdout.write((c.port||'')+'\t'+t+'\t'+(c.projectName||''))" "$peer_dir/.instar/config.json" 2>/dev/null || true)
|
|
332
332
|
peer_port="${peer_meta%% *}"
|
|
333
|
-
|
|
333
|
+
peer_rest="${peer_meta#* }"
|
|
334
|
+
peer_auth="${peer_rest%% *}"
|
|
335
|
+
peer_agent_id="${peer_rest#* }"
|
|
334
336
|
[ -z "$peer_port" ] && continue
|
|
335
337
|
|
|
336
338
|
health_code=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${peer_port}/health" 2>/dev/null || echo "000")
|
|
@@ -356,6 +358,7 @@ JSONEOF
|
|
|
356
358
|
resp_code=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
357
359
|
-X POST "http://localhost:${peer_port}/attention" \
|
|
358
360
|
-H "Authorization: Bearer $peer_auth" \
|
|
361
|
+
-H "X-Instar-AgentId: $peer_agent_id" \
|
|
359
362
|
-H "Content-Type: application/json" \
|
|
360
363
|
-H "X-Instar-Request: 1" \
|
|
361
364
|
-d "$payload" 2>/dev/null || echo "000")
|
|
@@ -385,6 +388,7 @@ JSONEOF
|
|
|
385
388
|
resp_code=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
386
389
|
-X POST "http://localhost:${peer_port}/attention" \
|
|
387
390
|
-H "Authorization: Bearer $peer_auth" \
|
|
391
|
+
-H "X-Instar-AgentId: $peer_agent_id" \
|
|
388
392
|
-H "Content-Type: application/json" \
|
|
389
393
|
-H "X-Instar-Request: 1" \
|
|
390
394
|
-d "$safe_payload" 2>/dev/null || echo "000")
|
|
@@ -434,7 +438,7 @@ probe_server_identity() {
|
|
|
434
438
|
local config_file="$project_dir/.instar/config.json"
|
|
435
439
|
[ ! -r "$config_file" ] && return 0
|
|
436
440
|
|
|
437
|
-
local node_bin port auth_token
|
|
441
|
+
local node_bin port auth_token agent_id
|
|
438
442
|
node_bin=$(resolve_node || true)
|
|
439
443
|
[ -z "$node_bin" ] && return 0
|
|
440
444
|
|
|
@@ -447,9 +451,11 @@ probe_server_identity() {
|
|
|
447
451
|
# auth token — `String(c.authToken || '')` would otherwise yield
|
|
448
452
|
# "[object Object]" and the Bearer would be invalid.
|
|
449
453
|
local meta
|
|
450
|
-
meta=$("$node_bin" -e "const c=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));const t=process.env.INSTAR_AUTH_TOKEN||(typeof c.authToken==='string'?c.authToken:'');process.stdout.write(String(c.port||'')+'\t'+t)" "$config_file" 2>/dev/null || true)
|
|
454
|
+
meta=$("$node_bin" -e "const c=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));const t=process.env.INSTAR_AUTH_TOKEN||(typeof c.authToken==='string'?c.authToken:'');process.stdout.write(String(c.port||'')+'\t'+t+'\t'+(c.projectName||''))" "$config_file" 2>/dev/null || true)
|
|
451
455
|
port="${meta%% *}"
|
|
452
|
-
|
|
456
|
+
rest="${meta#* }"
|
|
457
|
+
auth_token="${rest%% *}"
|
|
458
|
+
agent_id="${rest#* }"
|
|
453
459
|
# Lifeline-only agents have no port; treat as healthy from probe POV.
|
|
454
460
|
[ -z "$port" ] && return 0
|
|
455
461
|
|
|
@@ -460,7 +466,7 @@ probe_server_identity() {
|
|
|
460
466
|
local resp http_code body project_field auth_args
|
|
461
467
|
auth_args=()
|
|
462
468
|
if [ -n "$auth_token" ]; then
|
|
463
|
-
auth_args=(-H "Authorization: Bearer $auth_token")
|
|
469
|
+
auth_args=(-H "Authorization: Bearer $auth_token" -H "X-Instar-AgentId: $agent_id")
|
|
464
470
|
fi
|
|
465
471
|
resp=$(curl -sS --max-time 5 -w '\n%{http_code}' "${auth_args[@]}" "http://localhost:${port}/health" 2>/dev/null || echo $'\n000')
|
|
466
472
|
http_code=$(echo "$resp" | tail -n1)
|
|
@@ -81,10 +81,12 @@ if (runMode && runCmd.length === 0) {
|
|
|
81
81
|
// guard so the { "secret": true } placeholder produced by SecretMigrator
|
|
82
82
|
// cannot leak through as a bogus Bearer.
|
|
83
83
|
let authToken = process.env.INSTAR_AUTH_TOKEN || '';
|
|
84
|
+
let agentId = process.env.INSTAR_AGENT_ID || '';
|
|
84
85
|
let port;
|
|
85
86
|
try {
|
|
86
87
|
const config = JSON.parse(fs.readFileSync('.instar/config.json', 'utf-8'));
|
|
87
88
|
if (!authToken && typeof config.authToken === 'string') authToken = config.authToken;
|
|
89
|
+
if (!agentId && typeof config.projectName === 'string') agentId = config.projectName;
|
|
88
90
|
port = config.port;
|
|
89
91
|
} catch (e) {
|
|
90
92
|
if (!authToken) {
|
|
@@ -104,7 +106,10 @@ if (!resolvedPort) {
|
|
|
104
106
|
const url = `http://localhost:${resolvedPort}/secrets/retrieve/${token}${consume ? '?consume=true' : ''}`;
|
|
105
107
|
const res = await fetch(url, {
|
|
106
108
|
method: 'POST',
|
|
107
|
-
headers: {
|
|
109
|
+
headers: {
|
|
110
|
+
'Authorization': `Bearer ${authToken}`,
|
|
111
|
+
...(agentId ? { 'X-Instar-AgentId': agentId } : {}),
|
|
112
|
+
},
|
|
108
113
|
});
|
|
109
114
|
|
|
110
115
|
if (!res.ok) {
|
|
@@ -160,7 +165,10 @@ async function consumeSubmission() {
|
|
|
160
165
|
try {
|
|
161
166
|
await fetch(`http://localhost:${resolvedPort}/secrets/retrieve/${token}?consume=true`, {
|
|
162
167
|
method: 'POST',
|
|
163
|
-
headers: {
|
|
168
|
+
headers: {
|
|
169
|
+
'Authorization': `Bearer ${authToken}`,
|
|
170
|
+
...(agentId ? { 'X-Instar-AgentId': agentId } : {}),
|
|
171
|
+
},
|
|
164
172
|
});
|
|
165
173
|
} catch {
|
|
166
174
|
// Non-fatal — the sliding/absolute-cap cleanup will reclaim it regardless.
|
|
@@ -38,6 +38,7 @@ fi
|
|
|
38
38
|
# Get port from env or config
|
|
39
39
|
PORT="${INSTAR_PORT:-}"
|
|
40
40
|
AUTH=""
|
|
41
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
41
42
|
|
|
42
43
|
if [ -f ".instar/config.json" ]; then
|
|
43
44
|
if [ -z "$PORT" ]; then
|
|
@@ -52,6 +53,9 @@ if [ -f ".instar/config.json" ]; then
|
|
|
52
53
|
if [ -z "$AUTH" ]; then
|
|
53
54
|
AUTH=$(python3 -c "import json; v=json.load(open('.instar/config.json')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)
|
|
54
55
|
fi
|
|
56
|
+
if [ -z "$AGENT_ID" ]; then
|
|
57
|
+
AGENT_ID=$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('projectName',''))" 2>/dev/null)
|
|
58
|
+
fi
|
|
55
59
|
fi
|
|
56
60
|
|
|
57
61
|
PORT="${PORT:-4042}"
|
|
@@ -68,6 +72,7 @@ RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
|
|
|
68
72
|
"http://localhost:${PORT}/slack/reply/${CHANNEL_ID}" \
|
|
69
73
|
-H "Content-Type: application/json" \
|
|
70
74
|
${AUTH:+-H "Authorization: Bearer $AUTH"} \
|
|
75
|
+
${AGENT_ID:+-H "X-Instar-AgentId: $AGENT_ID"} \
|
|
71
76
|
-d "{\"text\": ${ESCAPED}}")
|
|
72
77
|
|
|
73
78
|
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
|
|
@@ -38,9 +38,13 @@ PORT="${INSTAR_PORT:-4040}"
|
|
|
38
38
|
# string-type guard so the { "secret": true } placeholder produced by
|
|
39
39
|
# SecretMigrator cannot leak through as a bogus Bearer.
|
|
40
40
|
AUTH_TOKEN="${INSTAR_AUTH_TOKEN:-}"
|
|
41
|
+
AGENT_ID="${INSTAR_AGENT_ID:-}"
|
|
41
42
|
if [ -z "$AUTH_TOKEN" ] && [ -f ".instar/config.json" ]; then
|
|
42
43
|
AUTH_TOKEN=$(python3 -c "import json; v=json.load(open('.instar/config.json')).get('authToken',''); print(v if isinstance(v, str) else '')" 2>/dev/null)
|
|
43
44
|
fi
|
|
45
|
+
if [ -z "$AGENT_ID" ] && [ -f ".instar/config.json" ]; then
|
|
46
|
+
AGENT_ID=$(python3 -c "import json; print(json.load(open('.instar/config.json')).get('projectName',''))" 2>/dev/null)
|
|
47
|
+
fi
|
|
44
48
|
|
|
45
49
|
# Escape for JSON
|
|
46
50
|
JSON_MSG=$(printf '%s' "$MSG" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))' 2>/dev/null)
|
|
@@ -53,6 +57,7 @@ if [ -n "$AUTH_TOKEN" ]; then
|
|
|
53
57
|
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "http://localhost:${PORT}/whatsapp/send/${JID}" \
|
|
54
58
|
-H 'Content-Type: application/json' \
|
|
55
59
|
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
60
|
+
-H "X-Instar-AgentId: ${AGENT_ID}" \
|
|
56
61
|
-d "{\"text\":${JSON_MSG}}")
|
|
57
62
|
else
|
|
58
63
|
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "http://localhost:${PORT}/whatsapp/send/${JID}" \
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Built-in job, hook, and helper-script templates now include the agent identity header on authenticated local API calls. The scheduler and session launcher expose `INSTAR_AGENT_ID` alongside the existing local auth token so installed templates do not need to guess their identity.
|
|
9
|
+
|
|
10
|
+
## What to Tell Your User
|
|
11
|
+
|
|
12
|
+
Nothing user-visible. This quiets internal auth deprecation warnings from built-in jobs and hooks and keeps installed templates aligned with the stricter local auth contract.
|
|
13
|
+
|
|
14
|
+
## Summary of New Capabilities
|
|
15
|
+
|
|
16
|
+
- Job gates, script jobs, and spawned sessions receive `INSTAR_AGENT_ID`.
|
|
17
|
+
- Built-in local API templates send `X-Instar-AgentId` with bearer auth.
|
|
18
|
+
- A regression test scans shipped templates for bearer-only local API calls.
|
|
19
|
+
|
|
20
|
+
## Evidence
|
|
21
|
+
|
|
22
|
+
Grounded against canonical `authMiddleware`: bearer-only local API calls are still accepted during the deprecation window but log a warning; `/whoami` already requires the header. Focused validation: `npx vitest run tests/unit/template-agent-id-header.test.ts tests/unit/JobScheduler.test.ts tests/unit/JobScheduler-script-job.test.ts tests/unit/telegram-topic-context-session-clock.test.ts` passed, 41/41 tests.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The instar-dev pre-commit gate's decision-audit entries gain a verdict field ('pass' | 'blocked', written as 'pending' and finalized by a process exit hook that also re-stages the corrected file). The riding-the-retry design is unchanged — blocked-run entries still ride the next successful commit — but each entry is now self-describing.
|
|
9
|
+
|
|
10
|
+
## What to Tell Your User
|
|
11
|
+
|
|
12
|
+
Nothing user-visible — contributor audit hygiene. Gate audit records that ride into a PR from earlier blocked attempts (often under an unresolved or stale slug) are now labeled as blocked evaluations instead of reading like real shipped decisions.
|
|
13
|
+
|
|
14
|
+
## Summary of New Capabilities
|
|
15
|
+
|
|
16
|
+
- Decision-audit entries carry verdict: pass | blocked (pending only if finalization itself failed). Reviewers can distinguish shipped decisions from rode-along blocked evaluations at a glance.
|
|
17
|
+
|
|
18
|
+
## Evidence
|
|
19
|
+
|
|
20
|
+
Live recurrence (2026-06-05, twice in one day): echo's PR #836 swept in a blocked-attempt entry labeled with a DIFFERENT PR's slug (the then-freshest stale trace), and codey's PR #842 shipped an "unknown"-slug entry from a pre-trace gate run — both read as real decisions and needed review-time explanation. Pinned by 2 new sandbox tests in tests/unit/instar-dev-precommit-audit-staging.test.ts (blocked run finalizes 'blocked' incl. the STAGED copy; passing Tier-1 run finalizes 'pass') alongside the existing 4; the development itself caught the source-order trap twice (TDZ on the shared variable, exit-before-registration on the hook) — both now documented in code comments and covered by the blocked-path test.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Gate audit entries now say whether the commit actually passed
|
|
2
|
+
|
|
3
|
+
Every gated commit writes a small audit record — which tier the change declared, what the risk heuristic suggested, how big the diff was. By design, a record written during a BLOCKED attempt also survives: it gets staged, and when the developer fixes the problem and commits again, the old record rides along. The intent was honest bookkeeping — every gate evaluation leaves a trace.
|
|
4
|
+
|
|
5
|
+
The problem: nothing in the record said whether the evaluation passed or was blocked. And blocked attempts often run before the developer's trace file is fully resolved, so the record gets written under the wrong name — the slug says "unknown", or worse, it carries the slug of some OTHER pull request whose stale trace happened to be the freshest file at that moment. The result reads like a real shipped decision for a change that never shipped under that name. This bit twice in one day: Echo's registry-fix PR carried a record labeled with a different PR's slug, and Codey's header PR carried an "unknown" record — both rode into main as confusing audit noise, and both had to be explained in review.
|
|
6
|
+
|
|
7
|
+
The fix keeps the deliberate riding-the-retry design and makes every record self-describing: a **verdict** field, finalized the moment the gate process exits. Records start as "pending" when written, and a process exit hook — one hook covering every way the gate can finish — rewrites them as "pass" or "blocked" and re-stages the corrected file. A reviewer who sees verdict "blocked" next to a foreign slug now knows exactly what they're looking at: a failed evaluation under a stale trace, not a shipped decision.
|
|
8
|
+
|
|
9
|
+
Two implementation traps are documented in the code because both actually fired during development: the shared variable and the exit hook must both live ABOVE the gate's main flow in the file, because the gate runs top-to-bottom and can exit (blocked) before ever reaching code placed lower down. The first draft had the hook at the bottom; blocked runs exited before the hook was registered, and the verdict silently stayed "pending" — caught by the new tests.
|
|
10
|
+
|
|
11
|
+
Nothing changes about when records are written, staged, or how files are named. Old records without a verdict field remain valid history. Two new tests pin both sides: a blocked run's record finalizes as "blocked" (including the staged copy), and a passing Tier-1 run's record finalizes as "pass".
|