instar 1.3.48 → 1.3.50

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,82 @@
1
+ /**
2
+ * HttpParitySource.ts — the live Phase-1/3 ParitySource adapter.
3
+ *
4
+ * Portal exposes `GET /api/instar/read` (Dawn, committed at d65136b3b6) as the
5
+ * read-only seam — the architectural pivot from raw Postgres after Prisma Data
6
+ * Platform refused to mint a database role (it forbids CREATE ROLE / GRANT). API
7
+ * level is actually a cleaner seam than direct DB access: the dry-run tests
8
+ * against the same contract Phase 2 (write/update APIs) will use, with no
9
+ * Prisma-admin dependency, and the architectural levels line up (Portal's HTTP
10
+ * surface ↔ Instar's {@link ParitySource} interface).
11
+ *
12
+ * This adapter implements ParitySource via that endpoint. The runner's seam,
13
+ * comparator, invariants, and JSONL audit trail are unchanged — only the read
14
+ * adapter swaps. Reads are PRE-FETCHED in {@link prepare}: the dry-run captures
15
+ * one snapshot of Portal's clusters at a moment in time, then compares against
16
+ * that frozen view. This makes the sync `readPortalClusters()` contract trivially
17
+ * satisfiable and stabilises the comparison against in-flight Portal writes
18
+ * (Portal remains the sole writer; we just look at one consistent slice).
19
+ *
20
+ * The endpoint is paginated (server-side cap: 1000 rows per request). The
21
+ * pagination is keyed off the feedback table — clusters/dispatches accompany each
22
+ * page and may repeat — so cluster collection deduplicates by clusterId across
23
+ * pages and stops when a page returns fewer feedback rows than the requested
24
+ * limit (the documented "no more pages" signal) OR after a hard safety cap.
25
+ *
26
+ * Field naming is intentionally tolerant: cluster rows may arrive as camelCase
27
+ * (Prisma default) or snake_case (raw SQL projection). The mapper accepts both.
28
+ */
29
+ import type { ParitySource } from './dryRunCompare.js';
30
+ import type { PortalCluster } from '../processor/parity.js';
31
+ /** Minimal fetch shape the adapter needs (lets tests pass a stub). */
32
+ export type FetchLike = (input: string, init?: {
33
+ headers?: Record<string, string>;
34
+ signal?: AbortSignal;
35
+ }) => Promise<{
36
+ ok: boolean;
37
+ status: number;
38
+ statusText: string;
39
+ json(): Promise<unknown>;
40
+ text(): Promise<string>;
41
+ }>;
42
+ export interface HttpParitySourceConfig {
43
+ /** Portal base URL, e.g. `https://portal.bot-me.ai`. */
44
+ baseUrl: string;
45
+ /** Bearer token with `instar:read` scope. */
46
+ token: string;
47
+ /** Page size sent as `?limit=`. Default 1000 (server max per Dawn's contract). */
48
+ pageSize?: number;
49
+ /** Safety cap on pagination loops. Default 200 pages (= 200k feedback rows). */
50
+ maxPages?: number;
51
+ /** Optional `?status=` filter passed through to the endpoint. */
52
+ status?: string;
53
+ /** Injectable fetch (for tests). Defaults to global `fetch`. */
54
+ fetchImpl?: FetchLike;
55
+ /** Optional path override (defaults to `/api/instar/read`). */
56
+ readPath?: string;
57
+ }
58
+ /** Error thrown when Portal returns non-OK. The status code is preserved for callers. */
59
+ export declare class HttpParitySourceError extends Error {
60
+ readonly status: number;
61
+ constructor(status: number, message: string);
62
+ }
63
+ /**
64
+ * Live ParitySource backed by Portal's `/api/instar/read` endpoint. Construct
65
+ * with the base URL + read-scope token, then `await source.prepare()` once
66
+ * before passing to {@link runDryRunCompare}. The pre-fetch captures a single
67
+ * consistent snapshot of Portal's clusters; the runner reads it synchronously.
68
+ */
69
+ export declare class HttpParitySource implements ParitySource {
70
+ private readonly config;
71
+ private snapshot;
72
+ private readonly pageSize;
73
+ private readonly maxPages;
74
+ private readonly fetch;
75
+ private readonly readPath;
76
+ constructor(config: HttpParitySourceConfig);
77
+ /** Pre-fetch and cache the cluster snapshot. Idempotent: re-calling re-fetches. */
78
+ prepare(): Promise<void>;
79
+ /** Sync ParitySource read — returns the snapshot captured by {@link prepare}. */
80
+ readPortalClusters(): PortalCluster[];
81
+ }
82
+ //# sourceMappingURL=HttpParitySource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpParitySource.d.ts","sourceRoot":"","sources":["../../../src/feedback-factory/dryrun/HttpParitySource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5D,sEAAsE;AACtE,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC;IACpH,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACzB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB,CAAC,CAAC;AAEH,MAAM,WAAW,sBAAsB;IACrC,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAiBD,yFAAyF;AACzF,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBACZ,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK5C;AAgCD;;;;;GAKG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IAOvC,OAAO,CAAC,QAAQ,CAAC,MAAM;IANnC,OAAO,CAAC,QAAQ,CAAgC;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEL,MAAM,EAAE,sBAAsB;IAU3D,mFAAmF;IAC7E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAqC9B,iFAAiF;IACjF,kBAAkB,IAAI,aAAa,EAAE;CAMtC"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * HttpParitySource.ts — the live Phase-1/3 ParitySource adapter.
3
+ *
4
+ * Portal exposes `GET /api/instar/read` (Dawn, committed at d65136b3b6) as the
5
+ * read-only seam — the architectural pivot from raw Postgres after Prisma Data
6
+ * Platform refused to mint a database role (it forbids CREATE ROLE / GRANT). API
7
+ * level is actually a cleaner seam than direct DB access: the dry-run tests
8
+ * against the same contract Phase 2 (write/update APIs) will use, with no
9
+ * Prisma-admin dependency, and the architectural levels line up (Portal's HTTP
10
+ * surface ↔ Instar's {@link ParitySource} interface).
11
+ *
12
+ * This adapter implements ParitySource via that endpoint. The runner's seam,
13
+ * comparator, invariants, and JSONL audit trail are unchanged — only the read
14
+ * adapter swaps. Reads are PRE-FETCHED in {@link prepare}: the dry-run captures
15
+ * one snapshot of Portal's clusters at a moment in time, then compares against
16
+ * that frozen view. This makes the sync `readPortalClusters()` contract trivially
17
+ * satisfiable and stabilises the comparison against in-flight Portal writes
18
+ * (Portal remains the sole writer; we just look at one consistent slice).
19
+ *
20
+ * The endpoint is paginated (server-side cap: 1000 rows per request). The
21
+ * pagination is keyed off the feedback table — clusters/dispatches accompany each
22
+ * page and may repeat — so cluster collection deduplicates by clusterId across
23
+ * pages and stops when a page returns fewer feedback rows than the requested
24
+ * limit (the documented "no more pages" signal) OR after a hard safety cap.
25
+ *
26
+ * Field naming is intentionally tolerant: cluster rows may arrive as camelCase
27
+ * (Prisma default) or snake_case (raw SQL projection). The mapper accepts both.
28
+ */
29
+ /** Error thrown when Portal returns non-OK. The status code is preserved for callers. */
30
+ export class HttpParitySourceError extends Error {
31
+ status;
32
+ constructor(status, message) {
33
+ super(message);
34
+ this.name = 'HttpParitySourceError';
35
+ this.status = status;
36
+ }
37
+ }
38
+ /**
39
+ * Coerce one raw cluster row into the {@link PortalCluster} shape. Accepts both
40
+ * camelCase and snake_case keys; missing optional fields stay undefined. Throws
41
+ * only when the required keys (clusterId, type, title, fingerprint) cannot be
42
+ * resolved — a row that opaque is a contract violation we want to surface.
43
+ */
44
+ function coerceCluster(row) {
45
+ if (!row || typeof row !== 'object') {
46
+ throw new HttpParitySourceError(502, 'cluster row is not an object');
47
+ }
48
+ const r = row;
49
+ const pick = (...keys) => {
50
+ for (const k of keys)
51
+ if (r[k] !== undefined && r[k] !== null)
52
+ return r[k];
53
+ return undefined;
54
+ };
55
+ const clusterId = pick('clusterId', 'cluster_id', 'id');
56
+ const type = pick('type');
57
+ const title = pick('title');
58
+ const fingerprint = pick('fingerprint') ?? '';
59
+ if (typeof clusterId !== 'string' || typeof type !== 'string' || typeof title !== 'string') {
60
+ throw new HttpParitySourceError(502, `cluster row missing required fields (clusterId/type/title)`);
61
+ }
62
+ const out = { clusterId, type, title, fingerprint: String(fingerprint) };
63
+ const status = pick('status');
64
+ if (typeof status === 'string')
65
+ out.status = status;
66
+ const rc = pick('recurrenceCount', 'recurrence_count');
67
+ if (typeof rc === 'number')
68
+ out.recurrenceCount = rc;
69
+ return out;
70
+ }
71
+ /**
72
+ * Live ParitySource backed by Portal's `/api/instar/read` endpoint. Construct
73
+ * with the base URL + read-scope token, then `await source.prepare()` once
74
+ * before passing to {@link runDryRunCompare}. The pre-fetch captures a single
75
+ * consistent snapshot of Portal's clusters; the runner reads it synchronously.
76
+ */
77
+ export class HttpParitySource {
78
+ config;
79
+ snapshot = null;
80
+ pageSize;
81
+ maxPages;
82
+ fetch;
83
+ readPath;
84
+ constructor(config) {
85
+ this.config = config;
86
+ this.pageSize = config.pageSize ?? 1000;
87
+ this.maxPages = config.maxPages ?? 200;
88
+ this.fetch = config.fetchImpl ?? globalThis.fetch;
89
+ this.readPath = config.readPath ?? '/api/instar/read';
90
+ if (!this.fetch) {
91
+ throw new HttpParitySourceError(500, 'no fetch available — pass fetchImpl or run on a runtime with global fetch');
92
+ }
93
+ }
94
+ /** Pre-fetch and cache the cluster snapshot. Idempotent: re-calling re-fetches. */
95
+ async prepare() {
96
+ const byId = new Map();
97
+ const base = this.config.baseUrl.replace(/\/+$/, '');
98
+ const authHeader = `Bearer ${this.config.token}`;
99
+ for (let page = 0; page < this.maxPages; page++) {
100
+ const offset = page * this.pageSize;
101
+ const qs = new URLSearchParams({ limit: String(this.pageSize), offset: String(offset) });
102
+ if (this.config.status)
103
+ qs.set('status', this.config.status);
104
+ const url = `${base}${this.readPath}?${qs.toString()}`;
105
+ const res = await this.fetch(url, { headers: { Authorization: authHeader, Accept: 'application/json' } });
106
+ if (!res.ok) {
107
+ let detail = '';
108
+ try {
109
+ detail = (await res.text()).slice(0, 200);
110
+ }
111
+ catch { /* ignore */ }
112
+ throw new HttpParitySourceError(res.status, `Portal /api/instar/read failed (page ${page}, status ${res.status} ${res.statusText}): ${detail}`);
113
+ }
114
+ const envelope = (await res.json());
115
+ const rawClusters = envelope?.data?.clusters ?? [];
116
+ for (const raw of rawClusters) {
117
+ const c = coerceCluster(raw);
118
+ if (!byId.has(c.clusterId))
119
+ byId.set(c.clusterId, c);
120
+ }
121
+ // Pagination stop signal: returned_count < pageSize means the feedback table
122
+ // is exhausted (clusters/dispatches per Dawn's contract accompany the feedback
123
+ // pages and stabilise quickly via the byId dedup above).
124
+ const returned = envelope?.meta?.returned_count ?? rawClusters.length;
125
+ if (returned < this.pageSize)
126
+ break;
127
+ }
128
+ this.snapshot = [...byId.values()];
129
+ }
130
+ /** Sync ParitySource read — returns the snapshot captured by {@link prepare}. */
131
+ readPortalClusters() {
132
+ if (this.snapshot === null) {
133
+ throw new HttpParitySourceError(500, 'HttpParitySource.prepare() must be awaited before readPortalClusters()');
134
+ }
135
+ return this.snapshot.map((c) => ({ ...c }));
136
+ }
137
+ }
138
+ //# sourceMappingURL=HttpParitySource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpParitySource.js","sourceRoot":"","sources":["../../../src/feedback-factory/dryrun/HttpParitySource.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TelegramLifeline.d.ts","sourceRoot":"","sources":["../../src/lifeline/TelegramLifeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TelegramLifeline.d.ts","sourceRoot":"","sources":["../../src/lifeline/TelegramLifeline.ts"],"names":[],"mappings":"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"}
@@ -1188,6 +1188,19 @@ export class TelegramLifeline {
1188
1188
  /** Full semver of this lifeline, read once at construction. */
1189
1189
  lifelineVersion = getInstarVersion();
1190
1190
  async forwardToServer(topicId, text, rawMsg) {
1191
+ // a2a spoof-defense fields (MENTOR-LIVE-READINESS-SPEC §Recipient side). The
1192
+ // server's /internal/telegram-forward handler passes these to
1193
+ // `TelegramAdapter.dispatchAgentMessageHook` so the a2a hook can distinguish
1194
+ // a real bot-sent marker (route) from a user typing a marker-shaped string
1195
+ // (drop as `agent-marker-spoofed-by-user`). An older server reads only the
1196
+ // fields it knows and ignores the rest. An older lifeline omits them →
1197
+ // server treats senderIsBot as falsy → marker-bearing forwards drop closed,
1198
+ // matching the spec invariant (a real user typing a marker MUST be dropped).
1199
+ const rawFrom = rawMsg.from;
1200
+ const rawSenderChat = rawMsg.sender_chat;
1201
+ const senderIsBot = rawFrom?.is_bot === true;
1202
+ const senderChatId = rawSenderChat?.id !== undefined ? String(rawSenderChat.id) : undefined;
1203
+ const senderBotId = senderChatId ?? (senderIsBot && rawFrom?.id !== undefined ? String(rawFrom.id) : undefined);
1191
1204
  const buildBody = (includeVersion) => JSON.stringify({
1192
1205
  topicId,
1193
1206
  text,
@@ -1196,6 +1209,9 @@ export class TelegramLifeline {
1196
1209
  fromFirstName: rawMsg.from.first_name,
1197
1210
  messageId: rawMsg.message_id,
1198
1211
  timestamp: new Date(rawMsg.date * 1000).toISOString(),
1212
+ senderIsBot,
1213
+ ...(senderChatId !== undefined ? { senderChatId } : {}),
1214
+ ...(senderBotId !== undefined ? { senderBotId } : {}),
1199
1215
  ...(includeVersion ? { lifelineVersion: this.lifelineVersion } : {}),
1200
1216
  });
1201
1217
  const doForward = async () => {