instar 1.3.163 → 1.3.165
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 +38 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/core/SessionRouter.d.ts +10 -0
- package/dist/core/SessionRouter.d.ts.map +1 -1
- package/dist/core/SessionRouter.js +4 -0
- package/dist/core/SessionRouter.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts +13 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +27 -4
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.164.md +47 -0
- package/upgrades/1.3.165.md +47 -0
- package/upgrades/side-effects/router-confirm-claim-on-place.md +96 -0
- package/upgrades/side-effects/standby-telegram-outbound-relay.md +100 -0
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-31T11:
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-05-31T11:39:31.391Z",
|
|
5
|
+
"instarVersion": "1.3.165",
|
|
6
6
|
"entryCount": 193,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What to Tell Your User
|
|
7
|
+
|
|
8
|
+
If you run your agent on more than one machine and move a conversation between them, the
|
|
9
|
+
move now actually sticks — the receiving machine is confirmed as the new owner and your
|
|
10
|
+
following messages route to it, instead of the move quietly failing. This only applies
|
|
11
|
+
when the multi-machine session pool is on; single-machine agents are unaffected. Nothing
|
|
12
|
+
to configure. (Two related pieces are still in progress: ownership surviving a restart,
|
|
13
|
+
and the moved conversation being able to reply — tracked separately.)
|
|
14
|
+
|
|
15
|
+
## Summary of New Capabilities
|
|
16
|
+
|
|
17
|
+
- `SessionRouter` gains an optional `confirmClaim(sessionKey, machineId)` dep, invoked
|
|
18
|
+
after a successful remote `spawnOnMachine`, advancing the ownership record
|
|
19
|
+
`placing → active`. Self-placement (router keeps it) does not confirm.
|
|
20
|
+
- Wired in the server to the ownership-registry `claim` CAS.
|
|
21
|
+
|
|
22
|
+
## Evidence
|
|
23
|
+
|
|
24
|
+
- Found live on 2026-05-31: after a move, every later message logged
|
|
25
|
+
"route … action=queued owner=?" and was handled back on the origin — the ownership
|
|
26
|
+
state machine requires place→claim→active and the claim was never issued, so the
|
|
27
|
+
record stayed "placing".
|
|
28
|
+
- Unit, `tests/unit/SessionRouter.test.ts`: a remote placement now confirms the owner
|
|
29
|
+
(placing→active); a self-placement does not.
|
|
30
|
+
- Unit, `tests/unit/SessionOwnership.test.ts`: the place→claim→active transition.
|
|
31
|
+
- 47 SessionRouter + ownership + wiring tests pass; tsc --noEmit clean.
|
|
32
|
+
- Spec, `docs/specs/router-confirm-claim-on-place.md` plus the .eli16.md sibling.
|
|
33
|
+
- Side-effects, `upgrades/side-effects/router-confirm-claim-on-place.md`.
|
|
34
|
+
|
|
35
|
+
## What Changed — a moved conversation now finishes its handoff instead of getting stuck
|
|
36
|
+
|
|
37
|
+
Another step toward the multi-machine "move this conversation to my other machine"
|
|
38
|
+
feature. The handoff tracks ownership in two steps: the router assigns the conversation
|
|
39
|
+
to the target machine (status "placing"), then the new owner is confirmed (status
|
|
40
|
+
"active"). The confirm step was never being issued, so a moved conversation stayed in
|
|
41
|
+
"placing" limbo forever — and the router's rule of "hold messages while a handoff is in
|
|
42
|
+
progress" then held every later message, so the move silently failed.
|
|
43
|
+
|
|
44
|
+
This release has the router issue the confirm right after it hands the conversation to
|
|
45
|
+
the target machine, advancing ownership to "active". From then on the router correctly
|
|
46
|
+
forwards your messages to the new owner. The state machine only allows confirming the
|
|
47
|
+
exact machine the conversation was just assigned to, so this stays safe.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What to Tell Your User
|
|
7
|
+
|
|
8
|
+
If you run your agent across more than one machine and move a conversation to another
|
|
9
|
+
one, that conversation can now reply to you — its replies are relayed through the
|
|
10
|
+
machine that owns your messaging bot, so you hear back without any risk of the two
|
|
11
|
+
machines clashing over the bot. Only relevant when the multi-machine session pool is
|
|
12
|
+
on; single-machine agents are unaffected. Nothing to configure.
|
|
13
|
+
|
|
14
|
+
## Summary of New Capabilities
|
|
15
|
+
|
|
16
|
+
- `TelegramAdapter` gains an `outboundRelay` hook. When the adapter has no bot token,
|
|
17
|
+
`sendToTopic` routes the send through it (the server wires it to POST the
|
|
18
|
+
Telegram-owning lease holder's `/telegram/reply`), instead of failing silently. A
|
|
19
|
+
token-holding adapter sends directly as before; a relay that can't deliver throws
|
|
20
|
+
rather than dropping the message.
|
|
21
|
+
|
|
22
|
+
## Evidence
|
|
23
|
+
|
|
24
|
+
- Found live: the backup machine's Telegram adapter had no bot token, so a moved
|
|
25
|
+
session's reply was sent with no token and silently vanished.
|
|
26
|
+
- Unit, `tests/unit/TelegramAdapter.test.ts`: a tokenless adapter routes the send
|
|
27
|
+
through the relay (the Telegram API is not hit, the relayed id is returned); a
|
|
28
|
+
token-holding adapter sends directly and never consults the relay; a relay that
|
|
29
|
+
returns nothing throws instead of silently dropping the reply.
|
|
30
|
+
- 19 TelegramAdapter tests pass; tsc --noEmit clean.
|
|
31
|
+
- Spec, `docs/specs/standby-telegram-outbound-relay.md` plus the .eli16.md sibling.
|
|
32
|
+
- Side-effects, `upgrades/side-effects/standby-telegram-outbound-relay.md`.
|
|
33
|
+
|
|
34
|
+
## What Changed — a conversation moved to your other machine can now reply to you
|
|
35
|
+
|
|
36
|
+
The final piece of the multi-machine "move this conversation to my other machine"
|
|
37
|
+
feature. Telegram only allows one program to use a given bot at a time, so in a
|
|
38
|
+
two-machine setup only the main machine holds the bot, and the backup deliberately
|
|
39
|
+
holds none (to avoid two programs fighting over the same bot — a real failure we fixed
|
|
40
|
+
earlier). That meant a conversation moved to the backup machine could run there but
|
|
41
|
+
couldn't actually send a reply — it came out silent.
|
|
42
|
+
|
|
43
|
+
This release adds a relay: when the backup machine wants to reply but has no bot, it
|
|
44
|
+
hands the message to the machine that does hold the bot, which sends it normally. So
|
|
45
|
+
your reply comes through, and there's still only ever one program using the bot — no
|
|
46
|
+
fighting, no duplicate messages. A machine that holds its own bot is completely
|
|
47
|
+
unaffected.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Side-Effects Review — Router confirms remote placement (bug #11)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `router-confirm-claim-on-place`
|
|
4
|
+
**Date:** `2026-05-31`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `not required`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
`SessionRouter` gains an optional `confirmClaim(sessionKey, machineId)` dep, called in
|
|
11
|
+
`placeAndClaim` right after a successful `spawnOnMachine` on a REMOTE target. `server.ts`
|
|
12
|
+
wires it to `ownReg.cas({ type: 'claim', machineId }, …)`, advancing the ownership
|
|
13
|
+
record `placing → active`. Without it the record stayed `placing` and every later
|
|
14
|
+
message for the session queued (bug #11).
|
|
15
|
+
|
|
16
|
+
## Decision-point inventory
|
|
17
|
+
|
|
18
|
+
- **remote branch only** — `confirmClaim` is called only in `placeAndClaim`'s
|
|
19
|
+
remote-spawn path (after `spawnOnMachine`). Self-placement returns handled-locally
|
|
20
|
+
and never confirms. Both covered by tests.
|
|
21
|
+
- **FSM gate** — `claim` is permitted only when the record's `ownerMachineId` equals the
|
|
22
|
+
claimed `machineId` (the placed owner). Covered in `SessionOwnership.test.ts`.
|
|
23
|
+
|
|
24
|
+
## 1. Over-block
|
|
25
|
+
|
|
26
|
+
**What legitimate inputs does this reject?** Nothing. It only ADDS a confirm after a
|
|
27
|
+
remote placement. Self-placement, forwarding to an existing owner, owner-dead
|
|
28
|
+
re-placement, and queue paths are unchanged. A `confirmClaim` that fails (e.g. CAS
|
|
29
|
+
contention) is best-effort and leaves the placement to recover via the normal fences.
|
|
30
|
+
|
|
31
|
+
## 2. Under-block
|
|
32
|
+
|
|
33
|
+
**What does this still miss?** It does not make ownership durable across restarts (the
|
|
34
|
+
store is per-machine in-memory — the shared Track-H store is separate). It confirms
|
|
35
|
+
optimistically once the spawn is DISPATCHED (the deliverMessage/spawn RPC resolved); if
|
|
36
|
+
the target's session later dies, the existing owner-dead detection re-places it. It does
|
|
37
|
+
not address bug #7 (standby outbound mute).
|
|
38
|
+
|
|
39
|
+
## 3. Level-of-abstraction fit
|
|
40
|
+
|
|
41
|
+
**Right layer?** Yes. The confirm lives in `SessionRouter.placeAndClaim` — the exact
|
|
42
|
+
place that owns the place→spawn lifecycle and already does the `place` CAS. The dep is
|
|
43
|
+
injected (testable, same pattern as `casClaimOwnership`/`spawnOnMachine`). The FSM
|
|
44
|
+
transition itself stays in `SessionOwnership.ts`.
|
|
45
|
+
|
|
46
|
+
## 4. Signal vs authority compliance
|
|
47
|
+
|
|
48
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
49
|
+
|
|
50
|
+
This advances an ownership record through its designed FSM (`place → claim`), gated by
|
|
51
|
+
the FSM's own legal-transition check (claim machineId must equal the placed owner). It
|
|
52
|
+
adds no user-facing authority and blocks nothing. It RELAXES nothing about who may own a
|
|
53
|
+
session — it completes a transition that was already supposed to happen.
|
|
54
|
+
|
|
55
|
+
## 5. Interactions
|
|
56
|
+
|
|
57
|
+
Consumes the existing `ownReg` CAS (the same registry `place`/`transfer`/`release` use).
|
|
58
|
+
Pairs with `placeAndClaim`'s `place` CAS (the two halves of a new-session handoff). After
|
|
59
|
+
the confirm, `dispatchOne` sees `active` (not `placing`) and forwards subsequent messages
|
|
60
|
+
to the owner instead of queueing. Idempotent at the FSM level (a duplicate claim on an
|
|
61
|
+
already-active record is rejected `claim-out-of-sequence`, harmless). No interaction with
|
|
62
|
+
the lease or the shared-state read-only guard.
|
|
63
|
+
|
|
64
|
+
## 6. External surfaces
|
|
65
|
+
|
|
66
|
+
None. No HTTP routes, config, or notifications. The visible effect is a moved session's
|
|
67
|
+
ownership reaching `active` (so `/pool` + routing reflect the new owner).
|
|
68
|
+
|
|
69
|
+
## 7. Rollback cost
|
|
70
|
+
|
|
71
|
+
Low. Remove the `confirmClaim` dep + call + wiring; transfers revert to stuck-`placing`
|
|
72
|
+
(bug #11). No schema, no persisted state, no migration (the dep is optional + in-memory).
|
|
73
|
+
|
|
74
|
+
## Conclusion
|
|
75
|
+
|
|
76
|
+
Minimal, FSM-gated completion of a handoff transition that was missing, scoped to the
|
|
77
|
+
remote-placement branch, both branches + the FSM transition unit-tested, no new
|
|
78
|
+
authority, no external surface, cheap revert. Makes the live single-router transfer's
|
|
79
|
+
ownership finalize instead of sticking. Durable cross-machine ownership (Track-H) and
|
|
80
|
+
the reply relay (#7) remain separate.
|
|
81
|
+
|
|
82
|
+
## Second-pass review (if required)
|
|
83
|
+
|
|
84
|
+
Not required — completes an existing FSM transition via its own legality gate, remote-
|
|
85
|
+
branch-only, both sides tested, no shared-state authority changed, reversible. The live
|
|
86
|
+
two-machine re-test (fresh topic) is the Tier-3 gate that follows.
|
|
87
|
+
|
|
88
|
+
## Evidence pointers
|
|
89
|
+
|
|
90
|
+
- `tests/unit/SessionRouter.test.ts` — remote placement calls `confirmClaim('s1','m_remote')`;
|
|
91
|
+
self-placement does not.
|
|
92
|
+
- `tests/unit/SessionOwnership.test.ts` — `place(placing) → claim(active)` transition.
|
|
93
|
+
- 47 SessionRouter + ownership + wiring tests green; `tsc --noEmit` clean.
|
|
94
|
+
- Found live: `route … action=queued owner=?` on every post-move message; the ownership
|
|
95
|
+
FSM requires place→claim→active and the claim was never issued.
|
|
96
|
+
- Spec: `docs/specs/router-confirm-claim-on-place.md` (+ `.eli16.md`).
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Side-Effects Review — Tokenless standby Telegram outbound relay (bug #7)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `standby-telegram-outbound-relay`
|
|
4
|
+
**Date:** `2026-05-31`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `not required`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
`TelegramAdapter` gains a settable `outboundRelay` callback. In `sendToTopic`, when
|
|
11
|
+
the adapter has NO bot token AND a relay is wired, the send routes through the relay
|
|
12
|
+
instead of the Telegram API (the relayed id feeds the same log/stall/promise
|
|
13
|
+
bookkeeping); a relay returning null throws. `server.ts` wires the relay to POST the
|
|
14
|
+
lease holder's `/telegram/reply/:topicId` (Bearer authToken + the holder's peer URL),
|
|
15
|
+
refusing to relay to self/unknown. Closes bug #7 (a session moved to a tokenless
|
|
16
|
+
standby was mute).
|
|
17
|
+
|
|
18
|
+
## Decision-point inventory
|
|
19
|
+
|
|
20
|
+
- **sendToTopic branch** — `!this.config.token && this.outboundRelay` → relay; else →
|
|
21
|
+
the unchanged API path. Both covered by tests.
|
|
22
|
+
- **relay closure** — `holder` is self or unknown, or no URL → return null (no
|
|
23
|
+
relay); else POST and return the id (or null on non-ok / throw). Self-relay guard
|
|
24
|
+
+ null-on-failure tested via the adapter-level null case.
|
|
25
|
+
|
|
26
|
+
## 1. Over-block
|
|
27
|
+
|
|
28
|
+
**What legitimate inputs does this reject?** Nothing for a token-holding machine — its
|
|
29
|
+
send path is byte-identical (the relay branch is skipped). For a tokenless standby, a
|
|
30
|
+
send that previously failed silently (no token) now either relays successfully or
|
|
31
|
+
throws a clear error (better than the silent mute). No legitimate send is dropped.
|
|
32
|
+
|
|
33
|
+
## 2. Under-block
|
|
34
|
+
|
|
35
|
+
**What does this still miss?** It does not sync conversation CONTEXT to the moved
|
|
36
|
+
session (audit #2 — the reply may lack prior history). It relays to the current lease
|
|
37
|
+
holder only (correct for the single-Telegram-owner model); if the holder is
|
|
38
|
+
unreachable it returns null → `sendToTopic` throws (surfaced, not silent). It does not
|
|
39
|
+
retry/queue a failed relay (the caller/session sees the throw); a durable relay queue
|
|
40
|
+
is possible later but out of scope.
|
|
41
|
+
|
|
42
|
+
## 3. Level-of-abstraction fit
|
|
43
|
+
|
|
44
|
+
**Right layer?** Yes. The send decision lives in `TelegramAdapter.sendToTopic` (the
|
|
45
|
+
one outbound chokepoint), expressed as an injected callback (same pattern as the
|
|
46
|
+
adapter's other `on*` hooks). The cross-machine resolution (holder → URL → POST) lives
|
|
47
|
+
in `server.ts` where the mesh/coordinator/authToken are already in scope. The relay
|
|
48
|
+
reuses the existing `/telegram/reply` route rather than a new mesh command.
|
|
49
|
+
|
|
50
|
+
## 4. Signal vs authority compliance
|
|
51
|
+
|
|
52
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
53
|
+
|
|
54
|
+
No new blocking authority. It changes HOW a tokenless adapter sends (relay vs direct),
|
|
55
|
+
not WHETHER. It preserves the single-Telegram-owner safety invariant (the standby
|
|
56
|
+
never sends on the bot directly), which is the 409-conflict guard — strengthening, not
|
|
57
|
+
weakening, a safety property.
|
|
58
|
+
|
|
59
|
+
## 5. Interactions
|
|
60
|
+
|
|
61
|
+
The relay POSTs the holder's `/telegram/reply` — the same route used for normal
|
|
62
|
+
replies — so the message goes out the one bot the holder owns; no second poller/sender
|
|
63
|
+
(no 409). Pairs with the lease coordinator (`getSyncStatus().leaseHolder` picks the
|
|
64
|
+
target) and peer presence (`peerUrl`). On a token-holding router the callback is wired
|
|
65
|
+
but never invoked. Idempotent per call; a failed relay throws (the session's reply
|
|
66
|
+
attempt errors rather than silently vanishing).
|
|
67
|
+
|
|
68
|
+
## 6. External surfaces
|
|
69
|
+
|
|
70
|
+
One cross-machine HTTP POST from a tokenless standby to the holder's existing
|
|
71
|
+
`/telegram/reply` (Bearer-authed). No new route, config, or notification. The visible
|
|
72
|
+
effect: a moved session's replies reach the user (via the holder's bot).
|
|
73
|
+
|
|
74
|
+
## 7. Rollback cost
|
|
75
|
+
|
|
76
|
+
Low. Remove the `outboundRelay` callback + the `sendToTopic` branch + the wiring; a
|
|
77
|
+
tokenless standby's sends revert to silent failure (bug #7). No schema, no state, no
|
|
78
|
+
migration (the callback is in-memory, default null).
|
|
79
|
+
|
|
80
|
+
## Conclusion
|
|
81
|
+
|
|
82
|
+
Minimal, injected-callback change scoped to the tokenless-send path, reusing the
|
|
83
|
+
existing reply route, preserving the single-owner 409-guard, both branches + the
|
|
84
|
+
null-failure case unit-tested, no change for token-holding machines, cheap revert.
|
|
85
|
+
The completion gate for the live transfer — a moved session can now reply.
|
|
86
|
+
|
|
87
|
+
## Second-pass review (if required)
|
|
88
|
+
|
|
89
|
+
Not required — scoped to the no-token send path, reuses a battle-tested route,
|
|
90
|
+
strengthens (not weakens) the single-owner invariant, both branches + failure tested,
|
|
91
|
+
reversible. The live two-machine re-test (a reply landing) is the Tier-3 gate.
|
|
92
|
+
|
|
93
|
+
## Evidence pointers
|
|
94
|
+
|
|
95
|
+
- `tests/unit/TelegramAdapter.test.ts` — tokenless adapter relays (API not hit, relayed
|
|
96
|
+
id returned); token-holding adapter sends directly (relay not consulted); relay→null
|
|
97
|
+
throws (no silent mute).
|
|
98
|
+
- 19 TelegramAdapter tests green; `tsc --noEmit` clean.
|
|
99
|
+
- Found live: mini telegram adapter `botToken=MISSING` → a moved session's reply muted.
|
|
100
|
+
- Spec: `docs/specs/standby-telegram-outbound-relay.md` (+ `.eli16.md`).
|