instar 1.3.489 → 1.3.491

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 (175) hide show
  1. package/dashboard/index.html +30 -0
  2. package/dist/commands/server.d.ts.map +1 -1
  3. package/dist/commands/server.js +1124 -28
  4. package/dist/commands/server.js.map +1 -1
  5. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  6. package/dist/config/ConfigDefaults.js +23 -0
  7. package/dist/config/ConfigDefaults.js.map +1 -1
  8. package/dist/core/CodexResumeMap.d.ts +95 -0
  9. package/dist/core/CodexResumeMap.d.ts.map +1 -0
  10. package/dist/core/CodexResumeMap.js +283 -0
  11. package/dist/core/CodexResumeMap.js.map +1 -0
  12. package/dist/core/GuardPostureStore.d.ts +38 -0
  13. package/dist/core/GuardPostureStore.d.ts.map +1 -0
  14. package/dist/core/GuardPostureStore.js +87 -0
  15. package/dist/core/GuardPostureStore.js.map +1 -0
  16. package/dist/core/MachinePoolRegistry.d.ts +16 -0
  17. package/dist/core/MachinePoolRegistry.d.ts.map +1 -1
  18. package/dist/core/MachinePoolRegistry.js +24 -1
  19. package/dist/core/MachinePoolRegistry.js.map +1 -1
  20. package/dist/core/MeshRpc.d.ts +3 -0
  21. package/dist/core/MeshRpc.d.ts.map +1 -1
  22. package/dist/core/MeshRpc.js +5 -0
  23. package/dist/core/MeshRpc.js.map +1 -1
  24. package/dist/core/ModelSwapService.d.ts +26 -11
  25. package/dist/core/ModelSwapService.d.ts.map +1 -1
  26. package/dist/core/ModelSwapService.js +59 -21
  27. package/dist/core/ModelSwapService.js.map +1 -1
  28. package/dist/core/PeerPresencePuller.d.ts +9 -0
  29. package/dist/core/PeerPresencePuller.d.ts.map +1 -1
  30. package/dist/core/PeerPresencePuller.js +1 -1
  31. package/dist/core/PeerPresencePuller.js.map +1 -1
  32. package/dist/core/PostUpdateMigrator.d.ts +16 -0
  33. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  34. package/dist/core/PostUpdateMigrator.js +92 -4
  35. package/dist/core/PostUpdateMigrator.js.map +1 -1
  36. package/dist/core/SessionManager.d.ts +4 -0
  37. package/dist/core/SessionManager.d.ts.map +1 -1
  38. package/dist/core/SessionManager.js +3 -0
  39. package/dist/core/SessionManager.js.map +1 -1
  40. package/dist/core/SessionRefresh.d.ts +36 -7
  41. package/dist/core/SessionRefresh.d.ts.map +1 -1
  42. package/dist/core/SessionRefresh.js +90 -29
  43. package/dist/core/SessionRefresh.js.map +1 -1
  44. package/dist/core/TopicProfileOrchestrator.d.ts +480 -0
  45. package/dist/core/TopicProfileOrchestrator.d.ts.map +1 -0
  46. package/dist/core/TopicProfileOrchestrator.js +1404 -0
  47. package/dist/core/TopicProfileOrchestrator.js.map +1 -0
  48. package/dist/core/TopicProfileResolver.d.ts +104 -0
  49. package/dist/core/TopicProfileResolver.d.ts.map +1 -0
  50. package/dist/core/TopicProfileResolver.js +231 -0
  51. package/dist/core/TopicProfileResolver.js.map +1 -0
  52. package/dist/core/TopicProfileStore.d.ts +308 -0
  53. package/dist/core/TopicProfileStore.d.ts.map +1 -0
  54. package/dist/core/TopicProfileStore.js +781 -0
  55. package/dist/core/TopicProfileStore.js.map +1 -0
  56. package/dist/core/TopicProfileTransferCarrier.d.ts +227 -0
  57. package/dist/core/TopicProfileTransferCarrier.d.ts.map +1 -0
  58. package/dist/core/TopicProfileTransferCarrier.js +533 -0
  59. package/dist/core/TopicProfileTransferCarrier.js.map +1 -0
  60. package/dist/core/TopicResumeMap.d.ts +67 -1
  61. package/dist/core/TopicResumeMap.d.ts.map +1 -1
  62. package/dist/core/TopicResumeMap.js +114 -1
  63. package/dist/core/TopicResumeMap.js.map +1 -1
  64. package/dist/core/classifyProfileChange.d.ts +83 -0
  65. package/dist/core/classifyProfileChange.d.ts.map +1 -0
  66. package/dist/core/classifyProfileChange.js +274 -0
  67. package/dist/core/classifyProfileChange.js.map +1 -0
  68. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  69. package/dist/core/devGatedFeatures.js +6 -0
  70. package/dist/core/devGatedFeatures.js.map +1 -1
  71. package/dist/core/frameworkSessionLaunch.d.ts +26 -0
  72. package/dist/core/frameworkSessionLaunch.d.ts.map +1 -1
  73. package/dist/core/frameworkSessionLaunch.js +58 -0
  74. package/dist/core/frameworkSessionLaunch.js.map +1 -1
  75. package/dist/core/slackRefreshBinding.d.ts +83 -0
  76. package/dist/core/slackRefreshBinding.d.ts.map +1 -0
  77. package/dist/core/slackRefreshBinding.js +54 -0
  78. package/dist/core/slackRefreshBinding.js.map +1 -0
  79. package/dist/core/topicProfileIngress.d.ts +128 -0
  80. package/dist/core/topicProfileIngress.d.ts.map +1 -0
  81. package/dist/core/topicProfileIngress.js +249 -0
  82. package/dist/core/topicProfileIngress.js.map +1 -0
  83. package/dist/core/topicProfileValidation.d.ts +109 -0
  84. package/dist/core/topicProfileValidation.d.ts.map +1 -0
  85. package/dist/core/topicProfileValidation.js +247 -0
  86. package/dist/core/topicProfileValidation.js.map +1 -0
  87. package/dist/core/topicProfileWriteSurface.d.ts +222 -0
  88. package/dist/core/topicProfileWriteSurface.d.ts.map +1 -0
  89. package/dist/core/topicProfileWriteSurface.js +631 -0
  90. package/dist/core/topicProfileWriteSurface.js.map +1 -0
  91. package/dist/core/types.d.ts +66 -0
  92. package/dist/core/types.d.ts.map +1 -1
  93. package/dist/core/types.js.map +1 -1
  94. package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
  95. package/dist/lifeline/TelegramLifeline.js +6 -0
  96. package/dist/lifeline/TelegramLifeline.js.map +1 -1
  97. package/dist/messaging/TelegramAdapter.d.ts +10 -1
  98. package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
  99. package/dist/messaging/TelegramAdapter.js +41 -1
  100. package/dist/messaging/TelegramAdapter.js.map +1 -1
  101. package/dist/monitoring/ActiveWorkSilenceSentinel.d.ts +8 -0
  102. package/dist/monitoring/ActiveWorkSilenceSentinel.d.ts.map +1 -1
  103. package/dist/monitoring/ActiveWorkSilenceSentinel.js +8 -0
  104. package/dist/monitoring/ActiveWorkSilenceSentinel.js.map +1 -1
  105. package/dist/monitoring/ContextWedgeSentinel.d.ts +8 -0
  106. package/dist/monitoring/ContextWedgeSentinel.d.ts.map +1 -1
  107. package/dist/monitoring/ContextWedgeSentinel.js +8 -0
  108. package/dist/monitoring/ContextWedgeSentinel.js.map +1 -1
  109. package/dist/monitoring/GuardPostureTripwire.d.ts +3 -27
  110. package/dist/monitoring/GuardPostureTripwire.d.ts.map +1 -1
  111. package/dist/monitoring/GuardPostureTripwire.js +8 -76
  112. package/dist/monitoring/GuardPostureTripwire.js.map +1 -1
  113. package/dist/monitoring/GuardRegistry.d.ts +48 -0
  114. package/dist/monitoring/GuardRegistry.d.ts.map +1 -0
  115. package/dist/monitoring/GuardRegistry.js +49 -0
  116. package/dist/monitoring/GuardRegistry.js.map +1 -0
  117. package/dist/monitoring/SessionReaper.d.ts +7 -0
  118. package/dist/monitoring/SessionReaper.d.ts.map +1 -1
  119. package/dist/monitoring/SessionReaper.js +9 -0
  120. package/dist/monitoring/SessionReaper.js.map +1 -1
  121. package/dist/monitoring/SessionWatchdog.d.ts +8 -0
  122. package/dist/monitoring/SessionWatchdog.d.ts.map +1 -1
  123. package/dist/monitoring/SessionWatchdog.js +8 -0
  124. package/dist/monitoring/SessionWatchdog.js.map +1 -1
  125. package/dist/monitoring/SocketDisconnectSentinel.d.ts +8 -0
  126. package/dist/monitoring/SocketDisconnectSentinel.d.ts.map +1 -1
  127. package/dist/monitoring/SocketDisconnectSentinel.js +8 -0
  128. package/dist/monitoring/SocketDisconnectSentinel.js.map +1 -1
  129. package/dist/monitoring/guardManifest.d.ts +67 -0
  130. package/dist/monitoring/guardManifest.d.ts.map +1 -0
  131. package/dist/monitoring/guardManifest.js +536 -0
  132. package/dist/monitoring/guardManifest.js.map +1 -0
  133. package/dist/monitoring/guardPosture.d.ts +77 -0
  134. package/dist/monitoring/guardPosture.d.ts.map +1 -0
  135. package/dist/monitoring/guardPosture.js +198 -0
  136. package/dist/monitoring/guardPosture.js.map +1 -0
  137. package/dist/monitoring/guardPostureView.d.ts +100 -0
  138. package/dist/monitoring/guardPostureView.d.ts.map +1 -0
  139. package/dist/monitoring/guardPostureView.js +294 -0
  140. package/dist/monitoring/guardPostureView.js.map +1 -0
  141. package/dist/monitoring/probes/GuardPostureProbe.d.ts +82 -0
  142. package/dist/monitoring/probes/GuardPostureProbe.d.ts.map +1 -0
  143. package/dist/monitoring/probes/GuardPostureProbe.js +384 -0
  144. package/dist/monitoring/probes/GuardPostureProbe.js.map +1 -0
  145. package/dist/scaffold/templates.d.ts.map +1 -1
  146. package/dist/scaffold/templates.js +18 -0
  147. package/dist/scaffold/templates.js.map +1 -1
  148. package/dist/scheduler/JobScheduler.d.ts +10 -0
  149. package/dist/scheduler/JobScheduler.d.ts.map +1 -1
  150. package/dist/scheduler/JobScheduler.js +12 -0
  151. package/dist/scheduler/JobScheduler.js.map +1 -1
  152. package/dist/server/AgentServer.d.ts +40 -0
  153. package/dist/server/AgentServer.d.ts.map +1 -1
  154. package/dist/server/AgentServer.js +54 -3
  155. package/dist/server/AgentServer.js.map +1 -1
  156. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  157. package/dist/server/CapabilityIndex.js +12 -0
  158. package/dist/server/CapabilityIndex.js.map +1 -1
  159. package/dist/server/peerUrlGuard.d.ts +35 -0
  160. package/dist/server/peerUrlGuard.d.ts.map +1 -0
  161. package/dist/server/peerUrlGuard.js +93 -0
  162. package/dist/server/peerUrlGuard.js.map +1 -0
  163. package/dist/server/routes.d.ts +31 -0
  164. package/dist/server/routes.d.ts.map +1 -1
  165. package/dist/server/routes.js +407 -1
  166. package/dist/server/routes.js.map +1 -1
  167. package/package.json +3 -2
  168. package/scripts/lint-guard-manifest.js +264 -0
  169. package/src/data/builtin-manifest.json +67 -67
  170. package/src/data/state-coherence-registry.json +54 -18
  171. package/src/scaffold/templates.ts +18 -0
  172. package/upgrades/1.3.490.md +34 -0
  173. package/upgrades/1.3.491.md +37 -0
  174. package/upgrades/side-effects/guard-posture-endpoint.md +101 -0
  175. package/upgrades/side-effects/topic-profile.md +142 -0
@@ -0,0 +1,249 @@
1
+ /**
2
+ * topicProfileIngress — the §10.1 SERVER-SIDE conversational ingress for
3
+ * Topic Profile writes (TOPIC-PROFILE-SPEC §10.1, §8 'switch now', §10.4
4
+ * cooldown confirm).
5
+ *
6
+ * Two pieces, both pure logic (no I/O — the write surface owns side effects):
7
+ *
8
+ * 1. The deterministic TRIGGER GRAMMAR (`parseProfileTrigger`). The parse
9
+ * runs in the message-ingress pipeline where the authenticated sender uid
10
+ * is first-party — the uid reaches the store through code, never through
11
+ * a body the agent composed. The grammar is a CLOSED set (Tier 0, §14):
12
+ * no fuzzy matcher ever holds write authority. Out-of-grammar phrasings
13
+ * ride the propose-then-confirm lane instead.
14
+ *
15
+ * 2. The ARMED-CONFIRM slot manager (`ProfileConfirmSlots`). ALL THREE
16
+ * confirm surfaces — §10.1 propose-confirm, §8 switch-now, §10.4
17
+ * re-apply-cooldown — share ONE armed slot per topic (round-7: a bare
18
+ * "yes" must never be ambiguous about which confirm it fires). The slot
19
+ * enforces:
20
+ * - TTL (`switchNowConfirmTtlMs`),
21
+ * - event-based ordering on PLATFORM MESSAGE IDS (the confirm's id must
22
+ * be > the latest echo's platform-returned id; ties refused toward
23
+ * re-echo — never a cross-clock timestamp compare, round-7),
24
+ * - supersession (a re-proposal invalidates any in-flight confirm; the
25
+ * confirm must answer the LATEST echo),
26
+ * - a re-proposal rate bound per topic (churn is a suspicion signal —
27
+ * past the bound the slot refuses re-proposals for a cooldown and
28
+ * tears down, round-7),
29
+ * - forwarded-content rejection (a message carrying platform forward
30
+ * metadata never matches ANY ingress recognition, round-5).
31
+ */
32
+ const FRAMEWORK_WORDS = {
33
+ codex: 'codex-cli',
34
+ 'codex-cli': 'codex-cli',
35
+ claude: 'claude-code',
36
+ 'claude-code': 'claude-code',
37
+ gemini: 'gemini-cli',
38
+ 'gemini-cli': 'gemini-cli',
39
+ pi: 'pi-cli',
40
+ 'pi-cli': 'pi-cli',
41
+ };
42
+ const THINKING_WORDS = ['off', 'low', 'medium', 'high', 'max'];
43
+ /**
44
+ * Parse a first-party operator turn against the closed trigger set.
45
+ * Returns null for anything outside the grammar — the message then routes
46
+ * to the session normally (the agent may propose via the §10.1
47
+ * propose-confirm lane). Deterministic and anchored: a trigger is the WHOLE
48
+ * message (trailing punctuation tolerated), never a substring match inside
49
+ * prose — quoting someone else's "use codex here" mid-paragraph must not
50
+ * fire a write.
51
+ */
52
+ export function parseProfileTrigger(text) {
53
+ const t = text.trim().replace(/[.!]+$/, '').trim().toLowerCase();
54
+ if (t.length === 0 || t.length > 120)
55
+ return null;
56
+ // framework switch: "use codex here" / "use codex for this topic" /
57
+ // "switch this topic to codex" / "switch to codex here"
58
+ let m = t.match(/^use (codex|codex-cli|claude|claude-code|gemini|gemini-cli|pi|pi-cli) (?:here|for this topic|in this topic)$/);
59
+ if (!m)
60
+ m = t.match(/^switch this topic to (codex|codex-cli|claude|claude-code|gemini|gemini-cli|pi|pi-cli)$/);
61
+ if (!m)
62
+ m = t.match(/^switch to (codex|codex-cli|claude|claude-code|gemini|gemini-cli|pi|pi-cli) (?:here|for this topic)$/);
63
+ if (m)
64
+ return { kind: 'write', patch: { framework: FRAMEWORK_WORDS[m[1]] ?? m[1] } };
65
+ // model pin: "pin this topic to <id>" / "use model <id> here".
66
+ // <id> is the literal id charset only — names ("Fable") are out-of-grammar
67
+ // by design and ride propose-confirm (Tier 0: no alias resolution here).
68
+ m = t.match(/^pin this topic to ([a-z0-9._-]{1,64})$/);
69
+ if (!m)
70
+ m = t.match(/^use model ([a-z0-9._-]{1,64}) (?:here|for this topic)$/);
71
+ if (m) {
72
+ if (m[1] === 'default' || m[1] === 'escalated') {
73
+ return { kind: 'write', patch: { modelTier: m[1], model: null } };
74
+ }
75
+ return { kind: 'write', patch: { model: m[1], modelTier: null } };
76
+ }
77
+ // thinking mode: "set high thinking on this topic" / "set thinking to high
78
+ // on this topic" / "use high thinking here"
79
+ m = t.match(/^set (off|low|medium|high|max) thinking (?:on this topic|here)$/);
80
+ if (!m)
81
+ m = t.match(/^set thinking to (off|low|medium|high|max)(?: on this topic| here)?$/);
82
+ if (!m)
83
+ m = t.match(/^use (off|low|medium|high|max) thinking (?:here|on this topic)$/);
84
+ if (m && THINKING_WORDS.includes(m[1])) {
85
+ return { kind: 'write', patch: { thinkingMode: m[1] } };
86
+ }
87
+ // escalation override — `suppress` requires an UNAMBIGUOUS explicit
88
+ // instruction (§9: any ambiguity defaults to inherit; no fuzzy step ever
89
+ // weakens the mandate). These are the explicit forms only.
90
+ if (/^(?:don'?t|do not|never) (?:auto-)?escalate this topic$/.test(t)
91
+ || /^no auto-escalation (?:here|on this topic)$/.test(t)) {
92
+ return { kind: 'write', patch: { escalationOverride: 'suppress' } };
93
+ }
94
+ if (/^(?:re-?enable|resume) (?:auto-)?escalation (?:here|on this topic)$/.test(t)
95
+ || /^escalate this topic normally$/.test(t)) {
96
+ return { kind: 'write', patch: { escalationOverride: 'inherit' } };
97
+ }
98
+ // readout
99
+ if (/^what(?:'s| is) this topic (?:pinned to|running on|using)$/.test(t)
100
+ || /^show (?:this topic'?s? )?(?:topic )?profile$/.test(t)) {
101
+ return { kind: 'readout' };
102
+ }
103
+ // undo — profile-scoped phrasings only (a bare "undo" is normal
104
+ // conversation and must reach the session untouched), plus the §10.1
105
+ // detection-loop phrase verbatim.
106
+ if (/^undo (?:that|the last|this) (?:topic |profile )?(?:profile |topic )?change$/.test(t)
107
+ || /^undo the (?:topic )?profile change$/.test(t)
108
+ || /^that wasn'?t me[\s,—–-]+undo$/.test(t)) {
109
+ return { kind: 'undo' };
110
+ }
111
+ // clear
112
+ if (/^(?:clear|reset) (?:this topic'?s? profile|the profile (?:on|for) this topic)$/.test(t)) {
113
+ return { kind: 'clear' };
114
+ }
115
+ // re-apply (the §10.4 revert notice promises exactly "say re-apply")
116
+ if (/^re-?apply(?: (?:the|that|my) (?:parked )?(?:pin|profile))?$/.test(t)) {
117
+ return { kind: 'reapply' };
118
+ }
119
+ // switch now (§8 — overrides busy/autonomous deferral, never protection)
120
+ if (/^switch now$/.test(t))
121
+ return { kind: 'switch-now' };
122
+ // bare affirmative — consumed ONLY when an armed confirm slot matches;
123
+ // otherwise the caller routes it to the session as normal conversation.
124
+ if (/^(?:yes|yep|yeah|confirm|confirmed|do it|go ahead)$/.test(t)) {
125
+ return { kind: 'confirm' };
126
+ }
127
+ return null;
128
+ }
129
+ const DEFAULT_TTL_MS = 300_000;
130
+ const DEFAULT_MAX_PROPOSALS = 5;
131
+ const DEFAULT_PROPOSAL_WINDOW_MS = 600_000;
132
+ export class ProfileConfirmSlots {
133
+ slots = new Map();
134
+ /** Re-proposal timestamps per topic (rate bound, round-7). */
135
+ proposalTimes = new Map();
136
+ /** Topics cooling down after a proposal-churn trip. */
137
+ churnCooldownUntil = new Map();
138
+ opts;
139
+ now;
140
+ constructor(opts = {}) {
141
+ this.opts = {
142
+ maxProposalsPerWindow: opts.maxProposalsPerWindow ?? DEFAULT_MAX_PROPOSALS,
143
+ proposalWindowMs: opts.proposalWindowMs ?? DEFAULT_PROPOSAL_WINDOW_MS,
144
+ ...opts,
145
+ };
146
+ this.now = opts.now ?? (() => Date.now());
147
+ }
148
+ ttl() {
149
+ return this.opts.ttlMs?.() ?? DEFAULT_TTL_MS;
150
+ }
151
+ /**
152
+ * Arm (or supersede) the topic's ONE confirm slot. Returns ok:false when
153
+ * the topic is rate-bounded on re-proposals (the armed proposal is torn
154
+ * down and the operator re-states fresh — round-7).
155
+ */
156
+ arm(topicKey, kind, patch, echoText, origin) {
157
+ const key = String(topicKey);
158
+ const now = this.now();
159
+ const coolUntil = this.churnCooldownUntil.get(key);
160
+ if (coolUntil !== undefined && now < coolUntil) {
161
+ return { ok: false, reason: 'proposal-churn-cooldown' };
162
+ }
163
+ const times = (this.proposalTimes.get(key) ?? []).filter((t) => now - t < this.opts.proposalWindowMs);
164
+ times.push(now);
165
+ this.proposalTimes.set(key, times);
166
+ if (times.length > this.opts.maxProposalsPerWindow) {
167
+ // Churn trip: tear the slot down + refuse further re-proposals for a
168
+ // cooldown. Audited as a suspicion signal.
169
+ this.slots.delete(key);
170
+ this.churnCooldownUntil.set(key, now + this.opts.proposalWindowMs);
171
+ this.opts.audit?.({ type: 'proposal-churn-trip', topic: key, count: times.length });
172
+ return { ok: false, reason: 'proposal-churn-cooldown' };
173
+ }
174
+ const superseded = this.slots.has(key);
175
+ this.slots.set(key, {
176
+ kind,
177
+ topicKey: key,
178
+ patch,
179
+ echoText,
180
+ echoPlatformMessageId: null,
181
+ armedAt: now,
182
+ origin,
183
+ });
184
+ this.opts.audit?.({
185
+ type: 'confirm-armed',
186
+ topic: key,
187
+ kind,
188
+ origin,
189
+ superseded,
190
+ });
191
+ return { ok: true, superseded };
192
+ }
193
+ /** Record the platform-returned id of the echo we just sent (ordering anchor). */
194
+ recordEchoMessageId(topicKey, platformMessageId) {
195
+ const slot = this.slots.get(String(topicKey));
196
+ if (slot)
197
+ slot.echoPlatformMessageId = platformMessageId;
198
+ }
199
+ /** The armed slot, if any (readout / switch-now no-op reply). */
200
+ peek(topicKey) {
201
+ return this.slots.get(String(topicKey)) ?? null;
202
+ }
203
+ disarm(topicKey) {
204
+ this.slots.delete(String(topicKey));
205
+ }
206
+ /**
207
+ * Match an inbound affirmative against the armed slot. On ok:true the slot
208
+ * is CONSUMED (one confirm per echo). Refusals leave the slot armed except
209
+ * 'expired' (torn down).
210
+ */
211
+ matchConfirm(topicKey, ctx) {
212
+ const key = String(topicKey);
213
+ // Forwarded content never matches ANY ingress recognition (round-5).
214
+ if (ctx.forwarded)
215
+ return { ok: false, reason: 'forwarded' };
216
+ const slot = this.slots.get(key);
217
+ if (!slot)
218
+ return { ok: false, reason: 'none-armed' };
219
+ if (this.now() - slot.armedAt > this.ttl()) {
220
+ this.slots.delete(key);
221
+ this.opts.audit?.({ type: 'confirm-expired', topic: key, kind: slot.kind });
222
+ return { ok: false, reason: 'expired' };
223
+ }
224
+ // Event-based ordering on platform message ids (round-7): the confirm
225
+ // must postdate the LATEST echo in the same conversation. An unknown
226
+ // echo id refuses toward re-echo (we cannot prove the operator saw the
227
+ // latest version); ties refuse the same way.
228
+ if (slot.echoPlatformMessageId === null) {
229
+ return { ok: false, reason: 'no-echo-id' };
230
+ }
231
+ if (ctx.platformMessageId === null || ctx.platformMessageId <= slot.echoPlatformMessageId) {
232
+ return { ok: false, reason: 'stale-order' };
233
+ }
234
+ this.slots.delete(key);
235
+ this.opts.audit?.({ type: 'confirm-fired', topic: key, kind: slot.kind, origin: slot.origin });
236
+ return { ok: true, armed: slot };
237
+ }
238
+ }
239
+ /** Parse the numeric platform message id from a Message.id like `tg-12345`. */
240
+ export function platformMessageIdFrom(messageId) {
241
+ if (!messageId)
242
+ return null;
243
+ const m = /^tg-(\d+)$/.exec(messageId);
244
+ if (!m)
245
+ return null;
246
+ const n = Number(m[1]);
247
+ return Number.isSafeInteger(n) ? n : null;
248
+ }
249
+ //# sourceMappingURL=topicProfileIngress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topicProfileIngress.js","sourceRoot":"","sources":["../../src/core/topicProfileIngress.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * topicProfileValidation — closed-enum clamps for every Topic Profile field.
3
+ *
4
+ * TOPIC-PROFILE-SPEC §10.2: EVERY profile field is clamped to its closed enum
5
+ * server-side BEFORE persist and before it can reach a launch arg — not just
6
+ * the model arm. The pin write is a NEW untrusted entry point that FABLE's
7
+ * config-sourced resolver did not cover, and `thinkingMode` becomes a launch
8
+ * arg (`-c model_reasoning_effort=…`), so it is an injection surface symmetric
9
+ * to the model one.
10
+ *
11
+ * The clamp runs at the RESOLUTION boundary for every source (store pin,
12
+ * config default, transferred entry) — `validateProfileFields` is pure and
13
+ * callable from all three. Pure logic, no I/O.
14
+ */
15
+ import type { IntelligenceFramework } from './intelligenceProviderFactory.js';
16
+ export declare const THINKING_MODES: readonly ["off", "low", "medium", "high", "max"];
17
+ export type ThinkingMode = (typeof THINKING_MODES)[number];
18
+ export declare const ESCALATION_OVERRIDES: readonly ["inherit", "suppress"];
19
+ export type EscalationOverride = (typeof ESCALATION_OVERRIDES)[number];
20
+ export declare const MODEL_TIERS: readonly ["default", "escalated"];
21
+ export type ProfileModelTier = (typeof MODEL_TIERS)[number];
22
+ /** Failure classes for the §10.3 audit — never the raw value verbatim. */
23
+ export type ProfileFieldFailure = 'off-enum' | 'regex' | 'length' | 'cross-framework-id' | 'model-and-tier-both-set' | 'per-token-lane' | 'unknown-field';
24
+ /**
25
+ * §10.2 billing lane — model ids whose lane would be PER-TOKEN (API-billed,
26
+ * outside the subscription envelope), refused at validation. `knownModelIds`
27
+ * membership proves an id is *recognized*, not that it is inside the
28
+ * subscription envelope; a pin can never introduce a per-token API path
29
+ * (§10.3 — the cost gate's write-time arm).
30
+ *
31
+ * The table is a closed allow/deny classification per framework. TODAY every
32
+ * member of the closed enums rides the subscription envelope (the codex enum
33
+ * mirrors CODEX_MODELS_SUBSCRIPTION by construction; the claude enum is the
34
+ * CLI's subscription-login surface), so the deny sets ship empty — the seam
35
+ * exists so the moment a per-token id joins a known-ids enum it is named here
36
+ * and refused, instead of slipping into a launch arg as billable API spend.
37
+ * If this table and a framework's enum ever disagree, the REFUSAL wins.
38
+ */
39
+ export declare const PER_TOKEN_LANE_MODEL_IDS: Partial<Record<IntelligenceFramework, readonly string[]>>;
40
+ /**
41
+ * §10.2 — refuse a model id whose billing lane would be per-token. Returns
42
+ * null when the id rides the subscription envelope. `perTokenIds` is
43
+ * injectable for tests (both sides of the boundary stay coverable while the
44
+ * shipped deny sets are empty).
45
+ */
46
+ export declare function billingLaneError(id: string, framework: IntelligenceFramework, perTokenIds?: readonly string[]): ProfileValidationError | null;
47
+ export interface ProfileValidationError {
48
+ field: string;
49
+ failure: ProfileFieldFailure;
50
+ /** Human-readable named reason (operator-facing refusal text). */
51
+ reason: string;
52
+ /** Hard-truncated, charset-clamped prefix of the rejected value (§10.3). */
53
+ rejectedPrefix?: string;
54
+ /** Length of the rejected raw value (§10.3 audit field). */
55
+ rejectedLength?: number;
56
+ }
57
+ export interface ProfilePatchInput {
58
+ framework?: string | null;
59
+ model?: string | null;
60
+ modelTier?: string | null;
61
+ escalationOverride?: string | null;
62
+ thinkingMode?: string | null;
63
+ }
64
+ export interface ValidatedProfilePatch {
65
+ framework?: IntelligenceFramework | null;
66
+ model?: string | null;
67
+ modelTier?: ProfileModelTier | null;
68
+ escalationOverride?: EscalationOverride | null;
69
+ thinkingMode?: ThinkingMode | null;
70
+ }
71
+ /** §10.3 — a rejected value is arbitrary text; store only a clamped prefix. */
72
+ export declare function clampRejectedValue(value: unknown): {
73
+ prefix: string;
74
+ length: number;
75
+ };
76
+ /**
77
+ * Validate a profile patch's fields against the closed enums.
78
+ *
79
+ * `effectiveFramework` is the framework the model id will be validated
80
+ * against — the patch's own framework when it sets one, otherwise the
81
+ * topic's currently-resolved framework (caller supplies it).
82
+ *
83
+ * Returns either the validated (narrowed) patch or the FIRST error — a
84
+ * refusal leaves the profile unchanged (§4), so one named reason suffices.
85
+ * `null` field values are CLEAR requests and always valid; `undefined`
86
+ * fields are absent from the patch.
87
+ */
88
+ export declare function validateProfileFields(patch: ProfilePatchInput, effectiveFramework: IntelligenceFramework): {
89
+ ok: true;
90
+ patch: ValidatedProfilePatch;
91
+ } | {
92
+ ok: false;
93
+ error: ProfileValidationError;
94
+ };
95
+ /**
96
+ * §10.2 model-id clamp: regex shape + membership in the framework's closed
97
+ * `knownModelIds` enumeration. Returns null when valid.
98
+ */
99
+ export declare function validateModelId(id: string, framework: IntelligenceFramework): ProfileValidationError | null;
100
+ /**
101
+ * §4 — model and modelTier are mutually exclusive; setting both (or a patch
102
+ * whose merge RESULT would hold both) is a HARD refusal, never a silent
103
+ * winner. Checked against the post-merge shape by the store.
104
+ */
105
+ export declare function modelTierMutualExclusionError(merged: {
106
+ model?: string | null;
107
+ modelTier?: string | null;
108
+ }): ProfileValidationError | null;
109
+ //# sourceMappingURL=topicProfileValidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topicProfileValidation.d.ts","sourceRoot":"","sources":["../../src/core/topicProfileValidation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,247 @@
1
+ /**
2
+ * topicProfileValidation — closed-enum clamps for every Topic Profile field.
3
+ *
4
+ * TOPIC-PROFILE-SPEC §10.2: EVERY profile field is clamped to its closed enum
5
+ * server-side BEFORE persist and before it can reach a launch arg — not just
6
+ * the model arm. The pin write is a NEW untrusted entry point that FABLE's
7
+ * config-sourced resolver did not cover, and `thinkingMode` becomes a launch
8
+ * arg (`-c model_reasoning_effort=…`), so it is an injection surface symmetric
9
+ * to the model one.
10
+ *
11
+ * The clamp runs at the RESOLUTION boundary for every source (store pin,
12
+ * config default, transferred entry) — `validateProfileFields` is pure and
13
+ * callable from all three. Pure logic, no I/O.
14
+ */
15
+ import { KNOWN_MODEL_IDS, MODEL_ID_RE, escapeIdForAudit } from './ModelTierEscalation.js';
16
+ import { SUPPORTED_FRAMEWORKS } from './TopicFrameworksStore.js';
17
+ export const THINKING_MODES = ['off', 'low', 'medium', 'high', 'max'];
18
+ export const ESCALATION_OVERRIDES = ['inherit', 'suppress'];
19
+ export const MODEL_TIERS = ['default', 'escalated'];
20
+ /**
21
+ * §10.2 billing lane — model ids whose lane would be PER-TOKEN (API-billed,
22
+ * outside the subscription envelope), refused at validation. `knownModelIds`
23
+ * membership proves an id is *recognized*, not that it is inside the
24
+ * subscription envelope; a pin can never introduce a per-token API path
25
+ * (§10.3 — the cost gate's write-time arm).
26
+ *
27
+ * The table is a closed allow/deny classification per framework. TODAY every
28
+ * member of the closed enums rides the subscription envelope (the codex enum
29
+ * mirrors CODEX_MODELS_SUBSCRIPTION by construction; the claude enum is the
30
+ * CLI's subscription-login surface), so the deny sets ship empty — the seam
31
+ * exists so the moment a per-token id joins a known-ids enum it is named here
32
+ * and refused, instead of slipping into a launch arg as billable API spend.
33
+ * If this table and a framework's enum ever disagree, the REFUSAL wins.
34
+ */
35
+ export const PER_TOKEN_LANE_MODEL_IDS = {
36
+ // All KNOWN_CLAUDE_MODEL_IDS launch via the subscription-authed CLI today.
37
+ 'claude-code': [],
38
+ // KNOWN_CODEX_MODEL_IDS mirrors the CODEX_MODELS_SUBSCRIPTION allowlist.
39
+ 'codex-cli': [],
40
+ // gemini-cli runs the OAuth (code-assist) lane, not an API key, today.
41
+ 'gemini-cli': [],
42
+ // pi-cli's enum is closed-empty — nothing to classify.
43
+ 'pi-cli': [],
44
+ };
45
+ /**
46
+ * §10.2 — refuse a model id whose billing lane would be per-token. Returns
47
+ * null when the id rides the subscription envelope. `perTokenIds` is
48
+ * injectable for tests (both sides of the boundary stay coverable while the
49
+ * shipped deny sets are empty).
50
+ */
51
+ export function billingLaneError(id, framework, perTokenIds = PER_TOKEN_LANE_MODEL_IDS[framework] ?? []) {
52
+ if (perTokenIds.includes(id)) {
53
+ return {
54
+ field: 'model',
55
+ failure: 'per-token-lane',
56
+ reason: `'${escapeIdForAudit(id)}' bills per-token (outside the subscription envelope) — ` +
57
+ `a topic pin can't introduce an API-billed path`,
58
+ rejectedPrefix: escapeIdForAudit(id).slice(0, 32),
59
+ rejectedLength: id.length,
60
+ };
61
+ }
62
+ return null;
63
+ }
64
+ /** §10.3 — a rejected value is arbitrary text; store only a clamped prefix. */
65
+ export function clampRejectedValue(value) {
66
+ const raw = typeof value === 'string' ? value : JSON.stringify(value) ?? '';
67
+ const safe = raw.replace(/[^A-Za-z0-9._\- ]/g, '?');
68
+ return { prefix: safe.slice(0, 32), length: raw.length };
69
+ }
70
+ /**
71
+ * Validate a profile patch's fields against the closed enums.
72
+ *
73
+ * `effectiveFramework` is the framework the model id will be validated
74
+ * against — the patch's own framework when it sets one, otherwise the
75
+ * topic's currently-resolved framework (caller supplies it).
76
+ *
77
+ * Returns either the validated (narrowed) patch or the FIRST error — a
78
+ * refusal leaves the profile unchanged (§4), so one named reason suffices.
79
+ * `null` field values are CLEAR requests and always valid; `undefined`
80
+ * fields are absent from the patch.
81
+ */
82
+ export function validateProfileFields(patch, effectiveFramework) {
83
+ const out = {};
84
+ if (patch.framework !== undefined) {
85
+ if (patch.framework === null) {
86
+ out.framework = null;
87
+ }
88
+ else if (SUPPORTED_FRAMEWORKS.includes(patch.framework)) {
89
+ out.framework = patch.framework;
90
+ }
91
+ else {
92
+ const { prefix, length } = clampRejectedValue(patch.framework);
93
+ return {
94
+ ok: false,
95
+ error: {
96
+ field: 'framework',
97
+ failure: 'off-enum',
98
+ reason: `framework must be one of: ${SUPPORTED_FRAMEWORKS.join(', ')}`,
99
+ rejectedPrefix: prefix,
100
+ rejectedLength: length,
101
+ },
102
+ };
103
+ }
104
+ }
105
+ if (patch.modelTier !== undefined) {
106
+ if (patch.modelTier === null) {
107
+ out.modelTier = null;
108
+ }
109
+ else if (MODEL_TIERS.includes(patch.modelTier)) {
110
+ out.modelTier = patch.modelTier;
111
+ }
112
+ else {
113
+ const { prefix, length } = clampRejectedValue(patch.modelTier);
114
+ return {
115
+ ok: false,
116
+ error: {
117
+ field: 'modelTier',
118
+ failure: 'off-enum',
119
+ reason: `modelTier must be 'default' or 'escalated'`,
120
+ rejectedPrefix: prefix,
121
+ rejectedLength: length,
122
+ },
123
+ };
124
+ }
125
+ }
126
+ if (patch.model !== undefined) {
127
+ if (patch.model === null) {
128
+ out.model = null;
129
+ }
130
+ else {
131
+ const modelError = validateModelId(patch.model, modelFramework(patch, effectiveFramework));
132
+ if (modelError)
133
+ return { ok: false, error: modelError };
134
+ out.model = patch.model;
135
+ }
136
+ }
137
+ if (patch.thinkingMode !== undefined) {
138
+ if (patch.thinkingMode === null) {
139
+ out.thinkingMode = null;
140
+ }
141
+ else if (THINKING_MODES.includes(patch.thinkingMode)) {
142
+ out.thinkingMode = patch.thinkingMode;
143
+ }
144
+ else {
145
+ const { prefix, length } = clampRejectedValue(patch.thinkingMode);
146
+ return {
147
+ ok: false,
148
+ error: {
149
+ field: 'thinkingMode',
150
+ failure: 'off-enum',
151
+ reason: `thinkingMode must be one of: ${THINKING_MODES.join(', ')}`,
152
+ rejectedPrefix: prefix,
153
+ rejectedLength: length,
154
+ },
155
+ };
156
+ }
157
+ }
158
+ if (patch.escalationOverride !== undefined) {
159
+ if (patch.escalationOverride === null) {
160
+ out.escalationOverride = null;
161
+ }
162
+ else if (ESCALATION_OVERRIDES.includes(patch.escalationOverride)) {
163
+ out.escalationOverride = patch.escalationOverride;
164
+ }
165
+ else {
166
+ const { prefix, length } = clampRejectedValue(patch.escalationOverride);
167
+ return {
168
+ ok: false,
169
+ error: {
170
+ field: 'escalationOverride',
171
+ failure: 'off-enum',
172
+ reason: `escalationOverride must be 'inherit' or 'suppress'`,
173
+ rejectedPrefix: prefix,
174
+ rejectedLength: length,
175
+ },
176
+ };
177
+ }
178
+ }
179
+ return { ok: true, patch: out };
180
+ }
181
+ /** The framework a model id in this patch should validate against. */
182
+ function modelFramework(patch, effectiveFramework) {
183
+ if (patch.framework && SUPPORTED_FRAMEWORKS.includes(patch.framework)) {
184
+ return patch.framework;
185
+ }
186
+ return effectiveFramework;
187
+ }
188
+ /**
189
+ * §10.2 model-id clamp: regex shape + membership in the framework's closed
190
+ * `knownModelIds` enumeration. Returns null when valid.
191
+ */
192
+ export function validateModelId(id, framework) {
193
+ if (typeof id !== 'string' || id.length === 0 || id.length > 64) {
194
+ const { prefix, length } = clampRejectedValue(id);
195
+ return {
196
+ field: 'model',
197
+ failure: 'length',
198
+ reason: 'model id must be a non-empty string of at most 64 characters',
199
+ rejectedPrefix: prefix,
200
+ rejectedLength: length,
201
+ };
202
+ }
203
+ if (!MODEL_ID_RE.test(id)) {
204
+ const { prefix, length } = clampRejectedValue(id);
205
+ return {
206
+ field: 'model',
207
+ failure: 'regex',
208
+ reason: 'model id contains characters outside the safe id charset',
209
+ rejectedPrefix: prefix,
210
+ rejectedLength: length,
211
+ };
212
+ }
213
+ const known = KNOWN_MODEL_IDS[framework] ?? [];
214
+ if (!known.includes(id)) {
215
+ return {
216
+ field: 'model',
217
+ failure: known.length === 0 ? 'cross-framework-id' : 'off-enum',
218
+ reason: known.length === 0
219
+ ? `framework '${framework}' has no pinnable model ids`
220
+ : `'${escapeIdForAudit(id)}' is not a known ${framework} model id`,
221
+ rejectedPrefix: escapeIdForAudit(id).slice(0, 32),
222
+ rejectedLength: id.length,
223
+ };
224
+ }
225
+ // §10.2 billing lane: enum membership proves the id is recognized, not that
226
+ // it is inside the subscription envelope — a per-token id is refused here.
227
+ const lane = billingLaneError(id, framework);
228
+ if (lane)
229
+ return lane;
230
+ return null;
231
+ }
232
+ /**
233
+ * §4 — model and modelTier are mutually exclusive; setting both (or a patch
234
+ * whose merge RESULT would hold both) is a HARD refusal, never a silent
235
+ * winner. Checked against the post-merge shape by the store.
236
+ */
237
+ export function modelTierMutualExclusionError(merged) {
238
+ if (merged.model != null && merged.modelTier != null) {
239
+ return {
240
+ field: 'model',
241
+ failure: 'model-and-tier-both-set',
242
+ reason: 'pick one: an explicit model or a tier — a profile cannot hold both',
243
+ };
244
+ }
245
+ return null;
246
+ }
247
+ //# sourceMappingURL=topicProfileValidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topicProfileValidation.js","sourceRoot":"","sources":["../../src/core/topicProfileValidation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAU,CAAC;AAG/E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,SAAS,EAAE,UAAU,CAAU,CAAC;AAGrE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,SAAS,EAAE,WAAW,CAAU,CAAC;AAa7D;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAA8D;IACjG,2EAA2E;IAC3E,aAAa,EAAE,EAAE;IACjB,yEAAyE;IACzE,WAAW,EAAE,EAAE;IACf,uEAAuE;IACvE,YAAY,EAAE,EAAE;IAChB,uDAAuD;IACvD,QAAQ,EAAE,EAAE;CACb,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,EAAU,EACV,SAAgC,EAChC,cAAiC,wBAAwB,CAAC,SAAS,CAAC,IAAI,EAAE;IAE1E,IAAI,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,gBAAgB;YACzB,MAAM,EACJ,IAAI,gBAAgB,CAAC,EAAE,CAAC,0DAA0D;gBAClF,gDAAgD;YAClD,cAAc,EAAE,gBAAgB,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;YACjD,cAAc,EAAE,EAAE,CAAC,MAAM;SAC1B,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AA6BD,+EAA+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC5E,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;AAC3D,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAwB,EACxB,kBAAyC;IAEzC,MAAM,GAAG,GAA0B,EAAE,CAAC;IAEtC,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAClC,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC7B,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;QACvB,CAAC;aAAM,IAAK,oBAA0C,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YACjF,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,SAAkC,CAAC;QAC3D,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAC/D,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE;oBACL,KAAK,EAAE,WAAW;oBAClB,OAAO,EAAE,UAAU;oBACnB,MAAM,EAAE,6BAA6B,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBACtE,cAAc,EAAE,MAAM;oBACtB,cAAc,EAAE,MAAM;iBACvB;aACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAClC,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC7B,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;QACvB,CAAC;aAAM,IAAK,WAAiC,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YACxE,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,SAA6B,CAAC;QACtD,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAC/D,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE;oBACL,KAAK,EAAE,WAAW;oBAClB,OAAO,EAAE,UAAU;oBACnB,MAAM,EAAE,4CAA4C;oBACpD,cAAc,EAAE,MAAM;oBACtB,cAAc,EAAE,MAAM;iBACvB;aACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACzB,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,MAAM,UAAU,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC;YAC3F,IAAI,UAAU;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;YACxD,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YAChC,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC;QAC1B,CAAC;aAAM,IAAK,cAAoC,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;YAC9E,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,YAA4B,CAAC;QACxD,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAClE,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE;oBACL,KAAK,EAAE,cAAc;oBACrB,OAAO,EAAE,UAAU;oBACnB,MAAM,EAAE,gCAAgC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBACnE,cAAc,EAAE,MAAM;oBACtB,cAAc,EAAE,MAAM;iBACvB;aACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;QAC3C,IAAI,KAAK,CAAC,kBAAkB,KAAK,IAAI,EAAE,CAAC;YACtC,GAAG,CAAC,kBAAkB,GAAG,IAAI,CAAC;QAChC,CAAC;aAAM,IAAK,oBAA0C,CAAC,QAAQ,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC1F,GAAG,CAAC,kBAAkB,GAAG,KAAK,CAAC,kBAAwC,CAAC;QAC1E,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;YACxE,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE;oBACL,KAAK,EAAE,oBAAoB;oBAC3B,OAAO,EAAE,UAAU;oBACnB,MAAM,EAAE,oDAAoD;oBAC5D,cAAc,EAAE,MAAM;oBACtB,cAAc,EAAE,MAAM;iBACvB;aACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AAClC,CAAC;AAED,sEAAsE;AACtE,SAAS,cAAc,CACrB,KAAwB,EACxB,kBAAyC;IAEzC,IAAI,KAAK,CAAC,SAAS,IAAK,oBAA0C,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7F,OAAO,KAAK,CAAC,SAAkC,CAAC;IAClD,CAAC;IACD,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,EAAU,EACV,SAAgC;IAEhC,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAChE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAAC;QAClD,OAAO;YACL,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,QAAQ;YACjB,MAAM,EAAE,8DAA8D;YACtE,cAAc,EAAE,MAAM;YACtB,cAAc,EAAE,MAAM;SACvB,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QAC1B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAAC;QAClD,OAAO;YACL,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,0DAA0D;YAClE,cAAc,EAAE,MAAM;YACtB,cAAc,EAAE,MAAM;SACvB,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,eAAe,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAC/C,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,UAAU;YAC/D,MAAM,EACJ,KAAK,CAAC,MAAM,KAAK,CAAC;gBAChB,CAAC,CAAC,cAAc,SAAS,6BAA6B;gBACtD,CAAC,CAAC,IAAI,gBAAgB,CAAC,EAAE,CAAC,oBAAoB,SAAS,WAAW;YACtE,cAAc,EAAE,gBAAgB,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;YACjD,cAAc,EAAE,EAAE,CAAC,MAAM;SAC1B,CAAC;IACJ,CAAC;IACD,4EAA4E;IAC5E,2EAA2E;IAC3E,MAAM,IAAI,GAAG,gBAAgB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAC7C,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,6BAA6B,CAAC,MAG7C;IACC,IAAI,MAAM,CAAC,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;QACrD,OAAO;YACL,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,yBAAyB;YAClC,MAAM,EAAE,oEAAoE;SAC7E,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}