instar 1.1.4 → 1.2.2
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/setup.d.ts +14 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +65 -6
- package/dist/commands/setup.js.map +1 -1
- package/dist/server/SecretDrop.d.ts +56 -3
- package/dist/server/SecretDrop.d.ts.map +1 -1
- package/dist/server/SecretDrop.js +116 -6
- package/dist/server/SecretDrop.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +46 -5
- package/dist/server/routes.js.map +1 -1
- package/package.json +2 -1
- package/scripts/lib/telegram-historian.mjs +224 -0
- package/scripts/lib/topic-memory-importer.mjs +132 -0
- package/scripts/telegram-history-backfill.mjs +353 -0
- package/src/data/builtin-manifest.json +46 -46
- package/upgrades/1.1.4.md +131 -30
- package/upgrades/1.2.1.md +71 -0
- package/upgrades/1.2.2.md +62 -0
- package/upgrades/side-effects/feat-bareword-framework-prompt.md +66 -0
- package/upgrades/side-effects/secret-drop-hardening.md +64 -0
- package/upgrades/side-effects/telegram-history-backfill.md +131 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Upgrade Guide — v1.2.1 (bareword runtime prompt)
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
<!-- patch = bug fixes, refactors, test additions, doc updates -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
**Bareword `npx instar` asks which runtime.**
|
|
9
|
+
|
|
10
|
+
A fresh user typing `npx instar` (no subcommand) is now asked which AI
|
|
11
|
+
runtime they want — Claude Code or Codex CLI — before the wizard
|
|
12
|
+
launches. Previously the bareword silently defaulted to Claude Code and
|
|
13
|
+
exited if Claude wasn't installed, leaving Codex-only users with no
|
|
14
|
+
discoverable path from the headline command.
|
|
15
|
+
|
|
16
|
+
The prompt fires only when stdin is a TTY (interactive terminal), so
|
|
17
|
+
piped invocations and CI runs are unaffected — they keep defaulting to
|
|
18
|
+
Claude Code as before.
|
|
19
|
+
|
|
20
|
+
When exactly one runtime is installed, the prompt skips itself and uses
|
|
21
|
+
the installed one. When both are installed (or neither is), the prompt
|
|
22
|
+
asks; the existing `checkFrameworkPrerequisite` then surfaces a clear
|
|
23
|
+
install URL if the user picked one that isn't on the machine.
|
|
24
|
+
|
|
25
|
+
The explicit subcommand forms continue to work exactly as in v1.1.0:
|
|
26
|
+
`instar init --framework codex-cli --standalone` and
|
|
27
|
+
`instar setup --framework codex-cli` both bypass the prompt and use the
|
|
28
|
+
flag value directly.
|
|
29
|
+
|
|
30
|
+
Spec: `specs/dev-infrastructure/bareword-framework-prompt.md`. ELI16:
|
|
31
|
+
`specs/dev-infrastructure/bareword-framework-prompt.eli16.md`.
|
|
32
|
+
Side-effects review:
|
|
33
|
+
`upgrades/side-effects/feat-bareword-framework-prompt.md`.
|
|
34
|
+
|
|
35
|
+
## What to Tell Your User
|
|
36
|
+
|
|
37
|
+
Typing the plain instar command now asks which AI runtime you want —
|
|
38
|
+
Claude Code or Codex CLI. Pick whichever you have installed. The
|
|
39
|
+
explicit subcommand forms continue to work exactly as before, so any
|
|
40
|
+
scripts you have written keep working without changes.
|
|
41
|
+
|
|
42
|
+
## Summary of New Capabilities
|
|
43
|
+
|
|
44
|
+
Closes the final UX gap in the install/wizard portability arc shipped
|
|
45
|
+
in v1.1.0-v1.1.4. The headline command now reaches Codex-only users
|
|
46
|
+
the same way it reaches Claude-only users.
|
|
47
|
+
|
|
48
|
+
## Evidence
|
|
49
|
+
|
|
50
|
+
Reproduction prior: on a Codex-only host, running `npx instar` exits
|
|
51
|
+
with the "Claude Code is required" message because the bareword defaults
|
|
52
|
+
to Claude Code without asking. After: on a TTY, the bareword presents a
|
|
53
|
+
"Which AI runtime should this agent use?" prompt with the two options
|
|
54
|
+
and an installed/not-installed indicator. Answering "2" routes through
|
|
55
|
+
the Codex setup path; answering "1" or pressing enter routes through
|
|
56
|
+
the Claude path. In a piped or CI environment, the prompt is skipped
|
|
57
|
+
entirely and the default remains Claude Code.
|
|
58
|
+
|
|
59
|
+
Unit verification: `tests/unit/bareword-framework-prompt.test.ts` pins
|
|
60
|
+
the four cases of the pure decision function — both installed prompts,
|
|
61
|
+
only-Claude returns Claude, only-Codex returns Codex, neither installed
|
|
62
|
+
prompts so the user gets to pick which one to install.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Side-effects review — Bareword framework prompt
|
|
2
|
+
|
|
3
|
+
Per L6. Seven dimensions.
|
|
4
|
+
|
|
5
|
+
## 1. Over-block / under-block
|
|
6
|
+
|
|
7
|
+
Before: UNDER — bareword `npx instar` had no path to Codex-only setup
|
|
8
|
+
even with the underlying machinery in place. After: no over-block.
|
|
9
|
+
Subcommand explicit-flag path unchanged; non-TTY default unchanged. Only
|
|
10
|
+
the bareword on an interactive terminal gains the prompt.
|
|
11
|
+
|
|
12
|
+
## 2. Level-of-abstraction fit
|
|
13
|
+
|
|
14
|
+
Pure decision in `resolveFrameworkPromptBehavior` (exported, testable);
|
|
15
|
+
side-effectful readline interaction in `promptForFramework` (private to
|
|
16
|
+
setup.ts, only called when the decision says "prompt"). The wizard flow
|
|
17
|
+
itself is untouched; the resolved framework value flows into the same
|
|
18
|
+
existing `checkFrameworkPrerequisite` call.
|
|
19
|
+
|
|
20
|
+
## 3. Signal vs Authority compliance
|
|
21
|
+
|
|
22
|
+
The `--framework` flag (when present) and the prompt answer (when
|
|
23
|
+
prompted) are SIGNALS of operator intent. The existing
|
|
24
|
+
`checkFrameworkPrerequisite` is the single AUTHORITY for "is the chosen
|
|
25
|
+
runtime's binary present." The prompt's auto-skip-when-only-one-installed
|
|
26
|
+
behavior uses detection results from `detectClaudePath` /
|
|
27
|
+
`detectCodexPath`, which are the existing single source of truth.
|
|
28
|
+
|
|
29
|
+
## 4. Interactions with adjacent systems
|
|
30
|
+
|
|
31
|
+
- **Explicit `--framework` flag on subcommands** (PRs 3+4 + #276) —
|
|
32
|
+
unchanged. When the flag is set, the prompt is skipped entirely (the
|
|
33
|
+
decision is `opts.framework ?? prompt`).
|
|
34
|
+
- **Non-interactive `runNonInteractiveSetup`** — unchanged. That entry
|
|
35
|
+
point doesn't go through `runSetup` and never triggers the prompt.
|
|
36
|
+
- **`checkFrameworkPrerequisite`** — unchanged. Consumes the resolved
|
|
37
|
+
framework string regardless of whether it came from a flag or the
|
|
38
|
+
prompt.
|
|
39
|
+
- **CI runs (no TTY)** — unchanged. `process.stdin.isTTY` is false in
|
|
40
|
+
CI, so the prompt is bypassed and the default Claude Code behavior
|
|
41
|
+
preserved.
|
|
42
|
+
|
|
43
|
+
## 5. Rollback cost
|
|
44
|
+
|
|
45
|
+
Trivial. One readline-using function added, one decision helper added,
|
|
46
|
+
one conditional in `runSetup`'s opening lines. `git revert` restores
|
|
47
|
+
the pre-prompt bareword behavior; subcommand and CI flows unaffected.
|
|
48
|
+
|
|
49
|
+
## 6. Backwards compatibility / drift surface
|
|
50
|
+
|
|
51
|
+
Fully backward-compatible. Default behavior on non-TTY paths is
|
|
52
|
+
unchanged. Subcommand behavior is unchanged. Only interactive bareword
|
|
53
|
+
gets the new UX. Drift surface: none — the decision is centralized in
|
|
54
|
+
one pure exported function.
|
|
55
|
+
|
|
56
|
+
## 7. Authorization / Trust posture
|
|
57
|
+
|
|
58
|
+
No new authority. The prompt reads stdin and returns a string;
|
|
59
|
+
`checkFrameworkPrerequisite` and the rest of the existing wizard chain
|
|
60
|
+
handle everything downstream as they did before.
|
|
61
|
+
|
|
62
|
+
## Outcome
|
|
63
|
+
|
|
64
|
+
Ship. Closes the install/wizard portability arc's last UX gap. Pure
|
|
65
|
+
decision logic unit-tested; readline I/O verified by Justin's manual
|
|
66
|
+
end-to-end after merge.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Side-effects review — Secret Drop hardening
|
|
2
|
+
|
|
3
|
+
Per L6. Seven dimensions.
|
|
4
|
+
|
|
5
|
+
## 1. Over-block / under-block
|
|
6
|
+
|
|
7
|
+
**Before.** UNDER-block: a buggy consumer that called `/secrets/retrieve/<token>` once and dropped the response (parse error, exception in the writer step) lost the secret with no recovery path. The aggressive deletion-on-retrieve looked like a tight one-shot guarantee but actually created a silent-loss footgun, because the same 5-minute cleanup timer was already handling memory hygiene.
|
|
8
|
+
|
|
9
|
+
**After.** No new over-block: the default retrieval shape returns the same value the previous code did. Existing callers that don't pass `?consume=true` simply gain the ability to retry within the cleanup window — a safer state, not a different one. Callers that genuinely need one-shot semantics opt in with the query parameter.
|
|
10
|
+
|
|
11
|
+
The new stuck-consumer event closes the silent-failure gap: a submission that lingers unconsumed for 60s now produces a visible cue to the bound topic. Operators see the failure instead of waiting on a value that's already lost.
|
|
12
|
+
|
|
13
|
+
## 2. Level-of-abstraction fit
|
|
14
|
+
|
|
15
|
+
Three changes at deliberately different altitudes:
|
|
16
|
+
|
|
17
|
+
- `SecretDrop.ts`: new methods (`peekReceived`, `consumeReceived`, `onStuckConsumer`) and the stuck-timer state machine. Pure logic, unit-tested.
|
|
18
|
+
- `routes.ts`: the route honors `?consume=true`; one default listener wires the stuck-consumer event to the topic-bound session via `injectPasteNotification`. Thin orchestration only.
|
|
19
|
+
- Back-compat alias: `getReceived` is kept as `consumeReceived`'s legacy synonym so no caller has to migrate in this PR. Callers migrate at their own pace by switching to `peekReceived` + `consumeReceived` explicitly.
|
|
20
|
+
|
|
21
|
+
## 3. Signal vs Authority compliance
|
|
22
|
+
|
|
23
|
+
The Secret Drop request remains the authoritative declaration that this token may receive a submission (operator-issued via `/secrets/request`). The submission, once made, remains the authoritative payload — no agent code can mint a fake submission. The new methods change only the lifecycle of an already-authorized value:
|
|
24
|
+
|
|
25
|
+
- `peekReceived`: read-only, idempotent, returns the existing authority.
|
|
26
|
+
- `consumeReceived`: terminates the authority's lifetime explicitly.
|
|
27
|
+
- `onStuckConsumer`: signal-only emission to listeners; no listener gains write access to the submission.
|
|
28
|
+
|
|
29
|
+
No new authority is created. Existing CSRF, rate-limit, expiry, and one-time submission semantics are unchanged.
|
|
30
|
+
|
|
31
|
+
## 4. Interactions with adjacent systems
|
|
32
|
+
|
|
33
|
+
- **The `/secrets/retrieve/<token>` route.** Default behavior shifts from destructive to non-destructive. Existing callers that don't add `?consume=true` retain the value across multiple calls — strictly safer; no callers depend on "second call returns 404" except as a footgun.
|
|
34
|
+
- **The `/secrets/drop/<token>` form-side route.** Untouched. Operator-facing flow is byte-identical.
|
|
35
|
+
- **`onReceive` callback on the request.** Untouched. Still fires once on submit.
|
|
36
|
+
- **Existing 5-minute auto-cleanup.** Untouched. Still the safety net.
|
|
37
|
+
- **System-message routing via `injectPasteNotification`.** Reused for the new stuck-consumer system message. Same pattern as `secret-drop-received`.
|
|
38
|
+
- **Existing `SecretDrop.test.ts` suite.** All 25 cases continue to pass. The new test file adds 13 cases for the new surface.
|
|
39
|
+
|
|
40
|
+
## 5. Rollback cost
|
|
41
|
+
|
|
42
|
+
Low. Three files modified (`SecretDrop.ts`, `routes.ts`, one test file added). `git revert <merge-sha>` restores the pre-change atomic retrieve-and-delete behavior. Callers that adopted `?consume=true` continue to work after a revert (the parameter becomes a no-op against the pre-change atomic-delete path). The stuck-consumer event simply stops firing.
|
|
43
|
+
|
|
44
|
+
No state migration, no deployed-agent impact, no schema change. In-memory only.
|
|
45
|
+
|
|
46
|
+
## 6. Backwards compatibility / drift surface
|
|
47
|
+
|
|
48
|
+
Fully backwards-compatible at the call-site level:
|
|
49
|
+
|
|
50
|
+
- `getReceived(token)` is preserved as an alias for `consumeReceived(token)`.
|
|
51
|
+
- The route default is non-destructive — strictly safer for existing callers.
|
|
52
|
+
- Callers that depend on one-shot semantics (the bridge script in the parallel telegram-history-backfill PR being one example) opt in via `?consume=true` explicitly.
|
|
53
|
+
|
|
54
|
+
Drift surface: small. Two new methods on `SecretDrop`. One new event interface. One new constant for the grace period. The grace period is a hard-coded constant for v1 ship; a future PR can make it configurable per-request if a real need surfaces.
|
|
55
|
+
|
|
56
|
+
## 7. Authorization / Trust posture
|
|
57
|
+
|
|
58
|
+
No new authority. The two new methods read/write the same in-memory `received` map the previous code touched. The new listener-registration surface (`onStuckConsumer`) accepts callbacks from the route layer only — there is no external API to register a listener over HTTP. The route layer's default listener routes only to the topic-bound session (the same authority the `secret-drop-received` path already uses).
|
|
59
|
+
|
|
60
|
+
Trust ramifications: the operator gains a new visible signal (the stuck-consumer system message), which strengthens their ability to detect consumer-side failures. The agent gains a new structural primitive for safe-by-default retrieval, which reduces the surface for silent-loss bugs. Both directions of trust move in the safer direction.
|
|
61
|
+
|
|
62
|
+
## Outcome
|
|
63
|
+
|
|
64
|
+
Ship. Incident-driven, three independent changes, fully unit-tested, fully backwards-compatible at the call-site level. The single highest-impact change is the default-non-destructive retrieve — that alone makes the 2026-05-20 lost-SMS-code failure mode structurally impossible to repeat without an explicit `?consume=true` from the buggy consumer.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Side-effects review — Telegram history backfill (script tier)
|
|
2
|
+
|
|
3
|
+
Per L6. Seven dimensions.
|
|
4
|
+
|
|
5
|
+
## 1. Over-block / under-block
|
|
6
|
+
|
|
7
|
+
**Before.** Echo had no path to retrieve Telegram history after a local
|
|
8
|
+
topic-memory.db truncation. The Bot API is forward-only, so any data loss
|
|
9
|
+
in the local mirror was permanent absent a Telegram-side export. The
|
|
10
|
+
2026-05-20 native-module cascade exposed this gap.
|
|
11
|
+
|
|
12
|
+
**After.** The script-tier backfill closes the gap for the post-recovery
|
|
13
|
+
scenario. There is no over-block — the script runs only when invoked
|
|
14
|
+
explicitly (`--chat` flag required), never silently. No new automatic
|
|
15
|
+
behavior is added to any always-on path. Under-block protection: the
|
|
16
|
+
importer's UNIQUE-constraint-guarded INSERT OR IGNORE prevents duplicates
|
|
17
|
+
even under retries, FloodWait recoveries, and manual reruns.
|
|
18
|
+
|
|
19
|
+
## 2. Level-of-abstraction fit
|
|
20
|
+
|
|
21
|
+
Three modules at deliberately different altitudes:
|
|
22
|
+
|
|
23
|
+
- `topic-memory-importer.mjs` — pure DB logic, unit-tested against the
|
|
24
|
+
real schema. No network, no auth, no gramjs dependency. Trivially testable.
|
|
25
|
+
- `telegram-historian.mjs` — gramjs wrapper. Exposes the minimum surface
|
|
26
|
+
needed (connect, listForumTopics, iterTopicMessages). No business logic.
|
|
27
|
+
- `telegram-history-backfill.mjs` — orchestration only. Parses CLI flags,
|
|
28
|
+
resolves credentials, ties the two libs together.
|
|
29
|
+
|
|
30
|
+
Each module is replaceable without touching the others.
|
|
31
|
+
|
|
32
|
+
## 3. Signal vs Authority compliance
|
|
33
|
+
|
|
34
|
+
This is the central concern for this PR.
|
|
35
|
+
|
|
36
|
+
`.instar/secrets/...` is the credential signal. The operator's sealed
|
|
37
|
+
file is the authoritative declaration that this account may be accessed
|
|
38
|
+
via MTProto. The script reads the signal and acts on it; it has no
|
|
39
|
+
authority to mint or rotate credentials.
|
|
40
|
+
|
|
41
|
+
The session string, once captured, is the long-lived authority. Storing
|
|
42
|
+
it at mode 0600 outside any git path is the structural enforcement that
|
|
43
|
+
no other process can read it. The operator's Telegram Devices page is
|
|
44
|
+
the only revocation channel — exactly the right placement of authority,
|
|
45
|
+
because the operator alone owns that surface.
|
|
46
|
+
|
|
47
|
+
The script's read-only Telegram posture (iter/get only, no send/edit/
|
|
48
|
+
delete) is the third layer: even if everything else were compromised,
|
|
49
|
+
this code path cannot produce outbound messages or mutations against
|
|
50
|
+
Telegram.
|
|
51
|
+
|
|
52
|
+
## 4. Interactions with adjacent systems
|
|
53
|
+
|
|
54
|
+
- **instar server (echo's own process).** Holds `topic-memory.db` open
|
|
55
|
+
under WAL mode. SQLite serializes writes across processes, so the
|
|
56
|
+
backfill can write while the server is reading without corruption. The
|
|
57
|
+
FTS triggers maintain the FTS index automatically; tested by
|
|
58
|
+
`tests/unit/topic-memory-importer.test.ts`'s "does not corrupt FTS
|
|
59
|
+
index across re-runs" case.
|
|
60
|
+
- **Bot-API polling (TelegramAdapter).** Unaffected. Live messages
|
|
61
|
+
continue to arrive through the bot. The backfill writes the same
|
|
62
|
+
schema with the same constraints, so the live path's INSERT OR IGNORE
|
|
63
|
+
(if any) and the backfill's INSERT OR IGNORE converge to the same
|
|
64
|
+
final state regardless of ordering.
|
|
65
|
+
- **Coherence gate / external-operation-gate hook.** The script is a
|
|
66
|
+
local Node process; it does not go through the MCP tool path the
|
|
67
|
+
external-operation-gate intercepts. The gate's authority remains over
|
|
68
|
+
agent-initiated network operations from the harness; this script is
|
|
69
|
+
operator-initiated from the CLI.
|
|
70
|
+
- **Migration parity / PostUpdateMigrator.** No agent-installed file is
|
|
71
|
+
modified by this PR. No template change, no hook change, no config
|
|
72
|
+
default change. Existing agents are unaffected by the merge — they
|
|
73
|
+
pick up the script only if they explicitly run it.
|
|
74
|
+
|
|
75
|
+
## 5. Rollback cost
|
|
76
|
+
|
|
77
|
+
Trivial. Five files, two libraries, one test, one spec, one ELI16, one
|
|
78
|
+
side-effects review. `git revert <merge-sha>` removes the code. The
|
|
79
|
+
sealed credential file and the session string file are outside the repo
|
|
80
|
+
and unaffected by the revert. The `topic-memory.db` is touched only by
|
|
81
|
+
INSERT OR IGNORE, so already-imported rows are correct and stay — no
|
|
82
|
+
data restoration needed.
|
|
83
|
+
|
|
84
|
+
If the operator wants the imported rows undone too, a single SQL command
|
|
85
|
+
removes them: `DELETE FROM messages WHERE timestamp < '<backfill cutoff>'`.
|
|
86
|
+
This is safe because all writes go through the importer, which can be
|
|
87
|
+
disabled by removing the script.
|
|
88
|
+
|
|
89
|
+
## 6. Backwards compatibility / drift surface
|
|
90
|
+
|
|
91
|
+
Fully backwards-compatible. The script is additive only — no existing
|
|
92
|
+
behavior is modified. The `topic-memory.db` schema is read but never
|
|
93
|
+
altered (no migrations). The CLI entry is a new file; nothing references
|
|
94
|
+
it from existing code paths.
|
|
95
|
+
|
|
96
|
+
Drift surface:
|
|
97
|
+
- The script targets the schema as it exists 2026-05-20 (read live for
|
|
98
|
+
this PR). A future schema change (e.g. adding a `chat_id` column)
|
|
99
|
+
would require updating `topic-memory-importer.mjs`. The narrow surface
|
|
100
|
+
and unit-test coverage make this easy to spot in code review.
|
|
101
|
+
- gramjs (`telegram` package) is a new dependency. Version pin is
|
|
102
|
+
`^2.26.22` — caret range allows non-breaking updates. If gramjs ever
|
|
103
|
+
ships a breaking 3.x, the import surface in `telegram-historian.mjs`
|
|
104
|
+
is small (4 imports, 3 method calls) so adaptation cost is bounded.
|
|
105
|
+
|
|
106
|
+
## 7. Authorization / Trust posture
|
|
107
|
+
|
|
108
|
+
The single point where new authority is granted is the operator's
|
|
109
|
+
explicit hand-off of `api_id` + `api_hash` (2026-05-20T03:10:18Z via
|
|
110
|
+
Telegram topic 10873) and the live SMS-code exchange during `--auth`.
|
|
111
|
+
The script grants itself nothing — it only reads what the operator has
|
|
112
|
+
already authorized.
|
|
113
|
+
|
|
114
|
+
The session string, once captured, is the durable artifact of that
|
|
115
|
+
authorization. The operator can revoke it via Telegram's Devices UI at
|
|
116
|
+
any time; the script will then error on the next run and require a
|
|
117
|
+
fresh `--auth`. This places revocation entirely in the operator's hands
|
|
118
|
+
with no instar-side dependency.
|
|
119
|
+
|
|
120
|
+
The "read-only on Telegram" posture is enforced by **not exposing any
|
|
121
|
+
write method**, not by a guard the operator must trust. Adding a write
|
|
122
|
+
method would require a spec change AND a fresh operator approval — the
|
|
123
|
+
review-convergence + approved frontmatter tags would have to be
|
|
124
|
+
regenerated, which is the structural enforcement here.
|
|
125
|
+
|
|
126
|
+
## Outcome
|
|
127
|
+
|
|
128
|
+
Ship. Incident-driven, single-purpose, read-only, idempotent, unit-tested.
|
|
129
|
+
Promotion to a full `src/messaging/TelegramHistorian.ts` feature is a
|
|
130
|
+
separate, follow-up PR after the recovery itself is verified end-to-end
|
|
131
|
+
against the live lifeline supergroup.
|