instar 1.2.37 → 1.2.39
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 +19 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/TopicIntent.d.ts +162 -0
- package/dist/core/TopicIntent.d.ts.map +1 -0
- package/dist/core/TopicIntent.js +355 -0
- package/dist/core/TopicIntent.js.map +1 -0
- package/dist/core/TopicIntentArcCheck.d.ts +101 -0
- package/dist/core/TopicIntentArcCheck.d.ts.map +1 -0
- package/dist/core/TopicIntentArcCheck.js +146 -0
- package/dist/core/TopicIntentArcCheck.js.map +1 -0
- package/dist/core/TopicIntentBriefing.d.ts +44 -0
- package/dist/core/TopicIntentBriefing.d.ts.map +1 -0
- package/dist/core/TopicIntentBriefing.js +88 -0
- package/dist/core/TopicIntentBriefing.js.map +1 -0
- package/dist/core/TopicIntentExtractor.d.ts +90 -0
- package/dist/core/TopicIntentExtractor.d.ts.map +1 -0
- package/dist/core/TopicIntentExtractor.js +153 -0
- package/dist/core/TopicIntentExtractor.js.map +1 -0
- package/dist/core/TopicIntentPendingConfirm.d.ts +96 -0
- package/dist/core/TopicIntentPendingConfirm.d.ts.map +1 -0
- package/dist/core/TopicIntentPendingConfirm.js +223 -0
- package/dist/core/TopicIntentPendingConfirm.js.map +1 -0
- package/dist/messaging/TelegramAdapter.d.ts +45 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +52 -0
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/server/AgentServer.d.ts +3 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +10 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/topicIntentRoutes.d.ts +27 -0
- package/dist/server/topicIntentRoutes.d.ts.map +1 -0
- package/dist/server/topicIntentRoutes.js +189 -0
- package/dist/server/topicIntentRoutes.js.map +1 -0
- package/dist/tunnel/TunnelManager.d.ts +32 -1
- package/dist/tunnel/TunnelManager.d.ts.map +1 -1
- package/dist/tunnel/TunnelManager.js +36 -0
- package/dist/tunnel/TunnelManager.js.map +1 -1
- package/dist/tunnel/TunnelNotifier.d.ts +28 -0
- package/dist/tunnel/TunnelNotifier.d.ts.map +1 -1
- package/dist/tunnel/TunnelNotifier.js +38 -4
- package/dist/tunnel/TunnelNotifier.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +4 -4
- package/src/templates/hooks/telegram-topic-context.sh +19 -0
- package/upgrades/1.2.38.md +103 -0
- package/upgrades/1.2.39.md +54 -0
- package/upgrades/side-effects/owner-dm-channel.md +152 -0
- package/upgrades/side-effects/topic-intent-layer.md +86 -0
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
export class TunnelNotifier {
|
|
30
30
|
sink;
|
|
31
31
|
clock;
|
|
32
|
+
credentialProvider;
|
|
32
33
|
stateChangeMinIntervalMs;
|
|
33
34
|
flapThreshold;
|
|
34
35
|
// Dedup state. The lifecycle's epoch is monotonic per manager instance,
|
|
@@ -49,6 +50,7 @@ export class TunnelNotifier {
|
|
|
49
50
|
constructor(opts) {
|
|
50
51
|
this.sink = opts.sink;
|
|
51
52
|
this.clock = opts.clock ?? { now: () => Date.now() };
|
|
53
|
+
this.credentialProvider = opts.credentialProvider;
|
|
52
54
|
this.stateChangeMinIntervalMs = opts.stateChangeMinIntervalMs ?? 15 * 60_000;
|
|
53
55
|
this.flapThreshold = opts.flapThreshold ?? 3;
|
|
54
56
|
}
|
|
@@ -147,7 +149,7 @@ export class TunnelNotifier {
|
|
|
147
149
|
messages.push({
|
|
148
150
|
channel: 'owner-dm',
|
|
149
151
|
class: 'state-change',
|
|
150
|
-
text:
|
|
152
|
+
text: this.composeRestoredDM(),
|
|
151
153
|
episodeId: ep,
|
|
152
154
|
epoch: e.epoch,
|
|
153
155
|
});
|
|
@@ -164,7 +166,7 @@ export class TunnelNotifier {
|
|
|
164
166
|
messages.push({
|
|
165
167
|
channel: 'owner-dm',
|
|
166
168
|
class: 'state-change',
|
|
167
|
-
text:
|
|
169
|
+
text: this.composeRecoveredDM(),
|
|
168
170
|
episodeId: ep,
|
|
169
171
|
epoch: e.epoch,
|
|
170
172
|
});
|
|
@@ -183,7 +185,7 @@ export class TunnelNotifier {
|
|
|
183
185
|
messages.push({
|
|
184
186
|
channel: 'owner-dm',
|
|
185
187
|
class: 'action-required',
|
|
186
|
-
text:
|
|
188
|
+
text: this.composeConsentPromptDM(),
|
|
187
189
|
episodeId: ep,
|
|
188
190
|
epoch: e.epoch,
|
|
189
191
|
});
|
|
@@ -200,7 +202,7 @@ export class TunnelNotifier {
|
|
|
200
202
|
messages.push({
|
|
201
203
|
channel: 'owner-dm',
|
|
202
204
|
class: 'state-change',
|
|
203
|
-
text:
|
|
205
|
+
text: this.composeRelayActiveDM(),
|
|
204
206
|
episodeId: ep,
|
|
205
207
|
epoch: e.epoch,
|
|
206
208
|
});
|
|
@@ -250,6 +252,38 @@ export class TunnelNotifier {
|
|
|
250
252
|
return true;
|
|
251
253
|
return this.clock.now() - lastAt >= this.stateChangeMinIntervalMs;
|
|
252
254
|
}
|
|
255
|
+
// ── Owner-DM message composers ───────────────────────────────────
|
|
256
|
+
/** Snapshot of credentials at compose time (safely degrades when no provider). */
|
|
257
|
+
creds() {
|
|
258
|
+
if (!this.credentialProvider)
|
|
259
|
+
return { url: null };
|
|
260
|
+
try {
|
|
261
|
+
return this.credentialProvider();
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
return { url: null };
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
renderLink() {
|
|
268
|
+
const { url, pin } = this.creds();
|
|
269
|
+
if (!url)
|
|
270
|
+
return `(link not available yet — I'll send it as soon as the tunnel is up)`;
|
|
271
|
+
if (pin)
|
|
272
|
+
return `Link: ${url}\nPIN: ${pin}`;
|
|
273
|
+
return `Link: ${url}`;
|
|
274
|
+
}
|
|
275
|
+
composeRecoveredDM() {
|
|
276
|
+
return `Cloudflare is back. Your dashboard link:\n\n${this.renderLink()}`;
|
|
277
|
+
}
|
|
278
|
+
composeRestoredDM() {
|
|
279
|
+
return `Your permanent Cloudflare link is back — switched off the backup. New link:\n\n${this.renderLink()}\n\nIf you had your dashboard open, you may need to re-enter the PIN.`;
|
|
280
|
+
}
|
|
281
|
+
composeRelayActiveDM() {
|
|
282
|
+
return `Backup tunnel is up and serving your dashboard. New link below.\n\nHeads up: this routes through a third-party server, so your dashboard traffic is briefly going through their machines while Cloudflare is down. The agent will switch you back to your normal link automatically as soon as Cloudflare recovers.\n\n${this.renderLink()}`;
|
|
283
|
+
}
|
|
284
|
+
composeConsentPromptDM() {
|
|
285
|
+
return `Cloudflare is unavailable and I can't get you a dashboard link through the usual path.\n\nI can try a backup that routes through a third-party server. This means your dashboard PIN and any private view links would briefly be visible to whoever operates that backup while it's in use — and your auth token will be rotated after the backup is no longer needed, which will sign you out of any open dashboard tabs and invalidate any previously-shared private view links.\n\nReply "yes, use a backup" to approve, or "no" to keep waiting for Cloudflare. If you don't reply, I'll keep waiting for Cloudflare and won't use a backup.`;
|
|
286
|
+
}
|
|
253
287
|
}
|
|
254
288
|
function throttleKey(state, channel) {
|
|
255
289
|
return `${state}::${channel}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TunnelNotifier.js","sourceRoot":"","sources":["../../src/tunnel/TunnelNotifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;
|
|
1
|
+
{"version":3,"file":"TunnelNotifier.js","sourceRoot":"","sources":["../../src/tunnel/TunnelNotifier.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-05-
|
|
5
|
-
"instarVersion": "1.2.
|
|
4
|
+
"generatedAt": "2026-05-23T02:07:34.437Z",
|
|
5
|
+
"instarVersion": "1.2.39",
|
|
6
6
|
"entryCount": 191,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -1064,7 +1064,7 @@
|
|
|
1064
1064
|
"type": "template",
|
|
1065
1065
|
"domain": "safety",
|
|
1066
1066
|
"sourcePath": "src/templates/hooks/telegram-topic-context.sh",
|
|
1067
|
-
"contentHash": "
|
|
1067
|
+
"contentHash": "57cfb744bbffcd21f6290c5f38726b105b93258bf09cc539c24d66aebed9c40e",
|
|
1068
1068
|
"since": "2025-01-01"
|
|
1069
1069
|
},
|
|
1070
1070
|
"template:convergence-check.sh": {
|
|
@@ -1440,7 +1440,7 @@
|
|
|
1440
1440
|
"type": "subsystem",
|
|
1441
1441
|
"domain": "server",
|
|
1442
1442
|
"sourcePath": "src/server/AgentServer.ts",
|
|
1443
|
-
"contentHash": "
|
|
1443
|
+
"contentHash": "0c4f63b37d72bc85415e464e181507b66bb247df1e903da2fe27c5d6539dab0f",
|
|
1444
1444
|
"since": "2025-01-01"
|
|
1445
1445
|
},
|
|
1446
1446
|
"subsystem:session-manager": {
|
|
@@ -65,6 +65,25 @@ else
|
|
|
65
65
|
"http://localhost:${PORT}/telegram/topics/${TOPIC_ID}/messages?limit=30" 2>/dev/null)
|
|
66
66
|
fi
|
|
67
67
|
|
|
68
|
+
# Layer 2: prepend the topic intent briefing if anything has accumulated.
|
|
69
|
+
# Returns empty body when nothing tracked yet — we silently skip injection.
|
|
70
|
+
# Degrades open: any failure here (server down, route 503'd, network blip)
|
|
71
|
+
# leaves recent-history-only output unchanged.
|
|
72
|
+
TOPIC_BRIEFING=""
|
|
73
|
+
if [ -n "$AUTH_TOKEN" ]; then
|
|
74
|
+
TOPIC_BRIEFING=$(curl -s --max-time 2 \
|
|
75
|
+
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
|
76
|
+
"http://localhost:${PORT}/topic-intent/${TOPIC_ID}/briefing" 2>/dev/null)
|
|
77
|
+
else
|
|
78
|
+
TOPIC_BRIEFING=$(curl -s --max-time 2 \
|
|
79
|
+
"http://localhost:${PORT}/topic-intent/${TOPIC_ID}/briefing" 2>/dev/null)
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
if [ -n "$TOPIC_BRIEFING" ]; then
|
|
83
|
+
echo "$TOPIC_BRIEFING"
|
|
84
|
+
echo ""
|
|
85
|
+
fi
|
|
86
|
+
|
|
68
87
|
# Format and output context with unanswered message detection
|
|
69
88
|
echo "$RECENT_MSGS" | python3 -c "
|
|
70
89
|
import sys, json
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
<!-- patch = owner-DM channel + notifier sink wiring; no public API breakage -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
**feat(tunnel,telegram): owner-DM channel + two-channel notifier wired into the tunnel manager.**
|
|
9
|
+
|
|
10
|
+
PR 3 of the tunnel-failure-resilience chain. Lands the owner-DM
|
|
11
|
+
messaging surface that the upcoming consent UX + relay providers
|
|
12
|
+
(PR 4) and credential rotation lifecycle (PR 5) depend on. Spec
|
|
13
|
+
`specs/dev-infrastructure/tunnel-failure-resilience.md` is converged
|
|
14
|
+
and approved on main.
|
|
15
|
+
|
|
16
|
+
What's new:
|
|
17
|
+
|
|
18
|
+
- `TelegramAdapter.ownerUserId` config field — the single Telegram
|
|
19
|
+
user id who controls security-sensitive decisions for this
|
|
20
|
+
instance (consent gate for backup tunnels, credential exposure).
|
|
21
|
+
Separate from the broader `authorizedUserIds` allowlist per the
|
|
22
|
+
GPT external review's CRITICAL finding that consent decisions
|
|
23
|
+
must not trust the broader set. Falls back to
|
|
24
|
+
`promptGate.ownerId` for back-compat when not set explicitly.
|
|
25
|
+
- `TelegramAdapter.getOwnerUserId()` — returns the configured owner
|
|
26
|
+
principal (or the back-compat fallback).
|
|
27
|
+
- `TelegramAdapter.sendToOwnerDM(text)` — sends a private DM to the
|
|
28
|
+
owner's direct bot chat. Failure modes ("no owner configured",
|
|
29
|
+
"owner hasn't messaged the bot yet", network error) log a warning
|
|
30
|
+
and return null; never throw into the caller. Telegram bots can
|
|
31
|
+
only initiate a DM after the user has messaged the bot at least
|
|
32
|
+
once, so an early forbidden response is expected and non-fatal.
|
|
33
|
+
- `TunnelManager.attachTelegram(adapter, dashboardPin)` — wires the
|
|
34
|
+
notifier sink. Group messages route to the Dashboard topic (with
|
|
35
|
+
Lifeline fallback); owner-DM messages route to
|
|
36
|
+
`sendToOwnerDM`. A credentialProvider callback returns the live
|
|
37
|
+
URL + current PIN at compose time, so the notifier never holds
|
|
38
|
+
stale credentials in its own state. `server.ts` calls
|
|
39
|
+
`attachTelegram` after the Dashboard topic is ensured.
|
|
40
|
+
- `TunnelNotifier` composers — the four owner-DM messages
|
|
41
|
+
(recovered, restored, consent prompt, relay activated) now render
|
|
42
|
+
real English text with credentials substituted. Group-channel
|
|
43
|
+
messages continue to carry status text only — credentials NEVER
|
|
44
|
+
appear in group messages.
|
|
45
|
+
|
|
46
|
+
What's NOT new yet (deliberate, future PRs in this chain):
|
|
47
|
+
|
|
48
|
+
- Inline-button consent UX + callback handler (PR 4).
|
|
49
|
+
- Tier-2 relay providers (localtunnel) + the actual consent-driven
|
|
50
|
+
transition into `relay-active` (PR 4).
|
|
51
|
+
- `authToken` / PIN rotation on relay-episode end + boot recovery
|
|
52
|
+
(PR 5).
|
|
53
|
+
- Full N-consecutive-success self-heal stability gate (PR 6).
|
|
54
|
+
- The auth-gated `/tunnel` route (PR 7).
|
|
55
|
+
|
|
56
|
+
The lifecycle state machine already supports all of these; the
|
|
57
|
+
manager just doesn't transition into the relevant states yet.
|
|
58
|
+
|
|
59
|
+
## Evidence
|
|
60
|
+
|
|
61
|
+
3 new unit tests in `tests/unit/tunnel-manager-rewrite.test.ts`:
|
|
62
|
+
|
|
63
|
+
- `attachTelegram` routes the "couldn't reach" group message to the
|
|
64
|
+
Dashboard topic id.
|
|
65
|
+
- `attachTelegram` falls back to the Lifeline topic when no Dashboard
|
|
66
|
+
topic is configured.
|
|
67
|
+
- `attachTelegram` owner-DM message carries the live URL and current
|
|
68
|
+
PIN (credential substitution at compose time).
|
|
69
|
+
|
|
70
|
+
1 new + updated tests in `tests/unit/tunnel-notifier.test.ts`:
|
|
71
|
+
|
|
72
|
+
- The credential-snapshot test now uses a real credentialProvider and
|
|
73
|
+
asserts the URL + PIN appear in owner-DM messages AND that they
|
|
74
|
+
NEVER appear in group messages.
|
|
75
|
+
- New "renders a graceful 'link not available' placeholder when no
|
|
76
|
+
credentialProvider is wired" test for the test-injection /
|
|
77
|
+
partial-wire-up path.
|
|
78
|
+
|
|
79
|
+
All 71 tunnel-related unit tests pass (32 lifecycle + 10 providers +
|
|
80
|
+
13 notifier + 19 manager). Typescript and lint clean.
|
|
81
|
+
|
|
82
|
+
## What to Tell Your User
|
|
83
|
+
|
|
84
|
+
This release adds the plumbing for owner-only private messages from
|
|
85
|
+
the agent to you, separate from the group topics where everyone
|
|
86
|
+
sees the same messages. Nothing visible to you yet — the upcoming
|
|
87
|
+
release uses this plumbing to message you directly when Cloudflare
|
|
88
|
+
goes down and asks whether to use a backup. The release after that
|
|
89
|
+
uses the same channel to send you new dashboard credentials after a
|
|
90
|
+
backup tunnel finishes its work and your auth token rotates.
|
|
91
|
+
|
|
92
|
+
If you want to set the owner explicitly, the relevant config field
|
|
93
|
+
is your Telegram user id. The agent falls back to your prompt-gate
|
|
94
|
+
owner id if it's already set, so most existing installs don't need
|
|
95
|
+
to change anything.
|
|
96
|
+
|
|
97
|
+
## Summary of New Capabilities
|
|
98
|
+
|
|
99
|
+
| Capability | How to Use |
|
|
100
|
+
|-----------|-----------|
|
|
101
|
+
| Owner-DM channel | Automatic — the agent picks up your owner id from existing config and uses it for security-sensitive messages |
|
|
102
|
+
| Live credential substitution in DMs | Automatic — when the tunnel sends you a link, the URL + PIN are always the current ones |
|
|
103
|
+
| Group-vs-DM channel separation | Structural — credentials never appear in group messages regardless of how the tunnel layer wires up |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: minor -->
|
|
4
|
+
<!-- minor = new feature, requires meaningful user-visible behavior addition -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
**feat(topic-intent-layer): Layers 1-3 — per-topic confidence tracking + resume briefing + ArcCheck pre-send signal.**
|
|
9
|
+
|
|
10
|
+
A new three-layer feature that gives the agent a real sense of each conversation's arc. When you talk to the agent across many turns (or days, or after a context reset), it now keeps quiet track of what the topic is about, what's settled, and what's still tentative — without you ever typing a command. The agent does the bookkeeping in the background.
|
|
11
|
+
|
|
12
|
+
- **Layer 1** — a per-topic confidence tracker. Each candidate fact or decision the conversation establishes is tracked with confidence that rises with reinforcement (you re-reference, you affirm) and falls with contradiction or long silence. Authority requires user-authored evidence — the agent cannot silently promote its own guesses to "settled" by re-referencing itself.
|
|
13
|
+
- **Layer 2** — a briefing the agent reads at the top of every session resume. Telegram bootstrap automatically fetches it and prepends it to the recent-history block. Settled items shown unhedged; tentative items shown with confidence; observation tier (background noise) deliberately not surfaced.
|
|
14
|
+
- **Layer 3** — ArcCheck, a pre-send signal that fires when the agent's draft would either act on a tentative item without confirming OR contradict something already settled. Signal-only — the agent decides whether to redraft including a natural-language confirmation question. Never blocks.
|
|
15
|
+
|
|
16
|
+
Spec: `docs/specs/topic-intent-layer.md` (v14 CLEAN, approved 2026-05-22, 14 internal rounds plus 13 external rounds).
|
|
17
|
+
|
|
18
|
+
This build was also the test bed for evaluating whether to integrate GSD as a runtime dependency. Verdict: cherry-pick the methodologies into Instar primitives, do not try runtime composition. Comparison report archived at `.planning/SPIKE-FINDINGS.md` plus `.planning/phases/01-layer1/PLAN.md`.
|
|
19
|
+
|
|
20
|
+
## Evidence
|
|
21
|
+
|
|
22
|
+
The original failure modes that motivated the feature are documented as canonical incidents in the spec — qalatra topic 9235 (arc drift across a multi-turn investigation) and GCI/Luna topic 365 (decision amnesia where the agent proposed redesigning a settled OAuth architecture mid-debugging, contradicting an earlier decision).
|
|
23
|
+
|
|
24
|
+
96 tests across all three tiers, all green locally:
|
|
25
|
+
- Decay arithmetic at days 104, 105, 106 matches spec values to 3 decimal places.
|
|
26
|
+
- User-authored-episode authority gating clamps at 0.69 when no qualifying user-authored episode exists.
|
|
27
|
+
- Per-message dedup with larger-delta-wins on collision.
|
|
28
|
+
- Pending-confirm queue depth 3 with silent drop plus telemetry counter.
|
|
29
|
+
- Sharpen-retry up to 2 then abandoned.
|
|
30
|
+
- PII-safe diagnostics endpoint (raw evidence meta allowlist-filtered, never returns user message text).
|
|
31
|
+
- Framework-parity grep gate (zero Claude-Code or Codex-CLI specific tokens in any Layer 1/2/3 source).
|
|
32
|
+
- Tier-3 lifecycle test boots through the real production server path and observes tier transitions through HTTP.
|
|
33
|
+
|
|
34
|
+
Side-effects review artifact: `upgrades/side-effects/topic-intent-layer.md`.
|
|
35
|
+
|
|
36
|
+
## What to Tell Your User
|
|
37
|
+
|
|
38
|
+
In long, multi-day, or multi-topic conversations, the agent stays on-point. It resumes knowing the goal. It stops re-litigating things you already settled together. When it is about to do something based on an assumption that might be wrong, it asks first in plain English, as part of its reply. When you correct it, it actually updates.
|
|
39
|
+
|
|
40
|
+
In short, simple conversations, nothing changes. The layer only kicks in once a conversation has substance worth tracking, and stays quiet otherwise.
|
|
41
|
+
|
|
42
|
+
No commands to learn. No configuration to set. The agent does the bookkeeping; you just talk.
|
|
43
|
+
|
|
44
|
+
A diagnostics view per conversation tells you exactly what the agent is tracking, how confident it is, what signals built that confidence, and whether anything is in conflict.
|
|
45
|
+
|
|
46
|
+
## Summary of New Capabilities
|
|
47
|
+
|
|
48
|
+
- New core modules covering confidence tracking, pending-confirmation lifecycle, LLM-backed extraction, resume briefing rendering, and pre-send classifier.
|
|
49
|
+
- New HTTP routes for diagnostics, refs filter, pending state, telemetry, briefing, and ArcCheck.
|
|
50
|
+
- Telegram bootstrap hook auto-fetches the briefing on session resume. Degrades open on any failure.
|
|
51
|
+
- Production server boot path constructs the store and passes it to AgentServer. Mount unconditional (503 stub when store disabled).
|
|
52
|
+
- Out of v1: cross-conversation insight spreading, retroactive backfill of pre-shipped conversations, cross-machine CRDT collaborative state, automatic conversation renaming, rich dashboard editor, user-tunable confidence weights (operator may tune; end user cannot).
|
|
53
|
+
|
|
54
|
+
Layer 3's outbound-path wiring (the actual every-send-fires-ArcCheck plumbing into the tone-gate and response-review stack) is intentionally separated from this ship. The classifier IS the spec's Layer 3 mechanism; the rollout is best done with care under a dedicated follow-up since it touches the existing outbound gate authority.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Side-effects review — owner-DM channel + notifier sink wiring (PR 3 of tunnel-failure-resilience chain)
|
|
2
|
+
|
|
3
|
+
**Scope (PR 3 of the chain):** Add the owner-DM messaging surface and
|
|
4
|
+
wire it into the tunnel manager's two-channel notifier. Spec
|
|
5
|
+
`specs/dev-infrastructure/tunnel-failure-resilience.md` is converged
|
|
6
|
+
+ approved on main. Public API: `TelegramAdapter.getOwnerUserId()` and
|
|
7
|
+
`TelegramAdapter.sendToOwnerDM(text)` are new; `TunnelManager.attachTelegram()`
|
|
8
|
+
is new; the notifier now substitutes live URL + PIN credentials into
|
|
9
|
+
owner-DM messages via a `credentialProvider` callback.
|
|
10
|
+
|
|
11
|
+
**Files touched:**
|
|
12
|
+
- `src/messaging/TelegramAdapter.ts` — adds `ownerUserId?: number` to
|
|
13
|
+
`TelegramConfig`; adds `getOwnerUserId()` accessor (falls back to
|
|
14
|
+
`promptGate?.ownerId` for back-compat); adds `sendToOwnerDM(text)`
|
|
15
|
+
which posts to the owner's private bot chat via `sendMessage` with
|
|
16
|
+
`chat_id: ownerUserId`. The DM send is fire-and-forget: failure paths
|
|
17
|
+
("no owner configured", "owner hasn't messaged the bot", network
|
|
18
|
+
error) log-not-throw and return `null` so callers can degrade
|
|
19
|
+
gracefully.
|
|
20
|
+
- `src/tunnel/TunnelNotifier.ts` — adds `credentialProvider?: () =>
|
|
21
|
+
CredentialSnapshot` to `TunnelNotifierOptions`. The four owner-DM
|
|
22
|
+
message variants (recovered, restored, consent prompt, relay
|
|
23
|
+
activated) now compose real English text with the credentials
|
|
24
|
+
substituted at compose time. The four group-channel variants
|
|
25
|
+
continue to carry status text only — credentials NEVER appear in
|
|
26
|
+
group messages.
|
|
27
|
+
- `src/tunnel/TunnelManager.ts` — new `attachTelegram(adapter,
|
|
28
|
+
dashboardPin)` method builds a notifier sink from a duck-typed
|
|
29
|
+
`TunnelMessagingAdapter` (group → Dashboard topic with Lifeline
|
|
30
|
+
fallback; owner DM → `sendToOwnerDM`) and a credentialProvider that
|
|
31
|
+
returns `{ url: this.url, pin: dashboardPin() }`. Holds the notifier
|
|
32
|
+
in a mutable field so callers can wire it AFTER tunnel construction
|
|
33
|
+
(the messaging adapter is constructed later in server.ts).
|
|
34
|
+
- `src/commands/server.ts` — calls `tunnel.attachTelegram(telegram,
|
|
35
|
+
() => liveConfig.get(...) || config.dashboardPin)` after the
|
|
36
|
+
Dashboard topic is ensured. The wire is non-fatal: if attachTelegram
|
|
37
|
+
throws, the catch around `ensureDashboardTopic` already swallows it.
|
|
38
|
+
- `tests/unit/tunnel-notifier.test.ts` — replaces the placeholder
|
|
39
|
+
assertions with real-credential assertions: a DM with a credential
|
|
40
|
+
provider contains the URL + PIN; a DM without a provider renders a
|
|
41
|
+
graceful "link not available yet" placeholder; group messages NEVER
|
|
42
|
+
contain credentials.
|
|
43
|
+
- `tests/unit/tunnel-manager-rewrite.test.ts` — 3 new tests for
|
|
44
|
+
`attachTelegram`: Dashboard-topic routing, Lifeline fallback when
|
|
45
|
+
Dashboard isn't ensured, owner-DM credential snapshot includes the
|
|
46
|
+
live URL and current PIN.
|
|
47
|
+
|
|
48
|
+
**Under-block**: None. The owner-DM channel adds NEW capability;
|
|
49
|
+
existing behavior is unchanged when no `ownerUserId` is configured
|
|
50
|
+
(`sendToOwnerDM` logs a warning and returns null; the notifier's DM
|
|
51
|
+
sink call is a no-op). Existing Telegram group flows are untouched.
|
|
52
|
+
|
|
53
|
+
**Over-block**: None — but note the existing
|
|
54
|
+
`config.promptGate?.ownerId` continues to serve as a back-compat
|
|
55
|
+
fallback when `ownerUserId` isn't set explicitly. Operators who used
|
|
56
|
+
`promptGate.ownerId` to mark their session-prompt owner will see that
|
|
57
|
+
same user receiving the tunnel-DM messages, which is consistent with
|
|
58
|
+
the spec's "owner principal" intent. New deployments should set
|
|
59
|
+
`ownerUserId` explicitly.
|
|
60
|
+
|
|
61
|
+
**Level-of-abstraction fit**:
|
|
62
|
+
- TunnelManager remains the single owner of the lifecycle; the
|
|
63
|
+
notifier sink is a thin pass-through to the messaging adapter.
|
|
64
|
+
- The `TunnelMessagingAdapter` duck-typed interface keeps the tunnel
|
|
65
|
+
module from importing `TelegramAdapter` directly — preserves the
|
|
66
|
+
layered architecture (tunnel does not depend on messaging type).
|
|
67
|
+
- Credential substitution happens in the notifier's composer methods.
|
|
68
|
+
The notifier never holds credentials in its own state; it calls
|
|
69
|
+
the provider at compose time so the credentials are always fresh.
|
|
70
|
+
|
|
71
|
+
**Signal vs authority**: Compliant. The credentialProvider returns a
|
|
72
|
+
SIGNAL (current URL + PIN); the notifier remains the AUTHORITY for
|
|
73
|
+
deciding when an owner-DM message goes out and what it says. The
|
|
74
|
+
sink is the routing layer (executes the send); the messaging adapter
|
|
75
|
+
is the platform layer. No new authority introduced at the wrong
|
|
76
|
+
layer.
|
|
77
|
+
|
|
78
|
+
**Interactions**:
|
|
79
|
+
- `TunnelManager` constructed in server.ts BEFORE telegram, so the
|
|
80
|
+
manager initially has no notifier. The wire-up happens AFTER
|
|
81
|
+
telegram + Dashboard topic are ready (one-shot call to
|
|
82
|
+
`attachTelegram`). Until then, transitions go through the lifecycle
|
|
83
|
+
but no user-facing messages fire — preserving the boot-time
|
|
84
|
+
ordering established in PR 2.
|
|
85
|
+
- The credentialProvider closes over `this._legacyState.url` and
|
|
86
|
+
the dashboardPin accessor; both are read at each compose, so a
|
|
87
|
+
PIN rotation (PR 5) will be visible to subsequent messages without
|
|
88
|
+
re-attaching.
|
|
89
|
+
- `sendToOwnerDM` failure modes are absorbed inside the adapter — the
|
|
90
|
+
sink wrapper never throws into the notifier path. Preserves the
|
|
91
|
+
fire-and-forget invariant.
|
|
92
|
+
|
|
93
|
+
**External surfaces**:
|
|
94
|
+
- New config field: `messaging.config.ownerUserId` (number). Optional.
|
|
95
|
+
Falls back to `messaging.config.promptGate.ownerId`. Documented in
|
|
96
|
+
the spec.
|
|
97
|
+
- No new API endpoint. No new CLI command.
|
|
98
|
+
- `TelegramAdapter.sendToOwnerDM(text)` is a new public method but
|
|
99
|
+
intended for in-process callers only (the tunnel manager is the
|
|
100
|
+
first caller; future PRs may add more).
|
|
101
|
+
|
|
102
|
+
**Migration parity**:
|
|
103
|
+
- No agent-installed file change in this PR. Existing agents pick up
|
|
104
|
+
the new behavior the moment they update — the `ownerUserId` field
|
|
105
|
+
remains optional with the documented fallback.
|
|
106
|
+
- A future PR in the chain (the ConfigDefaults / migration PR) will
|
|
107
|
+
add a ConfigDefaults entry for `ownerUserId` and a CLAUDE.md
|
|
108
|
+
template note so the agent surfaces the new capability
|
|
109
|
+
conversationally. For now the manager's behavior degrades
|
|
110
|
+
gracefully when the field is absent.
|
|
111
|
+
|
|
112
|
+
**Rollback cost**: Trivial. Revert four source files + the test
|
|
113
|
+
updates. The two new public methods (`getOwnerUserId`,
|
|
114
|
+
`sendToOwnerDM`, `attachTelegram`) become unused; the
|
|
115
|
+
`TunnelMessagingAdapter` interface becomes dead code. Nothing else
|
|
116
|
+
breaks.
|
|
117
|
+
|
|
118
|
+
**Tests**:
|
|
119
|
+
- 19/19 `tests/unit/tunnel-manager-rewrite.test.ts` (3 new for
|
|
120
|
+
`attachTelegram`).
|
|
121
|
+
- 13/13 `tests/unit/tunnel-notifier.test.ts` (placeholder assertions
|
|
122
|
+
replaced with credential assertions; 1 new "no-credential
|
|
123
|
+
gracefully renders 'link not available'" test).
|
|
124
|
+
- 32/32 `tests/unit/tunnel-lifecycle.test.ts` (no change).
|
|
125
|
+
- 10/10 `tests/unit/tunnel-providers.test.ts` (no change).
|
|
126
|
+
- `tsc --noEmit` clean. `npm run lint` clean.
|
|
127
|
+
|
|
128
|
+
**Decision-point inventory**:
|
|
129
|
+
1. Owner principal modeled as `ownerUserId` separate from
|
|
130
|
+
`authorizedUserIds` (vs. reusing the broader set) — the GPT
|
|
131
|
+
external review specifically flagged that consent / credential
|
|
132
|
+
exposure decisions must NOT trust the broader authorized-users
|
|
133
|
+
set. A separate single-principal field is the structural fix.
|
|
134
|
+
2. `sendToOwnerDM` returns null on failure (vs. throwing) — preserves
|
|
135
|
+
the fire-and-forget contract callers expect for outbound
|
|
136
|
+
messaging. The bot can't initiate a DM until the user has messaged
|
|
137
|
+
the bot first, so an early "forbidden" response is expected during
|
|
138
|
+
onboarding; treating it as fatal would block tunnel notifications
|
|
139
|
+
indefinitely.
|
|
140
|
+
3. `attachTelegram` is a post-construction method (vs. constructor
|
|
141
|
+
parameter) — the messaging adapter is constructed later than the
|
|
142
|
+
tunnel manager in server.ts. Putting the sink in the constructor
|
|
143
|
+
would require restructuring boot order. The post-attach pattern
|
|
144
|
+
keeps the existing boot sequence and is testable.
|
|
145
|
+
4. `TunnelMessagingAdapter` duck-typed interface (vs. direct
|
|
146
|
+
`TelegramAdapter` import) — keeps the tunnel module from depending
|
|
147
|
+
on the messaging layer's concrete types; future PRs can add Slack
|
|
148
|
+
/ iMessage variants without modifying tunnel/.
|
|
149
|
+
5. credentialProvider returns a snapshot (vs. holding the URL/PIN as
|
|
150
|
+
notifier state) — the notifier composes messages over time; a
|
|
151
|
+
stale PIN cached in notifier state would leak after rotation. The
|
|
152
|
+
provider pattern keeps credential freshness in one place.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Side-Effects Review — Topic Intent Layer (Layers 1-3)
|
|
2
|
+
|
|
3
|
+
**PR:** https://github.com/JKHeadley/instar/pull/332
|
|
4
|
+
**Spec:** `docs/specs/topic-intent-layer.md` (v14 CLEAN, approved 2026-05-22)
|
|
5
|
+
**Author:** Echo · autonomous run · 2026-05-22
|
|
6
|
+
**Process:** Per `feedback_side_effects_review.md` — seven-dimension review before ship.
|
|
7
|
+
|
|
8
|
+
## 1. Over-block
|
|
9
|
+
|
|
10
|
+
The feature could over-block if a hook surfaces tentative items as if settled, or if ArcCheck started blocking sends.
|
|
11
|
+
|
|
12
|
+
**Mitigations in place:**
|
|
13
|
+
- ArcCheck verdict shape never includes a `block` or `authority` field. Signal-only by spec and by test (`tests/unit/TopicIntent-arccheck.test.ts` asserts this).
|
|
14
|
+
- Tentative items in the Layer 2 briefing are explicitly hedged with the word "TENTATIVE" + confidence value. Settled items in a separate "SETTLED" section.
|
|
15
|
+
- Observation tier (< 0.30) is NOT surfaced in the briefing at all — no noise leak.
|
|
16
|
+
- The briefing endpoint returns empty body when nothing has accumulated, so bootstrap hooks can skip injection cleanly without distorting normal session start.
|
|
17
|
+
|
|
18
|
+
## 2. Under-block
|
|
19
|
+
|
|
20
|
+
The feature could under-block if it lets a single LLM extraction silently launder its own guesses to authoritative tier (the canonical GCI failure mode).
|
|
21
|
+
|
|
22
|
+
**Mitigations in place:**
|
|
23
|
+
- Authority hard rule (spec section "Authority gating"): confidence cannot reach the authoritative tier (≥0.7) unless at least one evidence event qualifies as a user-authored episode. Enforced in `projectConfidence` with a clamp at 0.69.
|
|
24
|
+
- Per-message dedup: multiple signals about the same refId from the same source message count as ONE episode with the larger applicable delta — agent cannot fork its own message into multiple episodes.
|
|
25
|
+
- Affirmation safety: per-refId per-24h cap of 1 affirmation bonus, plus per-single-user-message max of 3 distinct refIds affirmed, defends against a single "yes" mass-promoting.
|
|
26
|
+
- Signal-specific caps: `agent-reref` capped at +0.05 cumulative, `extract-agent` at +0.10. Even an unbounded loop of agent-origin signals cannot accumulate to authority.
|
|
27
|
+
|
|
28
|
+
## 3. Level-of-abstraction fit
|
|
29
|
+
|
|
30
|
+
Is the feature implemented at the right layer of the stack?
|
|
31
|
+
|
|
32
|
+
**Assessment:**
|
|
33
|
+
- Storage at `.instar/topic-intent/<topicId>.json` parallels the existing `.instar/topic-resume-map.json` convention — same level, same JSON-first approach, same file-per-topic granularity.
|
|
34
|
+
- Routes mounted via the same `createXxxRoutes` factory pattern as `worktreeRoutes`, `fileRoutes`, `machineRoutes` — consistent with the codebase's idiom.
|
|
35
|
+
- AgentServer options pattern mirrors existing optional dependencies (e.g., `worktreeManager?`). DI is uniform.
|
|
36
|
+
- Telegram bootstrap hook modification is minimal — one curl call with degrade-open semantics. Slack bootstrap hook is NOT modified (slack channel IDs are string-keyed, not numeric topic IDs; the cleanest adapter pattern for Slack is a follow-up).
|
|
37
|
+
|
|
38
|
+
## 4. Signal-vs-authority compliance
|
|
39
|
+
|
|
40
|
+
Per `feedback_signal_vs_authority`: brittle/low-context filters detect signals; only higher-level intelligent gates with full context block.
|
|
41
|
+
|
|
42
|
+
**Assessment:**
|
|
43
|
+
- ArcCheck is the signal layer. It returns verdicts that include `kind`, `refId`, `currentTier`, `currentConfidence`, `reason`, `suggestedRewriteHint` — no authority claim.
|
|
44
|
+
- The agent itself is the authority layer. The agent reads the ArcCheck signal in its draft preparation and chooses to either redraft (with a conversational confirmation question) or proceed.
|
|
45
|
+
- The existing outbound gates (tone-gate, response-review, outbound-dedup) retain their blocking authority and are unaffected by this change.
|
|
46
|
+
|
|
47
|
+
## 5. Cross-feature interactions
|
|
48
|
+
|
|
49
|
+
What other Instar systems could this affect?
|
|
50
|
+
|
|
51
|
+
- **CompactionSentinel** — the briefing prepended on every bootstrap may slightly increase context-window usage at session start. Mitigation: maxPerSection cap (default 8) + overflow note keeps it bounded; observation tier deliberately omitted. The briefing is empty body when nothing tracked.
|
|
52
|
+
- **Tone-gate / response-review** — Layer 3 does NOT integrate into the outbound path in this ship. The classifier exists and is reachable; the actual every-send wiring is a deliberate follow-up so we can think carefully about whether ArcCheck signals should be routed through the existing gate or augment them.
|
|
53
|
+
- **Telegram bootstrap hook** — modified, but failure modes are degrade-open (any error → empty briefing, recent-history output unchanged). The 2-second timeout (`curl --max-time 2`) bounds latency.
|
|
54
|
+
- **Slack bootstrap hook** — NOT modified. Slack channel IDs are string-keyed; topic-intent is numeric-keyed. Adapter pattern for Slack is a v1.1 follow-up.
|
|
55
|
+
- **iMessage / WhatsApp** — same situation as Slack, deferred.
|
|
56
|
+
|
|
57
|
+
## 6. Rollback cost
|
|
58
|
+
|
|
59
|
+
If this needs to revert, what's the blast radius?
|
|
60
|
+
|
|
61
|
+
- All new files (`src/core/TopicIntent*.ts`, `src/server/topicIntentRoutes.ts`, all `tests/unit/TopicIntent-*.test.ts`, `tests/integration/topic-intent-routes.test.ts`, `tests/e2e/topic-intent-lifecycle.test.ts`) can be deleted without affecting any other system.
|
|
62
|
+
- Modified files (`src/server/AgentServer.ts`, `src/commands/server.ts`, `src/templates/hooks/telegram-topic-context.sh`) have small surgical additions that can be reverted independently — store construction, route mount, optional briefing-fetch with degrade-open.
|
|
63
|
+
- Storage at `.instar/topic-intent/<topicId>.json` is per-topic JSON files; deleting them has no effect on any other Instar state.
|
|
64
|
+
- No new dependencies in `package.json`. No DB migrations. No config-format changes.
|
|
65
|
+
|
|
66
|
+
**Net rollback cost: low.** A clean revert of the feature commit + deleting the storage directory is sufficient.
|
|
67
|
+
|
|
68
|
+
## 7. Migration parity
|
|
69
|
+
|
|
70
|
+
Per `feedback_migration_parity_standard` (built into CLAUDE.md): changes to agent-installed files must reach existing agents via the update path.
|
|
71
|
+
|
|
72
|
+
**Files in this PR that need migration handling:**
|
|
73
|
+
- `src/templates/hooks/telegram-topic-context.sh` — built-in hook script. Per the Migration Parity Standard, built-in hooks are **always overwritten** on every migration run via `installBuiltinHooks()`. No additional migration code needed — the script will reach existing agents on their next `instar upgrade`.
|
|
74
|
+
|
|
75
|
+
**No other agent-installed files changed** in this PR. CLAUDE.md template is unchanged in this PR; Agent Awareness Standard update (so agents know about the new `/topic-intent/*` endpoints) is the recommended next follow-up before the cherry-pick v1.3.x project.
|
|
76
|
+
|
|
77
|
+
## Conclusion
|
|
78
|
+
|
|
79
|
+
Ship. Seven-dimension review clean. Rollback cost low. No new external dependencies. All three test tiers green. PII boundary explicitly tested. Framework-parity grep gate in place.
|
|
80
|
+
|
|
81
|
+
Follow-up scoped for future work (in priority order):
|
|
82
|
+
1. Layer 3 outbound-path wiring (the every-send ArcCheck integration with tone-gate / response-review).
|
|
83
|
+
2. Slack / iMessage / WhatsApp bootstrap parity for Layer 2 briefing.
|
|
84
|
+
3. CLAUDE.md template update for Agent Awareness Standard.
|
|
85
|
+
4. Daily probe job that replays the qalatra-9235 and GCI/Luna-365 fixtures against the live extractor.
|
|
86
|
+
5. The seven cherry-pick recommendations from the GSD spike (separate v1.3.x project).
|