instar 1.3.752 → 1.3.753
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/server.d.ts.map +1 -1
- package/dist/commands/server.js +76 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +18 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/MoveIntentClassifier.d.ts +135 -0
- package/dist/core/MoveIntentClassifier.d.ts.map +1 -0
- package/dist/core/MoveIntentClassifier.js +284 -0
- package/dist/core/MoveIntentClassifier.js.map +1 -0
- package/dist/core/NicknameCommand.d.ts +23 -17
- package/dist/core/NicknameCommand.d.ts.map +1 -1
- package/dist/core/NicknameCommand.js +20 -65
- package/dist/core/NicknameCommand.js.map +1 -1
- package/dist/core/componentCategories.d.ts.map +1 -1
- package/dist/core/componentCategories.js +5 -0
- package/dist/core/componentCategories.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +6 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/machineCoherenceManifest.d.ts.map +1 -1
- package/dist/core/machineCoherenceManifest.js +1 -0
- package/dist/core/machineCoherenceManifest.js.map +1 -1
- package/dist/data/llmBenchCoverage.d.ts.map +1 -1
- package/dist/data/llmBenchCoverage.js +6 -0
- package/dist/data/llmBenchCoverage.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/src/data/llmBenchCoverage.ts +7 -0
- package/upgrades/1.3.753.md +44 -0
- package/upgrades/side-effects/nickname-move-intent-llm-rebuild.md +160 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Side-Effects Review — Move-Intent Recognizer: Keyword List → LLM-With-Context
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `nickname-move-intent-llm-rebuild`
|
|
4
|
+
**Date:** `2026-07-04`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `spec-converge round (codex-cli:gpt-5.5 + gemini-cli:gemini-2.5-pro + 4 internal reviewers)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Replaces the keyword verb-list decision in `NicknameCommand.recognizeNicknameCommand`
|
|
11
|
+
(`TRANSFER_VERBS = [move,…,run,continue,resume,keep]`) — which hijacked the operator's discussion message
|
|
12
|
+
"keep the work on the laptop" on 2026-07-03 — with an LLM classifier (`src/core/MoveIntentClassifier.ts`)
|
|
13
|
+
that infers "is this a move/pin command to <known machine>?" over the message + a bounded window of recent
|
|
14
|
+
conversation, guardrailed by code-side enum validation of the model-emitted `targetNickname`. Fail-OPEN on
|
|
15
|
+
any uncertainty (never hijack). Wired at `server.ts` `_tryNicknameRelocation` behind a dev-agent dark gate +
|
|
16
|
+
dry-run. The keyword decision is removed from `NicknameCommand.ts` (only the `NicknameCommand` type, consumed
|
|
17
|
+
by the unchanged `TransferByNickname` planner, remains). Files: `src/core/MoveIntentClassifier.ts` (new),
|
|
18
|
+
`src/core/NicknameCommand.ts`, `src/commands/server.ts`, `src/config/ConfigDefaults.ts`,
|
|
19
|
+
`src/core/devGatedFeatures.ts`, `src/core/componentCategories.ts`, `src/core/machineCoherenceManifest.ts`,
|
|
20
|
+
`docs/LLM-ROUTING-REGISTRY.md`, three test files + the wiring/line-map test updates.
|
|
21
|
+
|
|
22
|
+
## Decision-point inventory
|
|
23
|
+
|
|
24
|
+
- `_tryNicknameRelocation` (server.ts) — **modify** — the "is this a move command?" decision changes from
|
|
25
|
+
keyword-match to LLM classifier; the downstream `planTransferByNickname` actuator is pass-through (unchanged).
|
|
26
|
+
- `NicknameCommand.recognizeNicknameCommand` — **remove** — the keyword verb-list decision is deleted.
|
|
27
|
+
- `MoveIntentClassifier.classifyRelocationIntent` — **add** — the new LLM decision + code-side enum guardrail.
|
|
28
|
+
- `multiMachine.sessionPool.moveIntent` config — **add** — dev-gated dark, dry-run-first knobs.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. Over-block
|
|
33
|
+
|
|
34
|
+
The "block" surface here = hijacking a message (treating it as a move command, swallowing it). Over-block =
|
|
35
|
+
falsely hijacking discussion. This change's entire purpose is to REDUCE over-block: the classifier fails
|
|
36
|
+
open (only a high-confidence command with a resolved enum target hijacks), and ships dry-run so it hijacks
|
|
37
|
+
NOTHING during the soak. The residual over-block risk is a confidently-wrong model returning
|
|
38
|
+
`isCommand:true, confidence≥0.85` on discussion — bounded by the graduation-gate live benchmark (must pass
|
|
39
|
+
before `dryRun:false`) and the conservative threshold. Strictly less over-block than the keyword list, which
|
|
40
|
+
hijacked on any verb×preposition×nickname co-occurrence.
|
|
41
|
+
|
|
42
|
+
## 2. Under-block
|
|
43
|
+
|
|
44
|
+
Under-block = missing a genuine move command (it passes through to the agent). By design this is the SAFE
|
|
45
|
+
direction (fail-open). Known misses: (a) a purely deictic target with no nickname anywhere in the window
|
|
46
|
+
("send it to my desktop") — the prefilter skips → pass-through; (b) any fail-open path (provider down,
|
|
47
|
+
timeout, low confidence, malformed output). All are cheap (the user re-phrases); none cause harm. The old
|
|
48
|
+
keyword list ALSO under-blocked ("let's have the mini take this one" was missed) — this reduces it via
|
|
49
|
+
context-aware inference.
|
|
50
|
+
|
|
51
|
+
## 3. Level-of-abstraction fit
|
|
52
|
+
|
|
53
|
+
Correct layer. It is a smart AUTHORITY (LLM reasoning over message + conversation context), not a brittle
|
|
54
|
+
detector — exactly what the "Intelligence Infers, Keywords Only Guard" standard requires for a
|
|
55
|
+
what-did-they-mean judgment. The cheap deterministic prefilter is a genuine *pre-filter* (drops toward
|
|
56
|
+
pass-through only, never decides a command), matching `TopicIntentCapture`. It USES existing primitives
|
|
57
|
+
(shared `IntelligenceProvider`, `RelocationNicknameSet`, `TransferByNickname`) rather than re-implementing
|
|
58
|
+
them; it FEEDS the unchanged planner rather than running parallel to it.
|
|
59
|
+
|
|
60
|
+
## 4. Signal vs authority compliance
|
|
61
|
+
|
|
62
|
+
**Required reference:** docs/signal-vs-authority.md
|
|
63
|
+
|
|
64
|
+
- [x] Yes — but the logic is a smart gate with full conversational context (LLM-backed with recent history).
|
|
65
|
+
|
|
66
|
+
The decision is an LLM reasoning over the message + a bounded conversation window — a smart gate, not a
|
|
67
|
+
brittle detector holding block authority. It replaces exactly the brittle-detector-with-authority
|
|
68
|
+
anti-pattern the standard forbids. It fails open, so even under provider failure it never asserts authority
|
|
69
|
+
it can't back.
|
|
70
|
+
|
|
71
|
+
## 5. Interactions
|
|
72
|
+
|
|
73
|
+
- **Shadowing:** `_tryNicknameRelocation` runs BEFORE `_sessionRouter.route()` (unchanged ordering) and only
|
|
74
|
+
when `_sessionPoolStage() !== 'dark'`. When it returns `handled:false` (the common case, and ALWAYS during
|
|
75
|
+
dry-run), routing proceeds normally — it cannot shadow the router. Verified by `transfer-activation-wiring.test.ts`.
|
|
76
|
+
- **Double-fire:** no. A message is classified once per inbound; a positive hijack returns early (`return`),
|
|
77
|
+
a pass-through continues to the router exactly once.
|
|
78
|
+
- **Races:** none new. The classifier is stateless; the only write is an append to `logs/move-intent.jsonl`
|
|
79
|
+
(best-effort, wrapped, never throws into the message path).
|
|
80
|
+
- **Feedback loops:** none. The classifier reads recent history but never writes conversation turns.
|
|
81
|
+
|
|
82
|
+
## 6. External surfaces
|
|
83
|
+
|
|
84
|
+
- **Other agents on the machine:** none — internal message-path classifier.
|
|
85
|
+
- **Install base:** dark on the fleet (dev-agent gate + the whole relocation path is already gated behind
|
|
86
|
+
`sessionPool != dark`, which ships dark). No fleet behavior change.
|
|
87
|
+
- **External systems:** one bounded fast-tier LLM call per nickname-mentioning candidate message, via the
|
|
88
|
+
shared `IntelligenceProvider` (spawn-cap + circuit-breaker + attribution). No new network dependency.
|
|
89
|
+
- **Persistent state:** `logs/move-intent.jsonl` — a new machine-local append-only observability log
|
|
90
|
+
(LLM-engaged decisions only; 80-char truncated preview; local, never synced). Consistent with all other
|
|
91
|
+
`logs/*.jsonl`.
|
|
92
|
+
- **Timing:** `timeoutMs` default 4000 bounds the worst-case pass-through delay for a candidate message.
|
|
93
|
+
- **Operator surface (Mobile-Complete):** No operator-facing actions added. The only operator interaction is
|
|
94
|
+
flipping `dryRun:false` / `modelTier` in config (already a standard config edit), plus reading the
|
|
95
|
+
dashboard **LLM Activity** tab (`/metrics/features`, already phone-accessible). N/A.
|
|
96
|
+
|
|
97
|
+
## 6b. Operator-surface quality
|
|
98
|
+
|
|
99
|
+
No operator surface — not applicable. This change touches no dashboard renderer, approval page, or
|
|
100
|
+
grant/revoke/secret-drop form (no `dashboard/*` or form files staged).
|
|
101
|
+
|
|
102
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
103
|
+
|
|
104
|
+
**Posture: machine-local BY DESIGN** — the classifier is stateless request-locality COMPUTE. It runs on the
|
|
105
|
+
machine that holds the topic (the lifeline forwards inbound to the holder), is a pure function of the
|
|
106
|
+
message + the live nickname registry + the provider, and has no durable cross-machine state to replicate.
|
|
107
|
+
The only durable artifact is `logs/move-intent.jsonl`, a machine-local observability log identical in
|
|
108
|
+
posture to `logs/sentinel-events.jsonl` / `logs/reaper-audit.jsonl` (pure per-machine observability).
|
|
109
|
+
|
|
110
|
+
- **User-facing notices / one-voice gating?** No — during dry-run it emits nothing to the user; when live it
|
|
111
|
+
produces the same single "Moving…" reply the existing keyword path did (unchanged), routed by the existing
|
|
112
|
+
single-owner path.
|
|
113
|
+
- **Durable state / strands on topic transfer?** The soak log is per-machine; a topic that transfers mid-soak
|
|
114
|
+
splits its would-hijack/would-pass evidence across machines. This is graduation-decision EVIDENCE, not user
|
|
115
|
+
data — at `dryRun:false` graduation the operator aggregates the per-machine logs (or scopes the soak to a
|
|
116
|
+
non-moving topic). No user data strands.
|
|
117
|
+
- **Generated URLs?** None.
|
|
118
|
+
|
|
119
|
+
Multi-dev note: the dev-agent gate resolves per-machine, so a multi-dev setup should activate
|
|
120
|
+
pool-consistently (documented in the spec Rollout §4) to avoid divergent behavior across topic-holding machines.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Migration parity
|
|
125
|
+
|
|
126
|
+
Config default `multiMachine.sessionPool.moveIntent` is added under the existing `sessionPool` block in
|
|
127
|
+
`ConfigDefaults.ts`; `applyDefaults`/`getMigrationDefaults` deep-merges it add-missing onto existing agents
|
|
128
|
+
(the documented sessionPool migration-parity path — verified: the whole config/migration test family, 1191
|
|
129
|
+
tests, stays green). `enabled` is OMITTED so `resolveDevAgentGate` decides (dark fleet / live dev);
|
|
130
|
+
registered in `DEV_GATED_FEATURES` (both-sides wiring test green). No hook/CLAUDE.md-template/skill changes
|
|
131
|
+
(the move-by-nickname capability is dev-only experimental behind the dark session pool, so no
|
|
132
|
+
Agent-Awareness template surface is added).
|
|
133
|
+
|
|
134
|
+
## Rollback
|
|
135
|
+
|
|
136
|
+
Pure revert. No schema/data migration to unwind. To disable without reverting: set
|
|
137
|
+
`multiMachine.sessionPool.moveIntent.enabled: false` in config (force-dark even on a dev agent), or leave the
|
|
138
|
+
session pool at `stage: dark` (the default) — either fully inerts the path. `logs/move-intent.jsonl` is
|
|
139
|
+
append-only observability and safe to delete.
|
|
140
|
+
|
|
141
|
+
## Risk
|
|
142
|
+
|
|
143
|
+
Low. Ships dark on the fleet AND dry-run-first on dev (logs would-hijack/would-pass, actuates nothing). The
|
|
144
|
+
change strictly REDUCES risk versus the shipped keyword recognizer (fail-open replaces false-positive
|
|
145
|
+
hijack). The residual risk (a confidently-wrong model once `dryRun:false`) is gated by the graduation-gate
|
|
146
|
+
live benchmark + user-role Live-Channel proof + the soak, all required before actuation. tsc clean; lint
|
|
147
|
+
clean; all three test tiers green including the discrimination corpus.
|
|
148
|
+
|
|
149
|
+
## Post-rebase addendum (2026-07-04)
|
|
150
|
+
|
|
151
|
+
Rebased onto current `main` (clean). The new LLM component `MoveIntentClassifier` was registered across the
|
|
152
|
+
LLM-coverage ratchets that landed on main (#1366 and the bench family): `src/data/llmBenchCoverage.ts` —
|
|
153
|
+
`LLM_UNTRUSTED_INPUT: true` (it reads untrusted user message + context), `LLM_JUDGES_CLAIMS: false` (it
|
|
154
|
+
classifies a USER's move intent, not an agent/session completion/health/credit claim), `LLM_PARSER_CONTRACT:
|
|
155
|
+
{ pending: 'contract-wave-2' }` (it parses a closed verdict), and `LLM_BENCH_COVERAGE: { exempt }` (it ships
|
|
156
|
+
its own discrimination benchmark). The keyword-intent-decision ratchet's baseline was decremented 6→5 and
|
|
157
|
+
`NicknameCommand.ts` removed from its `EXPECTED_OFFENDERS` (offender #2 converted — exactly what that ratchet
|
|
158
|
+
tracks). The `parseMoveIntentResponse` fail-open catch carries an `@silent-fallback-ok` marker (the
|
|
159
|
+
documented fail-open, surfaced via `result.source`/`reason` + the audit log, never a silent swallow). No
|
|
160
|
+
behavior change from these — they are the coverage/observability registrations a new LLM component requires.
|