@meetless/mla 0.1.4
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/LICENSE +201 -0
- package/README.md +81 -0
- package/dist/build-info.json +9 -0
- package/dist/bundles/ask-core.js +396 -0
- package/dist/bundles/mcp.js +16592 -0
- package/dist/bundles/trace-core.js +263 -0
- package/dist/cli.js +828 -0
- package/dist/commands/activate.js +781 -0
- package/dist/commands/adoption.js +130 -0
- package/dist/commands/ask.js +290 -0
- package/dist/commands/context.js +114 -0
- package/dist/commands/debug.js +313 -0
- package/dist/commands/doctor.js +1021 -0
- package/dist/commands/enrich.js +427 -0
- package/dist/commands/evidence.js +229 -0
- package/dist/commands/flush.js +184 -0
- package/dist/commands/graph.js +104 -0
- package/dist/commands/init.js +272 -0
- package/dist/commands/internal-active-review.js +322 -0
- package/dist/commands/internal-auto-index.js +188 -0
- package/dist/commands/internal-capture-decisions.js +320 -0
- package/dist/commands/internal-evidence-correlate.js +239 -0
- package/dist/commands/internal-evidence-hooks.js +240 -0
- package/dist/commands/internal-evidence-inject.js +231 -0
- package/dist/commands/internal-finalize.js +221 -0
- package/dist/commands/internal-pretool-observe.js +225 -0
- package/dist/commands/internal-refresh.js +136 -0
- package/dist/commands/internal-session-nudge.js +120 -0
- package/dist/commands/internal-steer-sync.js +117 -0
- package/dist/commands/internal-turn-recap.js +140 -0
- package/dist/commands/kb.js +375 -0
- package/dist/commands/kb_add.js +681 -0
- package/dist/commands/kb_forget.js +283 -0
- package/dist/commands/kb_move.js +45 -0
- package/dist/commands/kb_pending.js +410 -0
- package/dist/commands/kb_personal.js +149 -0
- package/dist/commands/kb_promote.js +188 -0
- package/dist/commands/kb_purge.js +168 -0
- package/dist/commands/kb_reingest.js +335 -0
- package/dist/commands/kb_retime.js +170 -0
- package/dist/commands/kb_review.js +391 -0
- package/dist/commands/kb_revision.js +179 -0
- package/dist/commands/kb_show.js +385 -0
- package/dist/commands/label.js +226 -0
- package/dist/commands/login.js +295 -0
- package/dist/commands/logout.js +108 -0
- package/dist/commands/mcp-supervisor.js +93 -0
- package/dist/commands/mcp.js +227 -0
- package/dist/commands/queue-prune.js +98 -0
- package/dist/commands/review.js +358 -0
- package/dist/commands/rewire.js +124 -0
- package/dist/commands/rules.js +728 -0
- package/dist/commands/scan-context.js +67 -0
- package/dist/commands/session.js +347 -0
- package/dist/commands/stats.js +479 -0
- package/dist/commands/status.js +61 -0
- package/dist/commands/summary.js +250 -0
- package/dist/commands/turn.js +114 -0
- package/dist/commands/uninstall.js +222 -0
- package/dist/commands/whoami.js +102 -0
- package/dist/commands/workspace.js +130 -0
- package/dist/hooks-template/ce0-post-tool-use.sh +34 -0
- package/dist/hooks-template/ce0-session-start.sh +49 -0
- package/dist/hooks-template/ce0-stop.sh +29 -0
- package/dist/hooks-template/ce0-user-prompt-submit.sh +38 -0
- package/dist/hooks-template/common.sh +934 -0
- package/dist/hooks-template/event-batch-filter.jq +67 -0
- package/dist/hooks-template/flush.sh +503 -0
- package/dist/hooks-template/post-tool-use.sh +423 -0
- package/dist/hooks-template/pre-tool-use.sh +69 -0
- package/dist/hooks-template/session-start.sh +140 -0
- package/dist/hooks-template/stop.sh +308 -0
- package/dist/hooks-template/user-prompt-submit.sh +1162 -0
- package/dist/lib/activation.js +79 -0
- package/dist/lib/active-conflict-cache.js +141 -0
- package/dist/lib/active-memory.js +59 -0
- package/dist/lib/active-review-runner.js +26 -0
- package/dist/lib/agent-decision/index.js +25 -0
- package/dist/lib/agent-decision/keys.js +49 -0
- package/dist/lib/agent-decision/normalize-claude.js +183 -0
- package/dist/lib/agent-decision/types.js +21 -0
- package/dist/lib/agent-decision/validate.js +216 -0
- package/dist/lib/analytics/capture.js +96 -0
- package/dist/lib/analytics/command-event.js +267 -0
- package/dist/lib/analytics/consent.js +58 -0
- package/dist/lib/analytics/coverage-gap.js +96 -0
- package/dist/lib/analytics/envelope.js +236 -0
- package/dist/lib/analytics/event-id.js +86 -0
- package/dist/lib/analytics/evidence.js +150 -0
- package/dist/lib/analytics/followthrough.js +194 -0
- package/dist/lib/analytics/forwarder.js +109 -0
- package/dist/lib/analytics/logs.js +78 -0
- package/dist/lib/analytics/metrics.js +78 -0
- package/dist/lib/analytics/recorder.js +92 -0
- package/dist/lib/analytics/review-analytics.js +75 -0
- package/dist/lib/analytics/sequence.js +77 -0
- package/dist/lib/analytics/store.js +131 -0
- package/dist/lib/analytics/turn-recap.js +279 -0
- package/dist/lib/artifact_id.js +108 -0
- package/dist/lib/auth-breaker.js +161 -0
- package/dist/lib/auto-index.js +112 -0
- package/dist/lib/classifier.js +88 -0
- package/dist/lib/config.js +298 -0
- package/dist/lib/conflict-advisory.js +64 -0
- package/dist/lib/debug-bundle.js +520 -0
- package/dist/lib/enrichment/ingest.js +301 -0
- package/dist/lib/enrichment/plan.js +253 -0
- package/dist/lib/enrichment/protocol.js +359 -0
- package/dist/lib/enrichment/scout-brief.js +176 -0
- package/dist/lib/failure-telemetry.js +444 -0
- package/dist/lib/git.js +200 -0
- package/dist/lib/governance-cache.js +77 -0
- package/dist/lib/governed-path-cache.js +76 -0
- package/dist/lib/http.js +677 -0
- package/dist/lib/identity-envelope.js +23 -0
- package/dist/lib/kb-candidate.js +65 -0
- package/dist/lib/kb_acl.js +98 -0
- package/dist/lib/login.js +353 -0
- package/dist/lib/mcp-fetchers.js +130 -0
- package/dist/lib/mcp-restart.js +47 -0
- package/dist/lib/observability.js +805 -0
- package/dist/lib/open-url.js +33 -0
- package/dist/lib/orphan-guard.js +70 -0
- package/dist/lib/packaged.js +21 -0
- package/dist/lib/reconcile-sessions.js +171 -0
- package/dist/lib/redactor.js +89 -0
- package/dist/lib/relationship-candidate-query.js +27 -0
- package/dist/lib/render.js +611 -0
- package/dist/lib/rules/applicability.js +64 -0
- package/dist/lib/rules/attest-code-rule-version.js +47 -0
- package/dist/lib/rules/attest-notes-location.js +217 -0
- package/dist/lib/rules/attest-rule-version.js +69 -0
- package/dist/lib/rules/canonical-json.js +97 -0
- package/dist/lib/rules/ce0-emit.js +64 -0
- package/dist/lib/rules/ce0-evidence.js +281 -0
- package/dist/lib/rules/ce0-recall-sample.js +82 -0
- package/dist/lib/rules/ce0-rule.js +55 -0
- package/dist/lib/rules/ce0-sampling-bucket.js +15 -0
- package/dist/lib/rules/ce0-store.js +683 -0
- package/dist/lib/rules/ce0-telemetry-project.js +93 -0
- package/dist/lib/rules/ce0-telemetry.js +158 -0
- package/dist/lib/rules/code-rule-registry.js +17 -0
- package/dist/lib/rules/command-match.js +185 -0
- package/dist/lib/rules/consult-evidence-binding.js +27 -0
- package/dist/lib/rules/consultation-capture-adapter.js +193 -0
- package/dist/lib/rules/content-match.js +56 -0
- package/dist/lib/rules/deny-admission.js +99 -0
- package/dist/lib/rules/durable-observation.js +190 -0
- package/dist/lib/rules/enforce-notes-version.js +421 -0
- package/dist/lib/rules/evaluation-input-hash.js +126 -0
- package/dist/lib/rules/evaluator.js +108 -0
- package/dist/lib/rules/inert-rule-families.js +51 -0
- package/dist/lib/rules/input-authority-resolver.js +241 -0
- package/dist/lib/rules/interception-schema.js +170 -0
- package/dist/lib/rules/interception-store.js +267 -0
- package/dist/lib/rules/live-input-authority.js +66 -0
- package/dist/lib/rules/local-matcher.js +108 -0
- package/dist/lib/rules/local-observe.js +79 -0
- package/dist/lib/rules/local-rule-version-repo.js +214 -0
- package/dist/lib/rules/memory-requirement.js +109 -0
- package/dist/lib/rules/notes-observe.js +39 -0
- package/dist/lib/rules/notes-path.js +261 -0
- package/dist/lib/rules/notes-rule.js +75 -0
- package/dist/lib/rules/observe-adapter.js +114 -0
- package/dist/lib/rules/observed-rule-hash.js +119 -0
- package/dist/lib/rules/prompt-submit-adapter.js +132 -0
- package/dist/lib/rules/requirement-subject.js +240 -0
- package/dist/lib/rules/rule-activity.js +67 -0
- package/dist/lib/rules/rule-version-hash.js +151 -0
- package/dist/lib/rules/runtime-scope.js +55 -0
- package/dist/lib/rules/stop-adapter.js +116 -0
- package/dist/lib/rules/stop-response-snapshot.js +174 -0
- package/dist/lib/rules/types.js +10 -0
- package/dist/lib/rules/ulid.js +46 -0
- package/dist/lib/rules/version-evaluation.js +156 -0
- package/dist/lib/scanner/agent-memory.js +99 -0
- package/dist/lib/scanner/bootstrap-summary.js +87 -0
- package/dist/lib/scanner/cache.js +59 -0
- package/dist/lib/scanner/frontmatter.js +42 -0
- package/dist/lib/scanner/parse-directives.js +69 -0
- package/dist/lib/scanner/parse-structured.js +72 -0
- package/dist/lib/scanner/render.js +73 -0
- package/dist/lib/scanner/scan.js +132 -0
- package/dist/lib/scanner/score.js +38 -0
- package/dist/lib/scanner/scout-mission.js +126 -0
- package/dist/lib/scanner/types.js +7 -0
- package/dist/lib/session-scope.js +195 -0
- package/dist/lib/spool.js +355 -0
- package/dist/lib/staleness.js +100 -0
- package/dist/lib/steer-cache.js +87 -0
- package/dist/lib/tagged-reference.js +20 -0
- package/dist/lib/temporal.js +109 -0
- package/dist/lib/turn-recap-emit.js +67 -0
- package/dist/lib/unwire.js +253 -0
- package/dist/lib/update-check.js +469 -0
- package/dist/lib/update-notifier.js +217 -0
- package/dist/lib/upgrade-apply.js +643 -0
- package/dist/lib/wire.js +1087 -0
- package/dist/lib/workspace.js +96 -0
- package/dist/lib/zip.js +154 -0
- package/dist/pretool-entry.js +37 -0
- package/package.json +75 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stop.sh: Claude Code Stop hook. Writes session_stopped + finalize_requested
|
|
3
|
+
# events to the spool and spawns the flusher. Stop must return in <1s.
|
|
4
|
+
#
|
|
5
|
+
# Source: notes/20260527-bare-bones-mvp-codebase-evaluation-and-plan.md §5.2.
|
|
6
|
+
source "$(dirname "$0")/common.sh"
|
|
7
|
+
|
|
8
|
+
# Per-folder activation gate (opt-in). Exit before any work unless a
|
|
9
|
+
# `.meetless.json` marker is found by walking up from $PWD. See
|
|
10
|
+
# meetless_activated in common.sh. Run `mla activate` in a repo to opt in.
|
|
11
|
+
meetless_activated || exit 0
|
|
12
|
+
|
|
13
|
+
INPUT="$(cat)"
|
|
14
|
+
# Wedge v6 Epoch 29: validate stdin parses as JSON BEFORE any jq substitution.
|
|
15
|
+
# Stop is the critical path for review-packet creation; a single malformed
|
|
16
|
+
# Claude Code payload here silently kills the finalize_requested + flush
|
|
17
|
+
# pipeline and no review is ever generated. See session-start.sh.
|
|
18
|
+
if [[ -z "$INPUT" ]] || ! printf '%s' "$INPUT" | jq -e . >/dev/null 2>&1; then
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
SESSION_ID="$(echo "$INPUT" | jq -r '.session_id // empty')"
|
|
22
|
+
[[ -z "$SESSION_ID" ]] && exit 0
|
|
23
|
+
# Per-session OFF override (`mla deactivate`). Silences this one session even in
|
|
24
|
+
# an activated folder. See meetless_session_disabled in common.sh.
|
|
25
|
+
meetless_session_disabled "$SESSION_ID" && exit 0
|
|
26
|
+
|
|
27
|
+
TRANSCRIPT="$(echo "$INPUT" | jq -r '.transcript_path // empty')"
|
|
28
|
+
TS="$(date -u +%FT%TZ)"
|
|
29
|
+
|
|
30
|
+
# Transcript-flush settle (Bug B / Q6 race). Claude Code can fire Stop a beat
|
|
31
|
+
# BEFORE the turn's CLOSING assistant message is flushed to the transcript file.
|
|
32
|
+
# The reads below then grab a MID-TURN text block as finalMessage and the
|
|
33
|
+
# narration slice inherits the same wrong boundary.
|
|
34
|
+
#
|
|
35
|
+
# a6b36c66's first cut polled the transcript byte-SIZE until it stopped growing.
|
|
36
|
+
# That has a residual hole the dogfood audit of session 5d428e3e hit head-on:
|
|
37
|
+
# byte-size stability cannot tell "the turn finished" from "the single closing
|
|
38
|
+
# append has not landed yet". If the writer goes quiet for one poll interval
|
|
39
|
+
# before flushing the closing message as one append, the byte-size settle breaks
|
|
40
|
+
# early and the extraction still lands on the stale mid-turn block (live: stored
|
|
41
|
+
# "Control owns it (graph service). Let me read the actual def." instead of the
|
|
42
|
+
# real closing answer "Pulled the canonical model and the actual code path...").
|
|
43
|
+
#
|
|
44
|
+
# Modern Claude Code transcripts stamp every assistant entry with a stop_reason:
|
|
45
|
+
# mid-turn blocks that precede a tool call carry "tool_use"; the turn's CLOSING
|
|
46
|
+
# message carries "end_turn". So gate the settle on that SEMANTIC boundary, not
|
|
47
|
+
# on bytes: wait until an end_turn assistant entry with non-whitespace text
|
|
48
|
+
# exists ("ready"), poll while modern entries exist but none is closed yet
|
|
49
|
+
# ("wait"), and only fall back to byte-size stability for LEGACY transcripts that
|
|
50
|
+
# carry no stop_reason at all ("legacy"). Bounded under the <1s Stop budget; the
|
|
51
|
+
# common already-flushed case breaks on the first check with zero sleeps.
|
|
52
|
+
# Fail-soft: a never-closing file just hits the attempt cap and the extraction
|
|
53
|
+
# below falls back to the last text block. Tunable via env for tests.
|
|
54
|
+
_settle_verdict_jq='
|
|
55
|
+
split("\n")
|
|
56
|
+
| map(select(length > 0) | fromjson?)
|
|
57
|
+
| [ .[] | select(.type == "assistant") ] as $a
|
|
58
|
+
| ([ $a[] | select(.message | has("stop_reason")) ] | length) as $modern
|
|
59
|
+
| ([ $a[]
|
|
60
|
+
| select(.message.stop_reason == "end_turn")
|
|
61
|
+
| select((.message.content // [])
|
|
62
|
+
| any(.type == "text" and ((.text // "") | gsub("\\s"; "") | length > 0))) ]
|
|
63
|
+
| length) as $closed
|
|
64
|
+
| if $modern > 0 then (if $closed > 0 then "ready" else "wait" end) else "legacy" end
|
|
65
|
+
'
|
|
66
|
+
if [[ -n "$TRANSCRIPT" && -f "$TRANSCRIPT" ]]; then
|
|
67
|
+
_settle_poll="${MEETLESS_FINALMSG_POLL_SEC:-0.06}"
|
|
68
|
+
_settle_max="${MEETLESS_FINALMSG_MAX_ATTEMPTS:-10}"
|
|
69
|
+
_settle_prev="-1"
|
|
70
|
+
_settle_i=0
|
|
71
|
+
while [ "$_settle_i" -lt "$_settle_max" ]; do
|
|
72
|
+
_settle_verdict="$(tail -n 400 "$TRANSCRIPT" 2>/dev/null \
|
|
73
|
+
| jq -rR --slurp "$_settle_verdict_jq" 2>/dev/null || echo legacy)"
|
|
74
|
+
if [[ "$_settle_verdict" == "ready" ]]; then
|
|
75
|
+
break
|
|
76
|
+
elif [[ "$_settle_verdict" == "legacy" ]]; then
|
|
77
|
+
# No stop_reason anywhere: fall back to a6b36c66's byte-size stability.
|
|
78
|
+
_settle_size="$(wc -c < "$TRANSCRIPT" 2>/dev/null | tr -d ' ' || true)"
|
|
79
|
+
[[ -z "$_settle_size" ]] && _settle_size=0
|
|
80
|
+
if [[ "$_settle_size" == "$_settle_prev" ]]; then break; fi
|
|
81
|
+
_settle_prev="$_settle_size"
|
|
82
|
+
fi
|
|
83
|
+
# "wait" (modern, end_turn not flushed yet) or "legacy still growing": poll.
|
|
84
|
+
sleep "$_settle_poll" 2>/dev/null || true
|
|
85
|
+
_settle_i=$((_settle_i + 1))
|
|
86
|
+
done
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Best-effort closing assistant message (Q6: option A; transcript-flush settled above).
|
|
90
|
+
#
|
|
91
|
+
# Modern Claude Code transcripts have shape `{type: "assistant", message: {content: [{type: "text", text: "..."}, {type: "tool_use", ...}], stop_reason: "..."}}`.
|
|
92
|
+
# The legacy `.content // .message` fallback caught the whole envelope object
|
|
93
|
+
# and `tostring`'d the JSON dump into finalMessage (model, id, content blocks),
|
|
94
|
+
# which poisoned `agentClaimsRaw` and the intel synthesizer's view of what the
|
|
95
|
+
# agent claimed it did.
|
|
96
|
+
#
|
|
97
|
+
# Pick the turn's CLOSING message, not merely "the last assistant text block".
|
|
98
|
+
# The two diverge whenever a non-end_turn assistant entry trails the closing one
|
|
99
|
+
# (a stale mid-turn tool_use block during the pre-flush gap, or a trailing
|
|
100
|
+
# continuation artifact): "last text block" grabs the wrong one. So prefer the
|
|
101
|
+
# last assistant entry whose stop_reason is "end_turn" (the semantic turn
|
|
102
|
+
# boundary), and only fall back to the last text block for LEGACY transcripts
|
|
103
|
+
# with no stop_reason at all. Join its text blocks.
|
|
104
|
+
FINAL_MSG=""
|
|
105
|
+
if [[ -n "$TRANSCRIPT" && -f "$TRANSCRIPT" ]]; then
|
|
106
|
+
FINAL_MSG="$(tail -n 400 "$TRANSCRIPT" 2>/dev/null \
|
|
107
|
+
| jq -rR --slurp '
|
|
108
|
+
split("\n")
|
|
109
|
+
| map(select(length > 0) | fromjson?)
|
|
110
|
+
| [ .[] | select(.type == "assistant")
|
|
111
|
+
| select((.message.content // []) | any(.type == "text")) ]
|
|
112
|
+
| ((map(select(.message.stop_reason == "end_turn")) | last) // last) as $pick
|
|
113
|
+
| if $pick == null then ""
|
|
114
|
+
else ($pick.message.content // [])
|
|
115
|
+
| map(select(.type == "text") | .text // "")
|
|
116
|
+
| join("\n")
|
|
117
|
+
end
|
|
118
|
+
' 2>/dev/null || true)"
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# Best-effort intra-turn narration (full-prose replay; note 20260610 §4 P3 step
|
|
122
|
+
# 11, capture-scope option B). FINAL_MSG above is the agent's LAST assistant
|
|
123
|
+
# message (the closing summary). NARRATION is everything the agent SAID earlier
|
|
124
|
+
# in THIS turn, between tool calls, which the timeline replay was otherwise
|
|
125
|
+
# missing. Turn-bounding is the subtle part: tool_result entries are user-role
|
|
126
|
+
# too, so we cannot slice at "the last user entry" or we would cut mid-turn.
|
|
127
|
+
# Instead we find the last REAL user prompt (string content, or an array with no
|
|
128
|
+
# tool_result block) and take the assistant text entries AFTER it, dropping the
|
|
129
|
+
# LAST one (that is FINAL_MSG, so it is never double-counted). Empty (no
|
|
130
|
+
# narration, or only a closing summary) means no event is spooled. Narration is
|
|
131
|
+
# the default now (no kill switch); the post-tool-use hook captures it LIVE and
|
|
132
|
+
# this Stop pass is the compaction-safe backstop.
|
|
133
|
+
NARRATION=""
|
|
134
|
+
if [[ -n "$TRANSCRIPT" && -f "$TRANSCRIPT" ]]; then
|
|
135
|
+
NARRATION="$(tail -n 400 "$TRANSCRIPT" 2>/dev/null \
|
|
136
|
+
| jq -rR --slurp '
|
|
137
|
+
split("\n")
|
|
138
|
+
| map(select(length > 0) | fromjson?)
|
|
139
|
+
| . as $rows
|
|
140
|
+
| (reduce range(0; ($rows | length)) as $i (-1;
|
|
141
|
+
if ($rows[$i].type == "user")
|
|
142
|
+
and (
|
|
143
|
+
(($rows[$i].message.content | type) == "string")
|
|
144
|
+
or (
|
|
145
|
+
(($rows[$i].message.content // []) | type) == "array"
|
|
146
|
+
and (($rows[$i].message.content // []) | any(.type == "tool_result") | not)
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
then $i else . end
|
|
150
|
+
)) as $start
|
|
151
|
+
| (if $start < 0 then [] else $rows[($start + 1):] end)
|
|
152
|
+
| [ .[] | select(.type == "assistant")
|
|
153
|
+
| select((.message.content // []) | any(.type == "text")) ] as $texts
|
|
154
|
+
| ($texts | length) as $n
|
|
155
|
+
# Drop the SAME entry FINAL_MSG selected as the closing message: the last
|
|
156
|
+
# end_turn block, else the last text block (legacy). Dropping by INDEX,
|
|
157
|
+
# not by value, so two identical-content blocks do not both vanish; for a
|
|
158
|
+
# legacy transcript this is exactly the old `.[0:-1]` slice.
|
|
159
|
+
| (([ range(0; $n) | select($texts[.].message.stop_reason == "end_turn") ] | last) // ($n - 1)) as $ci
|
|
160
|
+
| [ range(0; $n) | select(. != $ci) | $texts[.] ]
|
|
161
|
+
| map((.message.content // [])
|
|
162
|
+
| map(select(.type == "text") | .text // "")
|
|
163
|
+
| join("\n"))
|
|
164
|
+
| map(select((gsub("\\s"; "") | length) > 0))
|
|
165
|
+
| join("\n\n")
|
|
166
|
+
' 2>/dev/null || true)"
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# Best-effort current session name. Mirrors the local picker: human /title
|
|
170
|
+
# (`custom-title`) wins, else the auto-titler's name (`ai-title`). Carrying it on
|
|
171
|
+
# session_stopped lets control track renames last-write-wins. See
|
|
172
|
+
# resolve_session_title in common.sh for the precedence + fail-soft contract.
|
|
173
|
+
SESSION_TITLE="$(resolve_session_title "$TRANSCRIPT")"
|
|
174
|
+
|
|
175
|
+
# ---- report-citation capture (P3) ---------------------------------------
|
|
176
|
+
# Parse the [XX:id] evidence tokens the agent's FINAL report cited and record
|
|
177
|
+
# them LOCALLY, keyed by (session_id, turn_index). This is the push-reference
|
|
178
|
+
# side of A1b: "did the agent's final report cite a source_id we injected, even
|
|
179
|
+
# with no Pull?". It is a local sibling of mcp-calls.jsonl (the pull side, P1)
|
|
180
|
+
# and ask-traces.jsonl (the inject side), so the A1 evidence-followthrough join
|
|
181
|
+
# stays a purely local reader. The turn counter is READ, never advanced
|
|
182
|
+
# (UserPromptSubmit owns it); Stop fires at the end of turn N's response while
|
|
183
|
+
# the counter still holds N. An empty array is recorded too: "this turn's report
|
|
184
|
+
# cited nothing" is a real A1b denominator signal. extract_source_ids (common.sh)
|
|
185
|
+
# is the single shared grammar with the pull side.
|
|
186
|
+
# notes/20260603-mla-kb-agent-proxy-and-evidence-adoption.md §7.1 P3 / §7.4 A1.
|
|
187
|
+
mkdir -p "$QUEUE_DIR" "$LOG_DIR"
|
|
188
|
+
REPORT_TURN="$(current_turn_index "$SESSION_ID")"
|
|
189
|
+
REPORT_SIDS="$(extract_source_ids "$FINAL_MSG")"
|
|
190
|
+
REPORT_LINE="$(jq -c -n \
|
|
191
|
+
--arg ts "$TS" --arg event "report_citations" \
|
|
192
|
+
--arg sessionId "$SESSION_ID" --argjson turn "$REPORT_TURN" \
|
|
193
|
+
--argjson sids "$REPORT_SIDS" \
|
|
194
|
+
'{ts: $ts, event: $event, session_id: $sessionId, turn_index: $turn, source_ids: $sids}')"
|
|
195
|
+
(
|
|
196
|
+
flock 9
|
|
197
|
+
printf '%s\n' "$REPORT_LINE" >> "$LOG_DIR/report-citations.jsonl"
|
|
198
|
+
) 9>"$LOG_DIR/report-citations.lock"
|
|
199
|
+
|
|
200
|
+
# End-of-run review card: surface up to 5 deterministic stale signals to the user.
|
|
201
|
+
# P0A-minimal: written to a LOCAL jsonl only (review_card is not in the flush
|
|
202
|
+
# allowlist), later surfaced by `mla status` / `mla context list`. Cheap jq read of
|
|
203
|
+
# the scan cache; never recomputes the scan. Always exits 0 so it cannot abort Stop.
|
|
204
|
+
build_stop_review_card() {
|
|
205
|
+
local cache="$HOME/.meetless/workspaces/$WORKSPACE_ID/scan-cache.json"
|
|
206
|
+
[[ -r "$cache" ]] || return 0
|
|
207
|
+
jq -c -n \
|
|
208
|
+
--slurpfile c "$cache" \
|
|
209
|
+
--arg sid "$SESSION_ID" \
|
|
210
|
+
--arg ts "$TS" \
|
|
211
|
+
'{
|
|
212
|
+
ts: $ts, event: "review_card", session_id: $sid,
|
|
213
|
+
items: ($c[0].staleSignals // [])[0:5] | map({id: .id, detail: .detail, source: .source}),
|
|
214
|
+
total: (($c[0].staleSignals // []) | length)
|
|
215
|
+
}' 2>/dev/null || true
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
REVIEW_CARD_LINE="$(build_stop_review_card)"
|
|
219
|
+
if [[ -n "$REVIEW_CARD_LINE" ]]; then
|
|
220
|
+
printf '%s\n' "$REVIEW_CARD_LINE" >> "$HOME/.meetless/workspaces/$WORKSPACE_ID/review-cards.jsonl" 2>/dev/null || true
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
STOPPED_KEY="$(gen_event_key)"
|
|
224
|
+
LINE_STOPPED="$(jq -c -n \
|
|
225
|
+
--arg ts "$TS" --arg event "session_stopped" --arg key "$STOPPED_KEY" \
|
|
226
|
+
--arg sessionId "$SESSION_ID" --arg final "$FINAL_MSG" --arg title "$SESSION_TITLE" \
|
|
227
|
+
'{ts: $ts, event: $event, eventKey: $key, sessionId: $sessionId, payload: {finalMessage: $final, sessionTitle: $title}}')"
|
|
228
|
+
|
|
229
|
+
FINALIZE_KEY="$(gen_event_key)"
|
|
230
|
+
LINE_FINALIZE="$(jq -c -n \
|
|
231
|
+
--arg ts "$TS" --arg event "finalize_requested" --arg key "$FINALIZE_KEY" \
|
|
232
|
+
--arg sessionId "$SESSION_ID" \
|
|
233
|
+
'{ts: $ts, event: $event, eventKey: $key, sessionId: $sessionId, payload: {}}')"
|
|
234
|
+
|
|
235
|
+
# Spool the intra-turn narration FIRST (before session_stopped) so it sorts ahead
|
|
236
|
+
# of "Session ended" in control's occurredAt-asc, id-asc timeline: same TS, lower
|
|
237
|
+
# row id. Only when there is actually narration to show (empty stays unspooled).
|
|
238
|
+
if [[ -n "$NARRATION" ]]; then
|
|
239
|
+
NARRATION_KEY="$(gen_event_key)"
|
|
240
|
+
LINE_NARRATION="$(jq -c -n \
|
|
241
|
+
--arg ts "$TS" --arg event "assistant_message" --arg key "$NARRATION_KEY" \
|
|
242
|
+
--arg sessionId "$SESSION_ID" --arg narration "$NARRATION" \
|
|
243
|
+
'{ts: $ts, event: $event, eventKey: $key, sessionId: $sessionId, payload: {narration: $narration}}')"
|
|
244
|
+
spool_append "$SESSION_ID" "$LINE_NARRATION"
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
spool_append "$SESSION_ID" "$LINE_STOPPED"
|
|
248
|
+
spool_append "$SESSION_ID" "$LINE_FINALIZE"
|
|
249
|
+
|
|
250
|
+
# ---- AskUserQuestion agent-decision backstop (stop transcript scan) ------
|
|
251
|
+
# The PostToolUse primary path (post-tool-use.sh) captures each answered
|
|
252
|
+
# AskUserQuestion in real time, but a hook that never fired (crash, race, a
|
|
253
|
+
# session that predates the matcher) would lose the decision. The Stop hook is
|
|
254
|
+
# the guaranteed backstop: scan THIS session's transcript for AskUserQuestion
|
|
255
|
+
# tool_use / tool_result pairs and spool any decision the primary path missed.
|
|
256
|
+
# A fast `grep -q` gate skips the scan cost entirely on the common no-question
|
|
257
|
+
# session. Both paths derive the SAME providerEventId, and --spool dedups against
|
|
258
|
+
# what the primary already queued, so a doubly-captured decision is spooled once.
|
|
259
|
+
# Spooled BEFORE spawn_flush so the decisions ride this same flush cycle.
|
|
260
|
+
# Fail-soft: a missing mla binary, an unreadable transcript, or a command error
|
|
261
|
+
# is swallowed and never delays or fails Stop (<1s budget).
|
|
262
|
+
# See notes/20260608-agent-decision-capture-design.md section 5.
|
|
263
|
+
if [[ -n "${MLA_PATH:-}" && -x "$MLA_PATH" && -n "$TRANSCRIPT" && -f "$TRANSCRIPT" ]] \
|
|
264
|
+
&& grep -q "AskUserQuestion" "$TRANSCRIPT" 2>/dev/null; then
|
|
265
|
+
DECISION_LINES="$("$MLA_PATH" _internal capture-decisions \
|
|
266
|
+
--source stop_transcript_scan --transcript "$TRANSCRIPT" \
|
|
267
|
+
--session "$SESSION_ID" --spool "$QUEUE_DIR/$SESSION_ID.jsonl" 2>/dev/null || true)"
|
|
268
|
+
while IFS= read -r DECISION_LINE; do
|
|
269
|
+
[[ -z "$DECISION_LINE" ]] && continue
|
|
270
|
+
spool_append "$SESSION_ID" "$DECISION_LINE"
|
|
271
|
+
done <<< "$DECISION_LINES"
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
spawn_flush "$SESSION_ID"
|
|
275
|
+
|
|
276
|
+
# Hands-off stale-session GC: sweep dead-session litter idle > 24h. Detached and
|
|
277
|
+
# reap-only (no re-drain), so it never blocks Stop and never re-flushes the live
|
|
278
|
+
# sessions. This session's own spool is handled by spawn_flush above.
|
|
279
|
+
spawn_reap
|
|
280
|
+
|
|
281
|
+
# Zone 2 auto-index: index THIS session's produced prose docs into the owner's
|
|
282
|
+
# Personal KB as SHADOW (never grounds anyone; INV-GROUNDING-APPROVED). Detached,
|
|
283
|
+
# fail-soft, and kill-switchable (MEETLESS_AUTO_INDEX=0), so it never blocks Stop.
|
|
284
|
+
# Runs after the reap so it rides the same end-of-Stop tail without delaying GC.
|
|
285
|
+
spawn_auto_index "$SESSION_ID"
|
|
286
|
+
|
|
287
|
+
# T4.2 evidence-outcome correlator (INV-CORRELATOR-1): close every eligible PENDING
|
|
288
|
+
# inject window (3 turns or 15 minutes) across ALL sessions and append one
|
|
289
|
+
# mla_evidence_outcome per closed inject to the local jsonl, then forward if
|
|
290
|
+
# telemetry is on. Detached, fail-soft, and kill-switchable (MEETLESS_EVIDENCE_ANALYTICS=0),
|
|
291
|
+
# so it never blocks Stop. Rides the same end-of-Stop tail as the auto-index above.
|
|
292
|
+
# No session argument: it sweeps cross-session because a window can close minutes
|
|
293
|
+
# after the originating session ended, and a Stop is the natural recompute tick.
|
|
294
|
+
spawn_evidence_correlate
|
|
295
|
+
|
|
296
|
+
# Layer D per-turn recap -> Langfuse: post THIS just-finished turn's assist recap
|
|
297
|
+
# to intel so it attaches the mla_ran / mla_assist scores + the full recap metadata
|
|
298
|
+
# to the turn's Langfuse trace (keyed on the per-turn $TRACE_ID intel adopts as the
|
|
299
|
+
# langfuse_trace_id). REPORT_TURN computed above is the just-finished turn N (the
|
|
300
|
+
# counter still holds N at Stop; UserPromptSubmit owns advancing it). Detached,
|
|
301
|
+
# fail-soft, and kill-switchable via MEETLESS_TURN_RECAP_LANGFUSE=off (its OWN flag,
|
|
302
|
+
# independent of the C-lite injection's MEETLESS_TURN_RECAP), so it never blocks
|
|
303
|
+
# Stop. Rides the same end-of-Stop tail as the kickoffs above.
|
|
304
|
+
# See notes/20260609-mla-per-turn-assist-recap-plan.md §4.4.
|
|
305
|
+
spawn_turn_recap_emit "$SESSION_ID" "$REPORT_TURN"
|
|
306
|
+
|
|
307
|
+
# Stop returns in <1s. Worker runs review async.
|
|
308
|
+
exit 0
|