instar 1.3.595 → 1.3.596

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.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * TelegramLiveSender — the real Telegram `SurfaceSender` for the live-test harness
3
+ * (docs/specs/live-user-channel-proof-standard.md §5.4). It posts a message into a
4
+ * Telegram forum topic AS A NON-AGENT IDENTITY (a DEMO bot / second account in a demo
5
+ * group — NOT the agent's own bot, which the agent would not treat as inbound), then
6
+ * waits for the AGENT's reply by polling the topic history for the next agent-authored
7
+ * message.
8
+ *
9
+ * Same shape as SlackLiveSender: the send transport (the demo-bot post) and the
10
+ * history read are INJECTED, so the CODE is complete + unit-testable here and the only
11
+ * wiring-time dependency is the demo-bot CREDENTIAL + a demo group/topic (provisioning).
12
+ *
13
+ * The agent's reply is identified DETERMINISTICALLY: the earliest history entry with
14
+ * `fromUser === false` (an agent outbound) whose messageId is strictly after the one we
15
+ * sent. Null on timeout. The responder-MACHINE attribution (the cross-machine proof) is
16
+ * the RealChannelDriver's injected placement reader, not this sender.
17
+ */
18
+ import type { SurfaceSender } from './RealChannelDriver.js';
19
+ import type { SendResult, ReplyResult } from './LiveTestHarness.js';
20
+ /** The subset of TelegramAdapter.getTopicHistory's LogEntry this sender reads. */
21
+ export interface TelegramHistoryEntry {
22
+ messageId: number;
23
+ text: string;
24
+ /** true = inbound user message; false = an agent (bot) outbound — the reply we wait for. */
25
+ fromUser: boolean;
26
+ }
27
+ export interface TelegramLiveSenderDeps {
28
+ /** Post a message into the topic AS THE DEMO identity (demo-bot token). Returns the new message id. */
29
+ postAsDemoUser: (topicId: number, text: string) => Promise<{
30
+ messageId: number;
31
+ }>;
32
+ /** Read recent topic history (Echo's getTopicHistory), newest-or-oldest order tolerated. */
33
+ getHistory: (topicId: number, limit?: number) => TelegramHistoryEntry[] | Promise<TelegramHistoryEntry[]>;
34
+ pollIntervalMs?: number;
35
+ sleep?: (ms: number) => Promise<void>;
36
+ now?: () => number;
37
+ logger?: (m: string) => void;
38
+ }
39
+ export declare class TelegramLiveSender implements SurfaceSender {
40
+ private readonly d;
41
+ constructor(deps: TelegramLiveSenderDeps);
42
+ private now;
43
+ private sleep;
44
+ private log;
45
+ private topicNum;
46
+ send(channelId: string, text: string): Promise<SendResult>;
47
+ awaitReply(channelId: string, opts: {
48
+ timeoutMs: number;
49
+ afterMessageId?: string;
50
+ }): Promise<Omit<ReplyResult, 'responderMachineId'> | null>;
51
+ private findAgentReply;
52
+ }
53
+ //# sourceMappingURL=TelegramLiveSender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TelegramLiveSender.d.ts","sourceRoot":"","sources":["../../src/core/TelegramLiveSender.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEpE,kFAAkF;AAClF,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,uGAAuG;IACvG,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClF,4FAA4F;IAC5F,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,oBAAoB,EAAE,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC1G,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,qBAAa,kBAAmB,YAAW,aAAa;IACtD,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAyB;gBAC/B,IAAI,EAAE,sBAAsB;IAExC,OAAO,CAAC,GAAG;YACG,KAAK;IAGnB,OAAO,CAAC,GAAG;IAEX,OAAO,CAAC,QAAQ;IAMV,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAQ1D,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,oBAAoB,CAAC,GAAG,IAAI,CAAC;YAepI,cAAc;CAW7B"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * TelegramLiveSender — the real Telegram `SurfaceSender` for the live-test harness
3
+ * (docs/specs/live-user-channel-proof-standard.md §5.4). It posts a message into a
4
+ * Telegram forum topic AS A NON-AGENT IDENTITY (a DEMO bot / second account in a demo
5
+ * group — NOT the agent's own bot, which the agent would not treat as inbound), then
6
+ * waits for the AGENT's reply by polling the topic history for the next agent-authored
7
+ * message.
8
+ *
9
+ * Same shape as SlackLiveSender: the send transport (the demo-bot post) and the
10
+ * history read are INJECTED, so the CODE is complete + unit-testable here and the only
11
+ * wiring-time dependency is the demo-bot CREDENTIAL + a demo group/topic (provisioning).
12
+ *
13
+ * The agent's reply is identified DETERMINISTICALLY: the earliest history entry with
14
+ * `fromUser === false` (an agent outbound) whose messageId is strictly after the one we
15
+ * sent. Null on timeout. The responder-MACHINE attribution (the cross-machine proof) is
16
+ * the RealChannelDriver's injected placement reader, not this sender.
17
+ */
18
+ export class TelegramLiveSender {
19
+ d;
20
+ constructor(deps) { this.d = deps; }
21
+ now() { return (this.d.now ?? Date.now)(); }
22
+ async sleep(ms) {
23
+ return (this.d.sleep ?? ((m) => new Promise((r) => setTimeout(r, m))))(ms);
24
+ }
25
+ log(m) { this.d.logger?.(`[telegram-live-sender] ${m}`); }
26
+ topicNum(channelId) {
27
+ const n = Number(channelId);
28
+ if (!Number.isFinite(n))
29
+ throw new Error(`telegram channelId "${channelId}" is not a numeric topic id`);
30
+ return n;
31
+ }
32
+ async send(channelId, text) {
33
+ const res = await this.d.postAsDemoUser(this.topicNum(channelId), text);
34
+ if (!Number.isFinite(res?.messageId)) {
35
+ throw new Error(`postAsDemoUser returned no messageId — message not posted`);
36
+ }
37
+ return { messageId: String(res.messageId) };
38
+ }
39
+ async awaitReply(channelId, opts) {
40
+ const topic = this.topicNum(channelId);
41
+ const afterId = opts.afterMessageId != null ? Number(opts.afterMessageId) : -Infinity;
42
+ const deadline = this.now() + opts.timeoutMs;
43
+ const pollMs = this.d.pollIntervalMs ?? 2000;
44
+ for (let first = true; first || this.now() < deadline; first = false) {
45
+ const reply = await this.findAgentReply(topic, afterId);
46
+ if (reply)
47
+ return reply;
48
+ if (this.now() >= deadline)
49
+ break;
50
+ await this.sleep(pollMs);
51
+ }
52
+ this.log(`no agent reply in topic ${topic} within ${opts.timeoutMs}ms`);
53
+ return null;
54
+ }
55
+ async findAgentReply(topicId, afterId) {
56
+ const entries = await this.d.getHistory(topicId, 100);
57
+ // Oldest-first by messageId so we return the EARLIEST agent reply after the prompt.
58
+ const oldestFirst = [...entries].sort((a, b) => a.messageId - b.messageId);
59
+ for (const e of oldestFirst) {
60
+ if (e.messageId <= afterId)
61
+ continue; // strictly after the prompt
62
+ if (e.fromUser)
63
+ continue; // skip inbound user messages (incl. our own demo post)
64
+ return { text: e.text ?? '', messageId: String(e.messageId) };
65
+ }
66
+ return null;
67
+ }
68
+ }
69
+ //# sourceMappingURL=TelegramLiveSender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TelegramLiveSender.js","sourceRoot":"","sources":["../../src/core/TelegramLiveSender.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAwBH,MAAM,OAAO,kBAAkB;IACZ,CAAC,CAAyB;IAC3C,YAAY,IAA4B,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAEpD,GAAG,KAAa,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACpD,KAAK,CAAC,KAAK,CAAC,EAAU;QAC5B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3F,CAAC;IACO,GAAG,CAAC,CAAS,IAAU,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,0BAA0B,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAExE,QAAQ,CAAC,SAAiB;QAChC,MAAM,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,6BAA6B,CAAC,CAAC;QACxG,OAAO,CAAC,CAAC;IACX,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,SAAiB,EAAE,IAAY;QACxC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;QACxE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,SAAiB,EAAE,IAAoD;QACtF,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QACtF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;QAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,cAAc,IAAI,IAAI,CAAC;QAC7C,KAAK,IAAI,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,KAAK,GAAG,KAAK,EAAE,CAAC;YACrE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACxD,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC;YACxB,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;gBAAE,MAAM;YAClC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,2BAA2B,KAAK,WAAW,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,OAAe,EAAE,OAAe;QAC3D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACtD,oFAAoF;QACpF,MAAM,WAAW,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;QAC3E,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;YAC5B,IAAI,CAAC,CAAC,SAAS,IAAI,OAAO;gBAAE,SAAS,CAAG,4BAA4B;YACpE,IAAI,CAAC,CAAC,QAAQ;gBAAE,SAAS,CAAe,uDAAuD;YAC/F,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;QAChE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.595",
3
+ "version": "1.3.596",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-06-16T10:03:21.927Z",
5
- "instarVersion": "1.3.595",
4
+ "generatedAt": "2026-06-16T10:35:02.431Z",
5
+ "instarVersion": "1.3.596",
6
6
  "entryCount": 201,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,21 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ `TelegramLiveSender` — a real Telegram `SurfaceSender` for the live-test harness (spec §5.4), ships DARK (not wired). Posts into a forum topic as a non-agent demo identity (injected post fn), awaits the agent's reply by polling topic history for the earliest `fromUser===false` entry strictly after the sent messageId. Parameterized on the demo-bot post fn + history reader, so only the demo-bot credential + demo group need provisioning.
9
+
10
+ ## What to Tell Your User
11
+
12
+ Nothing yet — internal harness infrastructure, ships dark (no runtime surface, no behavior change).
13
+
14
+ ## Summary of New Capabilities
15
+
16
+ None user-facing. Internally: the real Telegram drive for the live-test harness (Telegram half of the Telegram-AND-Slack bar). No new routes, config, or flags.
17
+
18
+ ## Evidence
19
+
20
+ - `tests/unit/TelegramLiveSender.test.ts` — 8 tests: post→id, non-numeric-topic throw, no-id throw, earliest-agent-reply-after-prompt, ignores inbound, poll-until-lands, timeout→null, async getHistory.
21
+ - `tsc --noEmit` clean. instar-dev gate green.
@@ -0,0 +1,28 @@
1
+ # Side-Effects Review — Live-test Telegram sender
2
+
3
+ **Slug:** live-test-telegram-sender
4
+ **Spec:** docs/specs/live-user-channel-proof-standard.md §5.4 (Platform-Sanctioned Automation)
5
+ **Files:** src/core/TelegramLiveSender.ts, tests/unit/TelegramLiveSender.test.ts
6
+ **Posture:** ships DARK — one pure/injectable SurfaceSender, NOT wired into server.ts.
7
+
8
+ ## What it is
9
+ TelegramLiveSender — the real Telegram `SurfaceSender`: posts into a forum topic AS A NON-AGENT identity (an injected demo-bot post fn), then `awaitReply` polls the topic history for the AGENT's reply (the earliest entry with `fromUser === false` strictly after the sent messageId). Same proven shape as SlackLiveSender; parameterized on the demo-bot post fn + the history reader, so only the demo-bot CREDENTIAL + a demo group/topic need provisioning.
10
+
11
+ ## Phase 1 — Principle check (signal vs authority)
12
+ Not a decision point — transport (post + poll). No block/filter/gate. Compliant.
13
+
14
+ ## Phase 4 — Side-effects answers
15
+ 1. **Over-block** — n/a. Worst case: the reply match misses a genuine agent reply → scenario records no-reply FAIL. Mitigated by the deterministic `fromUser===false` + strictly-after-messageId match over the full poll window.
16
+ 2. **Under-block** — n/a. Risk of matching a STALE agent message mitigated by the `messageId > afterId` strictly-after guard + oldest-first scan (earliest reply after the prompt).
17
+ 3. **Level-of-abstraction fit** — correct: the concrete Telegram adapter for the harness's `SurfaceSender` seam. Wraps the demo-bot post + getTopicHistory rather than reinventing them.
18
+ 4. **Signal vs authority** — compliant (transport, no authority).
19
+ 5. **Interactions** — none yet (dark, unwired). The demo-bot identity is SEPARATE from Echo's own bot, so a demo post can't be confused with Echo's outbound; history read is read-only.
20
+ 6. **External surfaces** — when wired, it WILL post a real message into a demo Telegram group via the demo-bot token. THIS increment: no wiring, no external surface. The demo-bot token + demo group is the provisioning dependency; the code is parameterized on it.
21
+ 7. **Multi-machine posture** — machine-agnostic (talks to the Telegram Bot API + reads topic history). The cross-machine attribution is the RealChannelDriver's PlacementResponderReader, not this sender. No single-machine assumption.
22
+ 8. **Rollback cost** — trivial: dark, unwired. Revert the commit.
23
+
24
+ ## No-deferrals
25
+ The runner route (wires both senders + RealChannelDriver + harness + the multi-machine matrix) is the NEXT tracked increment (CMT-1568, `.instar/plans/live-test-harness-drivers-BUILD.md`), not a deferral. This sender is complete + fully unit-tested (8 tests). The demo-bot/group provisioning is an external-credential dependency tracked in the plan, not a code deferral — the code is parameterized so only the credential is missing.
26
+
27
+ ## Phase 5 — Second-pass review
28
+ Not required: transport adapter, no block/allow/lifecycle/sentinel/gate surface (the Phase-5 triggers).