instar 1.3.732 → 1.3.733

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.
Files changed (50) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +127 -2
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/ConversationRegistry.d.ts +95 -0
  5. package/dist/core/ConversationRegistry.d.ts.map +1 -1
  6. package/dist/core/ConversationRegistry.js +292 -5
  7. package/dist/core/ConversationRegistry.js.map +1 -1
  8. package/dist/core/SessionManager.d.ts +15 -0
  9. package/dist/core/SessionManager.d.ts.map +1 -1
  10. package/dist/core/SessionManager.js +28 -0
  11. package/dist/core/SessionManager.js.map +1 -1
  12. package/dist/core/conversationBindToken.d.ts +36 -0
  13. package/dist/core/conversationBindToken.d.ts.map +1 -0
  14. package/dist/core/conversationBindToken.js +144 -0
  15. package/dist/core/conversationBindToken.js.map +1 -0
  16. package/dist/core/conversationIdentity.d.ts +14 -0
  17. package/dist/core/conversationIdentity.d.ts.map +1 -1
  18. package/dist/core/conversationIdentity.js +19 -0
  19. package/dist/core/conversationIdentity.js.map +1 -1
  20. package/dist/core/deliverToConversation.d.ts +68 -25
  21. package/dist/core/deliverToConversation.d.ts.map +1 -1
  22. package/dist/core/deliverToConversation.js +300 -11
  23. package/dist/core/deliverToConversation.js.map +1 -1
  24. package/dist/core/slackRefreshBinding.d.ts +8 -10
  25. package/dist/core/slackRefreshBinding.d.ts.map +1 -1
  26. package/dist/core/slackRefreshBinding.js +8 -16
  27. package/dist/core/slackRefreshBinding.js.map +1 -1
  28. package/dist/core/types.d.ts +9 -0
  29. package/dist/core/types.d.ts.map +1 -1
  30. package/dist/core/types.js.map +1 -1
  31. package/dist/monitoring/CommitmentTracker.d.ts +64 -0
  32. package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
  33. package/dist/monitoring/CommitmentTracker.js +58 -0
  34. package/dist/monitoring/CommitmentTracker.js.map +1 -1
  35. package/dist/monitoring/PromiseBeacon.d.ts +70 -0
  36. package/dist/monitoring/PromiseBeacon.d.ts.map +1 -1
  37. package/dist/monitoring/PromiseBeacon.js +221 -24
  38. package/dist/monitoring/PromiseBeacon.js.map +1 -1
  39. package/dist/server/AgentServer.d.ts +9 -0
  40. package/dist/server/AgentServer.d.ts.map +1 -1
  41. package/dist/server/AgentServer.js +8 -0
  42. package/dist/server/AgentServer.js.map +1 -1
  43. package/dist/server/routes.d.ts +12 -0
  44. package/dist/server/routes.d.ts.map +1 -1
  45. package/dist/server/routes.js +128 -10
  46. package/dist/server/routes.js.map +1 -1
  47. package/package.json +1 -1
  48. package/src/data/builtin-manifest.json +48 -48
  49. package/upgrades/1.3.733.md +108 -0
  50. package/upgrades/side-effects/durable-conversation-identity-increment2.md +88 -0
@@ -0,0 +1,36 @@
1
+ export interface BindTokenPayload {
2
+ sessionName: string;
3
+ bootstrapConversationIds: number[];
4
+ mintedAt: string;
5
+ }
6
+ /** §7 R7-minor-2: the token-less positive-id straggler backstop window. */
7
+ export declare const TOKENLESS_BIND_GRACE_DAYS = 14;
8
+ /** Read-or-create the 32-byte bind-token secret (first-boot generation). */
9
+ export declare function ensureBindTokenSecret(stateDir: string): Buffer;
10
+ export declare function mintBindToken(secret: Buffer, payload: BindTokenPayload): string;
11
+ /**
12
+ * Verify a token. Returns the CLAMPED payload on a valid MAC, null otherwise
13
+ * (missing/tampered/undecodable — the caller maps null to the typed
14
+ * `conversation-bind-not-authorized` refusal, fail-closed on minted-id binds).
15
+ */
16
+ export declare function verifyBindToken(secret: Buffer, token: string): BindTokenPayload | null;
17
+ /**
18
+ * Write the per-feature deploy stamp on first boot (idempotent — the house
19
+ * per-feature deploy-stamp shape, R8-low-3). The stamp anchors the
20
+ * `tokenlessBindGraceDays` clock for the R7-minor-2 straggler backstop.
21
+ */
22
+ export declare function ensureBindDeployStamp(stateDir: string, version: string): void;
23
+ /** Age of the deploy stamp in whole days, or null when unstamped/unreadable. */
24
+ export declare function bindDeployStampAgeDays(stateDir: string, now?: () => number): number | null;
25
+ /** The ctx surface the routes-layer bind gate consumes (wired at bootstrap;
26
+ * AgentServer default-constructs it so the gate is alive on every init path).
27
+ * `mint` is the SessionManager spawn-env side (INSTAR_BIND_TOKEN); `verify` +
28
+ * `deployStampAgeDays` are the POST /commitments gate side — one shared
29
+ * secret backs both. */
30
+ export interface ConversationBindAuth {
31
+ mint: (sessionName: string, bootstrapConversationIds: number[]) => string;
32
+ verify: (token: string) => BindTokenPayload | null;
33
+ deployStampAgeDays: () => number | null;
34
+ }
35
+ export declare function createConversationBindAuth(stateDir: string): ConversationBindAuth;
36
+ //# sourceMappingURL=conversationBindToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversationBindToken.d.ts","sourceRoot":"","sources":["../../src/core/conversationBindToken.ts"],"names":[],"mappings":"AAgCA,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,2EAA2E;AAC3E,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAK5C,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY9D;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAI/E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAqCtF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAW7E;AAED,gFAAgF;AAChF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,MAAM,GAAG,IAAI,CAYpG;AAED;;;;yBAIyB;AACzB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,wBAAwB,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC;IAC1E,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,gBAAgB,GAAG,IAAI,CAAC;IACnD,kBAAkB,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,oBAAoB,CAQjF"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * conversationBindToken — the §7 bind-time authority primitive (B7 / R3-M5 /
3
+ * R4-M3, docs/specs/durable-conversation-identity.md).
4
+ *
5
+ * A durable-state BIND (`POST /commitments`, working-set carry) on a minted
6
+ * conversation id must be scoped to the session's OWN authenticated bootstrap
7
+ * context. The enforcement primitive is a per-session bind token minted at
8
+ * session spawn and delivered ONLY through the spawned session's environment
9
+ * (`INSTAR_BIND_TOKEN` in the tmux -e block) — never over a route.
10
+ *
11
+ * The token is SELF-AUTHENTICATING and validation is STATELESS (R4-M3):
12
+ * sessions are tmux processes that OUTLIVE the server process, and the server
13
+ * restarts on every auto-update — so the server stores nothing per-session.
14
+ * It verifies the HMAC and reads the bootstrap set FROM the token; a live
15
+ * session's token remains valid across any number of server restarts.
16
+ *
17
+ * Shape: `base64url(payload) + "." + base64url(HMAC-SHA256(secret, payload))`
18
+ * where payload = { sessionName, bootstrapConversationIds, mintedAt }.
19
+ *
20
+ * The secret is a random 32-byte value generated once at first boot and
21
+ * persisted in the stateDir (same at-rest posture as authToken — plaintext
22
+ * machine-local; it authorizes only bind-scoping, never delivery or message
23
+ * content). It is deliberately EXCLUDED from the backup manifest (R5-minor-4):
24
+ * a disaster restore regenerates it, so all outstanding tokens invalidate —
25
+ * live sessions then hit the loud typed refusal until respawned, never a
26
+ * silent failure. Rotating it (delete the file, restart) is the revocation
27
+ * story — a LOUD deliberate trade.
28
+ */
29
+ import crypto from 'node:crypto';
30
+ import fs from 'node:fs';
31
+ import path from 'node:path';
32
+ /** §7 R7-minor-2: the token-less positive-id straggler backstop window. */
33
+ export const TOKENLESS_BIND_GRACE_DAYS = 14;
34
+ const SECRET_RELATIVE_PATH = path.join('state', 'conversation-bind-token.secret');
35
+ const DEPLOY_STAMP_RELATIVE_PATH = path.join('state', 'conversation-registry-deploy.json');
36
+ /** Read-or-create the 32-byte bind-token secret (first-boot generation). */
37
+ export function ensureBindTokenSecret(stateDir) {
38
+ const p = path.join(stateDir, SECRET_RELATIVE_PATH);
39
+ try {
40
+ const existing = fs.readFileSync(p);
41
+ if (existing.length >= 32)
42
+ return existing;
43
+ }
44
+ catch {
45
+ /* falls through to generation */
46
+ }
47
+ const secret = crypto.randomBytes(32);
48
+ fs.mkdirSync(path.dirname(p), { recursive: true });
49
+ fs.writeFileSync(p, secret, { mode: 0o600 });
50
+ return secret;
51
+ }
52
+ export function mintBindToken(secret, payload) {
53
+ const body = Buffer.from(JSON.stringify(payload), 'utf-8').toString('base64url');
54
+ const mac = crypto.createHmac('sha256', secret).update(body).digest('base64url');
55
+ return `${body}.${mac}`;
56
+ }
57
+ /**
58
+ * Verify a token. Returns the CLAMPED payload on a valid MAC, null otherwise
59
+ * (missing/tampered/undecodable — the caller maps null to the typed
60
+ * `conversation-bind-not-authorized` refusal, fail-closed on minted-id binds).
61
+ */
62
+ export function verifyBindToken(secret, token) {
63
+ if (typeof token !== 'string' || token.length > 4096)
64
+ return null;
65
+ const idx = token.indexOf('.');
66
+ if (idx <= 0 || idx === token.length - 1)
67
+ return null;
68
+ const body = token.slice(0, idx);
69
+ const givenMac = token.slice(idx + 1);
70
+ const expected = crypto.createHmac('sha256', secret).update(body).digest();
71
+ let given;
72
+ try {
73
+ given = Buffer.from(givenMac, 'base64url');
74
+ }
75
+ catch {
76
+ // @silent-fallback-ok — a non-decodable MAC is an INVALID token; returning
77
+ // null is the fail-CLOSED security contract (the caller refuses the bind),
78
+ // not a degraded-service fallback.
79
+ return null;
80
+ }
81
+ if (given.length !== expected.length || !crypto.timingSafeEqual(given, expected))
82
+ return null;
83
+ try {
84
+ const parsed = JSON.parse(Buffer.from(body, 'base64url').toString('utf-8'));
85
+ if (!parsed ||
86
+ typeof parsed.sessionName !== 'string' ||
87
+ !Array.isArray(parsed.bootstrapConversationIds) ||
88
+ typeof parsed.mintedAt !== 'string') {
89
+ return null;
90
+ }
91
+ return {
92
+ sessionName: parsed.sessionName.slice(0, 128),
93
+ bootstrapConversationIds: parsed.bootstrapConversationIds.filter((n) => typeof n === 'number' && Number.isSafeInteger(n)),
94
+ mintedAt: parsed.mintedAt.slice(0, 64),
95
+ };
96
+ }
97
+ catch {
98
+ return null;
99
+ }
100
+ }
101
+ /**
102
+ * Write the per-feature deploy stamp on first boot (idempotent — the house
103
+ * per-feature deploy-stamp shape, R8-low-3). The stamp anchors the
104
+ * `tokenlessBindGraceDays` clock for the R7-minor-2 straggler backstop.
105
+ */
106
+ export function ensureBindDeployStamp(stateDir, version) {
107
+ const p = path.join(stateDir, DEPLOY_STAMP_RELATIVE_PATH);
108
+ if (fs.existsSync(p))
109
+ return;
110
+ try {
111
+ fs.mkdirSync(path.dirname(p), { recursive: true });
112
+ fs.writeFileSync(p, JSON.stringify({ firstBootVersion: version, at: new Date().toISOString() }, null, 2));
113
+ }
114
+ catch {
115
+ /* @silent-fallback-ok — the stamp only arms the fail-OPEN straggler
116
+ attention backstop; a missed write means the backstop stays unarmed
117
+ (today's behavior), never a refused bind. */
118
+ }
119
+ }
120
+ /** Age of the deploy stamp in whole days, or null when unstamped/unreadable. */
121
+ export function bindDeployStampAgeDays(stateDir, now = Date.now) {
122
+ try {
123
+ const raw = JSON.parse(fs.readFileSync(path.join(stateDir, DEPLOY_STAMP_RELATIVE_PATH), 'utf-8'));
124
+ const at = raw?.at ? Date.parse(raw.at) : NaN;
125
+ if (!Number.isFinite(at))
126
+ return null;
127
+ return Math.floor((now() - at) / 86400000);
128
+ }
129
+ catch {
130
+ // @silent-fallback-ok — an unreadable/absent deploy stamp reads as "no age"
131
+ // (null), so the R7-minor-2 straggler backstop simply stays UNARMED (today's
132
+ // fail-open behavior) — never a refused bind, never a degraded service path.
133
+ return null;
134
+ }
135
+ }
136
+ export function createConversationBindAuth(stateDir) {
137
+ const secret = ensureBindTokenSecret(stateDir);
138
+ return {
139
+ mint: (sessionName, bootstrapConversationIds) => mintBindToken(secret, { sessionName, bootstrapConversationIds, mintedAt: new Date().toISOString() }),
140
+ verify: (token) => verifyBindToken(secret, token),
141
+ deployStampAgeDays: () => bindDeployStampAgeDays(stateDir),
142
+ };
143
+ }
144
+ //# sourceMappingURL=conversationBindToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversationBindToken.js","sourceRoot":"","sources":["../../src/core/conversationBindToken.ts"],"names":[],"mappings":"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"}
@@ -86,6 +86,20 @@ export declare function parseCanonicalKey(key: string): {
86
86
  tuple: ConversationTuple;
87
87
  workspaceId: string;
88
88
  } | null;
89
+ /**
90
+ * The SHARED id↔tuple coherence predicate (§3.5 ingest check ≡ §3.5.2 R5-M2
91
+ * delivery-time check — ONE implementation, never a second copy; a second
92
+ * implementation is a CI failure per §10). An id is coherent with a tuple iff
93
+ * it is the tuple's recomputed candidate or a within-`MAX_PROBE_DISTANCE`
94
+ * displacement offset of it (probe direction DOWN):
95
+ *
96
+ * cand(routingKey(tuple)) − MAX_PROBE_DISTANCE ≤ id ≤ cand(routingKey(tuple))
97
+ *
98
+ * Used at delivery time to validate a binding's stored `topicId` against its
99
+ * record-carried `boundTuple` (increment 2); the §6.1-step-9 replication
100
+ * ingest check MUST reuse this export.
101
+ */
102
+ export declare function idWithinCoherenceBound(id: number, tuple: ConversationTuple): boolean;
89
103
  /** Result of a displacement walk (§3.3 probe / §3.5.1 step 2). */
90
104
  export type DisplacementResult = {
91
105
  ok: true;
@@ -1 +1 @@
1
- {"version":3,"file":"conversationIdentity.d.ts","sourceRoot":"","sources":["../../src/core/conversationIdentity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;kFAEkF;AAClF,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,uFAAuF;AACvF,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,2FAA2F;AAC3F,eAAO,MAAM,WAAW,gBAAgB,CAAC;AACzC,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,QAAqB,CAAC;AACtD,eAAO,MAAM,kBAAkB,QAAoB,CAAC;AACpD,eAAO,MAAM,qBAAqB,QAAiB,CAAC;AACpD,yFAAyF;AACzF,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,6EAA6E;AAC7E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAEnE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAO/E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAIjG;AAED,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQvG;AAED,kEAAkE;AAClE,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAA;CAAE,CAAC;AAElC;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,kBAAkB,CASzG"}
1
+ {"version":3,"file":"conversationIdentity.d.ts","sourceRoot":"","sources":["../../src/core/conversationIdentity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;kFAEkF;AAClF,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,uFAAuF;AACvF,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,2FAA2F;AAC3F,eAAO,MAAM,WAAW,gBAAgB,CAAC;AACzC,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,QAAqB,CAAC;AACtD,eAAO,MAAM,kBAAkB,QAAoB,CAAC;AACpD,eAAO,MAAM,qBAAqB,QAAiB,CAAC;AACpD,yFAAyF;AACzF,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,6EAA6E;AAC7E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAEnE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAO/E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAIjG;AAED,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQvG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAIpF;AAED,kEAAkE;AAClE,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAA;CAAE,CAAC;AAElC;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,kBAAkB,CASzG"}
@@ -114,6 +114,25 @@ export function parseCanonicalKey(key) {
114
114
  return null;
115
115
  return { tuple: { platform: 'slack', channelId, threadTs: threadTs ?? null }, workspaceId };
116
116
  }
117
+ /**
118
+ * The SHARED id↔tuple coherence predicate (§3.5 ingest check ≡ §3.5.2 R5-M2
119
+ * delivery-time check — ONE implementation, never a second copy; a second
120
+ * implementation is a CI failure per §10). An id is coherent with a tuple iff
121
+ * it is the tuple's recomputed candidate or a within-`MAX_PROBE_DISTANCE`
122
+ * displacement offset of it (probe direction DOWN):
123
+ *
124
+ * cand(routingKey(tuple)) − MAX_PROBE_DISTANCE ≤ id ≤ cand(routingKey(tuple))
125
+ *
126
+ * Used at delivery time to validate a binding's stored `topicId` against its
127
+ * record-carried `boundTuple` (increment 2); the §6.1-step-9 replication
128
+ * ingest check MUST reuse this export.
129
+ */
130
+ export function idWithinCoherenceBound(id, tuple) {
131
+ if (!Number.isSafeInteger(id) || id >= 0)
132
+ return false;
133
+ const cand = candidateIdForRoutingKey(routingKeyForTuple(tuple));
134
+ return id <= cand && id >= cand - MAX_PROBE_DISTANCE;
135
+ }
117
136
  /**
118
137
  * The ONE shared displacement implementation (§3.3 = §3.5.1 step 2 — pinned by
119
138
  * the §10 shared-implementation equivalence test; R2-adversarial-2). Walks the
@@ -1 +1 @@
1
- {"version":3,"file":"conversationIdentity.js","sourceRoot":"","sources":["../../src/core/conversationIdentity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conversationIdentity.js","sourceRoot":"","sources":["../../src/core/conversationIdentity.ts"],"names":[],"mappings":"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"}
@@ -1,27 +1,3 @@
1
- /**
2
- * deliverToConversation — the outbound funnel SKELETON
3
- * (docs/specs/durable-conversation-identity.md §5; §6.1 increment 1).
4
- *
5
- * A single delivery helper every follow-through consumer MIGRATES onto in the
6
- * later §6.1 increments. In increment 1 it ships with ZERO consumers and the
7
- * `id<0` arm dark-gated behind `conversationIdentity.followThrough` (§9:
8
- * `enabled` OMITTED → the developmentAgent gate resolves it — live-on-dev,
9
- * dark-fleet; `dryRun: true` FIRST because delivery is externally visible).
10
- *
11
- * §5.1 failure/dryRun contract: a funnel non-delivery is a TYPED,
12
- * NON-EXCEPTIONAL return the caller inspects — never a thrown exception, and
13
- * NEVER success-shaped. dryRun (and fleet-dark) return the SAME
14
- * `not-delivered` typed result the unresolvable path uses, plus a
15
- * `would-deliver` audit line — caller-visible as a non-delivery so beacon
16
- * retry / attention escalation keep engaging (A Refusal Stays a Refusal / P18).
17
- *
18
- * NOT in this skeleton (they land WITH their §6.1 increments, before any
19
- * consumer migrates onto the funnel): the §5.0(a) E1 ambiguous-outcome
20
- * idempotency guard + durable send-intent ops (increment 2, the proof
21
- * consumer), the §5 `deterministicKind` gate-exempt arm (increment 3), the
22
- * §5.2 P17 budgets (increment 5's attention migration), and the §5.1
23
- * permanent-error classification (increment 2).
24
- */
25
1
  import type { ConversationRegistry } from './ConversationRegistry.js';
26
2
  export interface DeliverOpts {
27
3
  isProxy?: boolean;
@@ -29,16 +5,78 @@ export interface DeliverOpts {
29
5
  tier?: string;
30
6
  allowDuplicate?: boolean;
31
7
  messageKind?: string;
8
+ /**
9
+ * §5.0(a) STABLE logical send identity (`<commitmentId>:<sendSeq>` for
10
+ * beacon sends — the §3.4 pinned encoding). A caller supplying its own
11
+ * logicalSendId MUST also define its retirement events (R8-low-2); a caller
12
+ * that cannot name them belongs on the content-hash window lane (omit this).
13
+ */
14
+ logicalSendId?: string;
15
+ /**
16
+ * §3.5.2 property 5: the binding record's denormalized bind-time tuple.
17
+ * Delivery targets `resolve(boundTuple)` when present AND coherent with the
18
+ * binding's stored id — the uniform every-machine delivery rule.
19
+ */
20
+ boundTuple?: {
21
+ platform: string;
22
+ channelId: string;
23
+ threadTs: string | null;
24
+ };
32
25
  }
33
26
  export type DeliveryOutcome = {
34
27
  delivered: true;
35
28
  outcome: 'delivered';
29
+ }
30
+ /** §5.0(a): DELIVERED-EQUIVALENT for sequencing (R7-M1) — the caller treats
31
+ * it as delivered (no re-escalation, seq advances) so suppression can never
32
+ * mute the beacon. */
33
+ | {
34
+ delivered: false;
35
+ outcome: 'already-delivered-recently';
36
36
  } | {
37
37
  delivered: false;
38
38
  outcome: 'not-delivered';
39
- reason: 'follow-through-dark' | 'follow-through-dry-run' | 'unresolvable' | 'replicated-only-origin' | 'system-channel-suppressed' | 'no-slack-adapter' | 'send-failed' | 'telegram-send-failed';
39
+ reason: 'follow-through-dark' | 'follow-through-dry-run' | 'unresolvable' | 'replicated-only-origin' | 'system-channel-suppressed' | 'no-slack-adapter' | 'send-failed' | 'telegram-send-failed' | 'conversation-unreachable' | 'conversation-binding-incoherent' | 'binding-target-unresolved';
40
40
  detail?: string;
41
+ /** §5.1: true ONLY for the pinned permanent error set — the beacon
42
+ * treats it as TERMINAL (dead-letter), never an infinite retry. */
43
+ permanent?: boolean;
44
+ /** §5/I1 dead-letter scoping: a by-design non-owning/unresolvable
45
+ * refusal — the beacon STANDS DOWN (bounded ownership recheck), and this
46
+ * NEVER increments the dead-letter counter. */
47
+ standDown?: boolean;
41
48
  };
49
+ /** §5.1 pinned PERMANENT `chat.postMessage` error codes — the code is
50
+ * `is_archived`, NOT `channel_archived` (adversarial-A2/codex-X4). DISTINCT
51
+ * from the adapter's token-scoped `SlackApiError.permanent` set. */
52
+ export declare const CONVERSATION_UNREACHABLE_ERRORS: Set<string>;
53
+ /** Content-hash lane length gate — mirrors the Telegram exact-duplicate
54
+ * dedup's `minLength` (brief acks are never suppressed). */
55
+ export declare const CONTENT_HASH_MIN_LENGTH = 40;
56
+ /** §5.1 R2-lessons-2/P17 pinned emitter coalescing window (mass-unreachable). */
57
+ export declare const UNREACHABLE_COALESCING_WINDOW_MS = 60000;
58
+ export type SlackSendErrorClass = {
59
+ kind: 'permanent';
60
+ code: string;
61
+ } | {
62
+ kind: 'permanent-shaped-unknown';
63
+ code: string;
64
+ } | {
65
+ kind: 'clean-transient';
66
+ detail: string;
67
+ } | {
68
+ kind: 'ambiguous';
69
+ detail: string;
70
+ };
71
+ /**
72
+ * Classify a Slack-arm send failure (§5.1 + §5.0(a) R2-security-NEW-3).
73
+ * A `SlackApiError` means Slack ANSWERED `ok:false` — positive evidence the
74
+ * message did NOT post (clean), further split permanent/canary/transient.
75
+ * A pre-accept network refusal is clean. Everything else (timeout, reset,
76
+ * unknown) is AMBIGUOUS — the message may actually have posted, so the E1
77
+ * suppressor IS recorded (only a CLEAN failure forbids recording).
78
+ */
79
+ export declare function classifySlackSendError(err: unknown): SlackSendErrorClass;
42
80
  export interface ConversationDeliveryDeps {
43
81
  registry: ConversationRegistry;
44
82
  /** §9 followThrough gate state, resolved through resolveDevAgentGate at wiring. */
@@ -55,7 +93,12 @@ export interface ConversationDeliveryDeps {
55
93
  isSystemChannel?: (channelId: string) => boolean;
56
94
  /** `would-deliver` audit sink for dark/dry non-deliveries (§5.1). */
57
95
  auditWouldDeliver?: (line: string) => void;
96
+ /** ONE deduped attention item per episode (incoherent binding, pin redirect,
97
+ * drift canary, mass-unreachable summary, non-owning heal paths). */
98
+ onAttention?: (dedupeKey: string, title: string, body: string) => void;
58
99
  log?: (line: string) => void;
100
+ /** Test override for the mass-unreachable coalescing window. */
101
+ coalescingWindowMs?: number;
59
102
  }
60
103
  export type DeliverToConversation = (id: number, text: string, opts?: DeliverOpts) => Promise<DeliveryOutcome>;
61
104
  export declare function createConversationDelivery(deps: ConversationDeliveryDeps): DeliverToConversation;
@@ -1 +1 @@
1
- {"version":3,"file":"deliverToConversation.d.ts","sourceRoot":"","sources":["../../src/core/deliverToConversation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GACzC;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EACF,qBAAqB,GACrB,wBAAwB,GACxB,cAAc,GACd,wBAAwB,GACxB,2BAA2B,GAC3B,kBAAkB,GAClB,aAAa,GACb,sBAAsB,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,mFAAmF;IACnF,aAAa,EAAE,MAAM;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3D,8EAA8E;IAC9E,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACtF,2EAA2E;IAC3E,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtG;2EACuE;IACvE,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;AAE/G,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,wBAAwB,GAAG,qBAAqB,CAwFhG"}
1
+ {"version":3,"file":"deliverToConversation.d.ts","sourceRoot":"","sources":["../../src/core/deliverToConversation.ts"],"names":[],"mappings":"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"}