instar 1.2.37 → 1.2.39

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 +19 -1
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/TopicIntent.d.ts +162 -0
  5. package/dist/core/TopicIntent.d.ts.map +1 -0
  6. package/dist/core/TopicIntent.js +355 -0
  7. package/dist/core/TopicIntent.js.map +1 -0
  8. package/dist/core/TopicIntentArcCheck.d.ts +101 -0
  9. package/dist/core/TopicIntentArcCheck.d.ts.map +1 -0
  10. package/dist/core/TopicIntentArcCheck.js +146 -0
  11. package/dist/core/TopicIntentArcCheck.js.map +1 -0
  12. package/dist/core/TopicIntentBriefing.d.ts +44 -0
  13. package/dist/core/TopicIntentBriefing.d.ts.map +1 -0
  14. package/dist/core/TopicIntentBriefing.js +88 -0
  15. package/dist/core/TopicIntentBriefing.js.map +1 -0
  16. package/dist/core/TopicIntentExtractor.d.ts +90 -0
  17. package/dist/core/TopicIntentExtractor.d.ts.map +1 -0
  18. package/dist/core/TopicIntentExtractor.js +153 -0
  19. package/dist/core/TopicIntentExtractor.js.map +1 -0
  20. package/dist/core/TopicIntentPendingConfirm.d.ts +96 -0
  21. package/dist/core/TopicIntentPendingConfirm.d.ts.map +1 -0
  22. package/dist/core/TopicIntentPendingConfirm.js +223 -0
  23. package/dist/core/TopicIntentPendingConfirm.js.map +1 -0
  24. package/dist/messaging/TelegramAdapter.d.ts +45 -0
  25. package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
  26. package/dist/messaging/TelegramAdapter.js +52 -0
  27. package/dist/messaging/TelegramAdapter.js.map +1 -1
  28. package/dist/server/AgentServer.d.ts +3 -0
  29. package/dist/server/AgentServer.d.ts.map +1 -1
  30. package/dist/server/AgentServer.js +10 -0
  31. package/dist/server/AgentServer.js.map +1 -1
  32. package/dist/server/topicIntentRoutes.d.ts +27 -0
  33. package/dist/server/topicIntentRoutes.d.ts.map +1 -0
  34. package/dist/server/topicIntentRoutes.js +189 -0
  35. package/dist/server/topicIntentRoutes.js.map +1 -0
  36. package/dist/tunnel/TunnelManager.d.ts +32 -1
  37. package/dist/tunnel/TunnelManager.d.ts.map +1 -1
  38. package/dist/tunnel/TunnelManager.js +36 -0
  39. package/dist/tunnel/TunnelManager.js.map +1 -1
  40. package/dist/tunnel/TunnelNotifier.d.ts +28 -0
  41. package/dist/tunnel/TunnelNotifier.d.ts.map +1 -1
  42. package/dist/tunnel/TunnelNotifier.js +38 -4
  43. package/dist/tunnel/TunnelNotifier.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/data/builtin-manifest.json +4 -4
  46. package/src/templates/hooks/telegram-topic-context.sh +19 -0
  47. package/upgrades/1.2.38.md +103 -0
  48. package/upgrades/1.2.39.md +54 -0
  49. package/upgrades/side-effects/owner-dm-channel.md +152 -0
  50. package/upgrades/side-effects/topic-intent-layer.md +86 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * TopicIntentArcCheck — Layer 3 of the Topic Intent Layer.
3
+ *
4
+ * Pre-send classifier that scans an agent's draft reply against the
5
+ * topic's current tentative + settled items. Fires signals (not blocks)
6
+ * when the draft would:
7
+ * - Act on a tentative item without conversational confirmation
8
+ * - Contradict an item already at authoritative tier
9
+ *
10
+ * Per spec v14 sections "How the agent asks for confirmation" and
11
+ * "Authority on send" — ArcCheck is SIGNAL-only. The existing outbound
12
+ * gate retains authority. The fired signal includes a suggested rewrite
13
+ * hint, but never blocks. The agent itself decides whether to redraft.
14
+ *
15
+ * Per `feedback_gate_latency_vs_client_timeout`: ArcCheck is designed
16
+ * to run concurrent with send-prep, not as a serial second gate. The
17
+ * timeout is enforced at the HTTP route layer (route caller sets the
18
+ * deadline; classifier itself is pure async).
19
+ *
20
+ * Per `feedback_signal_vs_authority`: ArcCheck is the brittle/low-context
21
+ * filter that detects and emits a signal. The higher-level outbound gate
22
+ * (tone-gate, response-review) has blocking authority. Two-layer split.
23
+ *
24
+ * Framework-agnostic: the LLM call is injected. Production wires it to
25
+ * Instar's LlmQueue + IntelligenceProvider; tests stub it.
26
+ */
27
+ export class ArcCheck {
28
+ store;
29
+ classifyFn;
30
+ constructor(store, classifyFn) {
31
+ this.store = store;
32
+ this.classifyFn = classifyFn;
33
+ }
34
+ /**
35
+ * Run ArcCheck on a draft. Returns a verdict object suitable for direct
36
+ * return from an HTTP route or consumption by a hook.
37
+ *
38
+ * Verdict priority (when multiple conditions fire):
39
+ * 1. contradicts-settled (highest — wrong direction on a decided item)
40
+ * 2. acting-on-tentative (lower — uncertain, but not wrong)
41
+ *
42
+ * If the draft engages multiple refs, the first matching one of the
43
+ * highest-priority kind wins. Subsequent fires are deferred to a
44
+ * future ArcCheck call after the agent's redraft.
45
+ */
46
+ async check(input) {
47
+ const refs = this.store.getRefsAtOrAbove(input.topicId, 'tentative');
48
+ if (refs.length === 0)
49
+ return { fire: false };
50
+ const classification = await this.classifyFn(input.draftText, refs);
51
+ // Priority 1: contradicts settled item
52
+ for (const refId of classification.contradicts) {
53
+ const ref = refs.find(r => r.refId === refId);
54
+ if (ref && ref.projection.tier === 'authoritative') {
55
+ return {
56
+ fire: true,
57
+ kind: 'contradicts-settled',
58
+ refId,
59
+ refText: ref.text,
60
+ currentTier: 'authoritative',
61
+ currentConfidence: ref.projection.confidence,
62
+ reason: `draft appears to contradict settled item "${ref.text}"`,
63
+ suggestedRewriteHint: `Pause and surface the contradiction in plain English: ` +
64
+ `acknowledge we previously settled on "${ref.text}", flag that the current draft would change that, ` +
65
+ `and ask the user to confirm the change before proceeding.`,
66
+ };
67
+ }
68
+ }
69
+ // Priority 2: acting on tentative item
70
+ for (const refId of classification.actsOn) {
71
+ const ref = refs.find(r => r.refId === refId);
72
+ if (ref && ref.projection.tier === 'tentative') {
73
+ return {
74
+ fire: true,
75
+ kind: 'acting-on-tentative',
76
+ refId,
77
+ refText: ref.text,
78
+ currentTier: 'tentative',
79
+ currentConfidence: ref.projection.confidence,
80
+ reason: `draft acts on tentative item "${ref.text}" (confidence ${ref.projection.confidence.toFixed(2)}) without explicit confirmation`,
81
+ suggestedRewriteHint: `Add a brief natural-language confirmation question before the action: ` +
82
+ `"I'm planning to use ${ref.text} here — just want to make sure that's still the call. ` +
83
+ `If we settled on something else, let me know."`,
84
+ };
85
+ }
86
+ }
87
+ return { fire: false };
88
+ }
89
+ }
90
+ /**
91
+ * Build the ArcCheck prompt for production use. Separated so prompt
92
+ * tuning can iterate without touching the classifier logic.
93
+ *
94
+ * The actual LLM provider call is wired in by the caller; this returns
95
+ * the prompt string + structured response schema description.
96
+ */
97
+ export function buildArcCheckPrompt(draftText, refs) {
98
+ const systemPrompt = `You are an arc-check classifier for a conversation. Your job is to read a draft agent reply and identify whether it ACTS ON or CONTRADICTS any items the conversation is currently tracking.
99
+
100
+ Output a JSON object: {"actsOn": ["<refId>", …], "contradicts": ["<refId>", …]}.
101
+
102
+ Rules:
103
+ - Be CONSERVATIVE. Most drafts engage with ZERO tracked refs. Don't pattern-match loosely.
104
+ - "actsOn" means the draft proceeds as if the ref is true / decided (uses the OAuth path, assumes the timeout value, etc.).
105
+ - "contradicts" means the draft says or implies the OPPOSITE of the ref's text.
106
+ - Mere mention of the same topic area is NOT engagement. The draft must commit to action ON the proposition.
107
+ - If unsure, return empty arrays.`;
108
+ const refsBlock = refs
109
+ .map(r => `- refId=${r.refId} tier=${r.projection.tier} text="${r.text}"`)
110
+ .join('\n');
111
+ const userPrompt = `Draft agent reply:
112
+ """
113
+ ${draftText}
114
+ """
115
+
116
+ Currently tracked refs on this topic:
117
+ ${refsBlock}
118
+
119
+ Return JSON: {"actsOn": [...], "contradicts": [...]}`;
120
+ return { systemPrompt, userPrompt };
121
+ }
122
+ /**
123
+ * Parse the LLM's response into an ArcCheckClassification. Tolerates
124
+ * code fences and prose preamble; degrades to empty arrays on parse
125
+ * failure so a busted LLM response never falsely fires ArcCheck.
126
+ */
127
+ export function parseArcCheckResponse(raw) {
128
+ let cleaned = raw.trim();
129
+ const fenceMatch = cleaned.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
130
+ if (fenceMatch)
131
+ cleaned = fenceMatch[1];
132
+ const start = cleaned.indexOf('{');
133
+ const end = cleaned.lastIndexOf('}');
134
+ if (start === -1 || end === -1 || end <= start)
135
+ return { actsOn: [], contradicts: [] };
136
+ try {
137
+ const parsed = JSON.parse(cleaned.slice(start, end + 1));
138
+ const actsOn = Array.isArray(parsed.actsOn) ? parsed.actsOn.filter((s) => typeof s === 'string') : [];
139
+ const contradicts = Array.isArray(parsed.contradicts) ? parsed.contradicts.filter((s) => typeof s === 'string') : [];
140
+ return { actsOn, contradicts };
141
+ }
142
+ catch {
143
+ return { actsOn: [], contradicts: [] };
144
+ }
145
+ }
146
+ //# sourceMappingURL=TopicIntentArcCheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicIntentArcCheck.js","sourceRoot":"","sources":["../../src/core/TopicIntentArcCheck.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * TopicIntentBriefing — Layer 2 of the Topic Intent Layer.
3
+ *
4
+ * Renders the per-topic semantic state as a plain-text briefing that the
5
+ * bootstrap context hooks prepend to the conversation history. Replaces
6
+ * "here's the transcript" with "here's the arc, then the transcript."
7
+ *
8
+ * Per spec v14 section "Layer 2 — arc-aware continuation header":
9
+ * - Stated goal of the topic (when extracted; optional)
10
+ * - Open threads (tentative items with extracted text, hedged)
11
+ * - Established (authoritative items, unhedged)
12
+ * - Observation tier is NOT surfaced
13
+ *
14
+ * Framework-agnostic: pure rendering function. Same output reachable from
15
+ * Claude Code bootstrap hooks, Codex bootstrap equivalents, and any other
16
+ * harness that wants the briefing as a plain string.
17
+ */
18
+ import { TopicIntentStore } from './TopicIntent.js';
19
+ export interface BriefingOptions {
20
+ /** Override "now" for time-sensitive projections (testing). */
21
+ nowMs?: number;
22
+ /** Maximum number of items per section (defaults to 8). */
23
+ maxPerSection?: number;
24
+ }
25
+ export interface BriefingResult {
26
+ /** The full briefing text. Empty string when nothing tracked yet. */
27
+ text: string;
28
+ /** Whether anything was rendered (false → safe to skip injection entirely). */
29
+ hasContent: boolean;
30
+ /** Counts for telemetry / diagnostics. */
31
+ counts: {
32
+ authoritative: number;
33
+ tentative: number;
34
+ /** Outstanding pending confirmation (if any). */
35
+ pendingOutstanding: boolean;
36
+ };
37
+ }
38
+ /**
39
+ * Render the briefing for a topic. Returns an empty string when nothing has
40
+ * accumulated to authoritative or tentative tier yet — observation-only is
41
+ * not surfaced to avoid noise.
42
+ */
43
+ export declare function renderTopicIntentBriefing(store: TopicIntentStore, topicId: number, opts?: BriefingOptions): BriefingResult;
44
+ //# sourceMappingURL=TopicIntentBriefing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicIntentBriefing.d.ts","sourceRoot":"","sources":["../../src/core/TopicIntentBriefing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,UAAU,EAAE,OAAO,CAAC;IACpB,0CAA0C;IAC1C,MAAM,EAAE;QACN,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,iDAAiD;QACjD,kBAAkB,EAAE,OAAO,CAAC;KAC7B,CAAC;CACH;AAID;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,eAAoB,GACzB,cAAc,CAqEhB"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * TopicIntentBriefing — Layer 2 of the Topic Intent Layer.
3
+ *
4
+ * Renders the per-topic semantic state as a plain-text briefing that the
5
+ * bootstrap context hooks prepend to the conversation history. Replaces
6
+ * "here's the transcript" with "here's the arc, then the transcript."
7
+ *
8
+ * Per spec v14 section "Layer 2 — arc-aware continuation header":
9
+ * - Stated goal of the topic (when extracted; optional)
10
+ * - Open threads (tentative items with extracted text, hedged)
11
+ * - Established (authoritative items, unhedged)
12
+ * - Observation tier is NOT surfaced
13
+ *
14
+ * Framework-agnostic: pure rendering function. Same output reachable from
15
+ * Claude Code bootstrap hooks, Codex bootstrap equivalents, and any other
16
+ * harness that wants the briefing as a plain string.
17
+ */
18
+ const DEFAULT_MAX_PER_SECTION = 8;
19
+ /**
20
+ * Render the briefing for a topic. Returns an empty string when nothing has
21
+ * accumulated to authoritative or tentative tier yet — observation-only is
22
+ * not surfaced to avoid noise.
23
+ */
24
+ export function renderTopicIntentBriefing(store, topicId, opts = {}) {
25
+ const max = opts.maxPerSection ?? DEFAULT_MAX_PER_SECTION;
26
+ const nowMs = opts.nowMs;
27
+ const file = store.read(topicId);
28
+ const refs = store.getRefsAtOrAbove(topicId, 'tentative', nowMs);
29
+ const authoritative = refs.filter(r => r.projection.tier === 'authoritative');
30
+ const tentative = refs.filter(r => r.projection.tier === 'tentative');
31
+ if (authoritative.length === 0 && tentative.length === 0 && !file.pending.outstanding) {
32
+ return {
33
+ text: '',
34
+ hasContent: false,
35
+ counts: { authoritative: 0, tentative: 0, pendingOutstanding: false },
36
+ };
37
+ }
38
+ const lines = [];
39
+ lines.push(`=== TOPIC ${topicId} INTENT BRIEFING (auto-injected) ===`);
40
+ lines.push(`The agent has been tracking the arc of this conversation. Read this before responding —`);
41
+ lines.push(`it's the goal-and-decisions context that won't appear in the message history alone.`);
42
+ if (authoritative.length > 0) {
43
+ lines.push('');
44
+ lines.push('SETTLED (treat as decided unless you see a contradiction):');
45
+ for (const r of sortByConfidenceDesc(authoritative).slice(0, max)) {
46
+ const kindTag = r.kind === 'decision' ? '[decision]' : '[fact]';
47
+ lines.push(` • ${kindTag} ${r.text}`);
48
+ }
49
+ if (authoritative.length > max) {
50
+ lines.push(` • (… ${authoritative.length - max} more settled items not shown)`);
51
+ }
52
+ }
53
+ if (tentative.length > 0) {
54
+ lines.push('');
55
+ lines.push('TENTATIVE (agent operating on these but not confirmed — verify before acting):');
56
+ for (const r of sortByConfidenceDesc(tentative).slice(0, max)) {
57
+ const kindTag = r.kind === 'decision' ? '[decision]' : '[fact]';
58
+ const conf = `(confidence ${r.projection.confidence.toFixed(2)})`;
59
+ lines.push(` • ${kindTag} ${r.text} ${conf}`);
60
+ }
61
+ if (tentative.length > max) {
62
+ lines.push(` • (… ${tentative.length - max} more tentative items not shown)`);
63
+ }
64
+ }
65
+ if (file.pending.outstanding) {
66
+ const pc = file.pending.outstanding;
67
+ lines.push('');
68
+ lines.push('PENDING CONFIRMATION (the agent asked the user about this; awaiting answer):');
69
+ lines.push(` • "${pc.propositionText}"`);
70
+ lines.push(` asked at turn ${pc.sentAtTurn} (retries: ${pc.retries}/${pc.maxRetries})`);
71
+ lines.push(` if the user just answered, the next response should record the verdict.`);
72
+ }
73
+ lines.push('');
74
+ lines.push('=== END TOPIC INTENT BRIEFING ===');
75
+ return {
76
+ text: lines.join('\n'),
77
+ hasContent: true,
78
+ counts: {
79
+ authoritative: authoritative.length,
80
+ tentative: tentative.length,
81
+ pendingOutstanding: file.pending.outstanding !== null,
82
+ },
83
+ };
84
+ }
85
+ function sortByConfidenceDesc(arr) {
86
+ return [...arr].sort((a, b) => b.projection.confidence - a.projection.confidence);
87
+ }
88
+ //# sourceMappingURL=TopicIntentBriefing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicIntentBriefing.js","sourceRoot":"","sources":["../../src/core/TopicIntentBriefing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * TopicIntentExtractor — converts a raw conversation turn into EvidenceEvents.
3
+ *
4
+ * Layer 1 component. Reads a new substantive message + the topic's existing
5
+ * EstablishedRef set, asks a Tier-1 LLM to identify signal events (new
6
+ * extractions, re-references, affirmations, contradictions), and persists
7
+ * them via TopicIntentStore.appendEvidence.
8
+ *
9
+ * Framework-agnostic: the LLM call itself is injected. Production wires it
10
+ * to Instar's LlmQueue + chosen provider; tests stub it.
11
+ *
12
+ * The extractor returns the events it CREATED so callers can act on them
13
+ * (e.g., trigger conflict-mark when two refs come into conflict).
14
+ */
15
+ import { TopicIntentStore, type EvidenceEvent, type RefKind, type EstablishedRef } from './TopicIntent.js';
16
+ export interface ExtractorInput {
17
+ topicId: number;
18
+ arcId: string;
19
+ message: {
20
+ id: string;
21
+ text: string;
22
+ fromUser: boolean;
23
+ turn: number;
24
+ at: string;
25
+ };
26
+ /** Existing refs on the topic, provided so the LLM can anchor signals. */
27
+ existingRefs: EstablishedRef[];
28
+ }
29
+ /**
30
+ * The LLM is asked to return zero or more SignalProposals per message.
31
+ * Each proposal references either an existing refId (re-reference,
32
+ * affirmation, contradiction) OR a new ref proposition text (initial
33
+ * extraction).
34
+ *
35
+ * The actual provider call is injected; this type is the contract.
36
+ */
37
+ export interface SignalProposal {
38
+ kind: 'new-ref' | 'reref' | 'affirm' | 'contradict';
39
+ /** Required for reref / affirm / contradict; null for new-ref. */
40
+ refId: string | null;
41
+ /** Required for new-ref; describes the proposition being extracted. */
42
+ propositionText?: string;
43
+ /** Required for new-ref; the type of proposition. */
44
+ refKind?: RefKind;
45
+ /** Optional: extractor's confidence in this signal (for logging; not used in projection). */
46
+ llmConfidence?: number;
47
+ }
48
+ export type ExtractFn = (input: ExtractorInput) => Promise<SignalProposal[]>;
49
+ export interface ExtractorResult {
50
+ emitted: EvidenceEvent[];
51
+ createdRefs: Array<{
52
+ refId: string;
53
+ kind: RefKind;
54
+ text: string;
55
+ }>;
56
+ skipped: number;
57
+ }
58
+ export declare class TopicIntentExtractor {
59
+ private store;
60
+ private extractFn;
61
+ constructor(store: TopicIntentStore, extractFn: ExtractFn);
62
+ /**
63
+ * Process a new message: run the LLM, translate proposals to events,
64
+ * append to store, return what was created.
65
+ */
66
+ ingest(input: ExtractorInput): Promise<ExtractorResult>;
67
+ /**
68
+ * Translate a SignalProposal into the (refId, EvidenceEvent, refInit?) tuple
69
+ * to be appended. Returns null if the proposal is invalid.
70
+ */
71
+ private translateProposal;
72
+ }
73
+ /**
74
+ * Build the extractor prompt for production use. Separated so prompt
75
+ * tuning can iterate without touching the extractor logic.
76
+ *
77
+ * The actual LLM provider call is wired in by the caller; this function
78
+ * returns the prompt string + the JSON schema description for the
79
+ * structured response.
80
+ */
81
+ export declare function buildExtractorPrompt(input: ExtractorInput): {
82
+ systemPrompt: string;
83
+ userPrompt: string;
84
+ };
85
+ /**
86
+ * Parse the LLM's response into SignalProposal[]. Tolerates the LLM
87
+ * wrapping the JSON in code fences or prose preamble.
88
+ */
89
+ export declare function parseExtractorResponse(raw: string): SignalProposal[];
90
+ //# sourceMappingURL=TopicIntentExtractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicIntentExtractor.d.ts","sourceRoot":"","sources":["../../src/core/TopicIntentExtractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EACL,gBAAgB,EAEhB,KAAK,aAAa,EAElB,KAAK,OAAO,EACZ,KAAK,cAAc,EAEpB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,OAAO,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;KACZ,CAAC;IACF,0EAA0E;IAC1E,YAAY,EAAE,cAAc,EAAE,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,CAAC;IACpD,kEAAkE;IAClE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;AAE7E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,WAAW,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,oBAAoB;IAE7B,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;gBADT,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,SAAS;IAG9B;;;OAGG;IACG,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAwB7D;;;OAGG;IACH,OAAO,CAAC,iBAAiB;CAuC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,GAAG;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CA8BxG;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CAkBpE"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * TopicIntentExtractor — converts a raw conversation turn into EvidenceEvents.
3
+ *
4
+ * Layer 1 component. Reads a new substantive message + the topic's existing
5
+ * EstablishedRef set, asks a Tier-1 LLM to identify signal events (new
6
+ * extractions, re-references, affirmations, contradictions), and persists
7
+ * them via TopicIntentStore.appendEvidence.
8
+ *
9
+ * Framework-agnostic: the LLM call itself is injected. Production wires it
10
+ * to Instar's LlmQueue + chosen provider; tests stub it.
11
+ *
12
+ * The extractor returns the events it CREATED so callers can act on them
13
+ * (e.g., trigger conflict-mark when two refs come into conflict).
14
+ */
15
+ import { randomUUID } from 'node:crypto';
16
+ import { buildEvent, } from './TopicIntent.js';
17
+ export class TopicIntentExtractor {
18
+ store;
19
+ extractFn;
20
+ constructor(store, extractFn) {
21
+ this.store = store;
22
+ this.extractFn = extractFn;
23
+ }
24
+ /**
25
+ * Process a new message: run the LLM, translate proposals to events,
26
+ * append to store, return what was created.
27
+ */
28
+ async ingest(input) {
29
+ const proposals = await this.extractFn(input);
30
+ const emitted = [];
31
+ const createdRefs = [];
32
+ let skipped = 0;
33
+ for (const p of proposals) {
34
+ const translated = this.translateProposal(p, input);
35
+ if (!translated) {
36
+ skipped++;
37
+ continue;
38
+ }
39
+ const { refId, ev, refInit } = translated;
40
+ this.store.appendEvidence(input.topicId, refId, ev, refInit);
41
+ emitted.push(ev);
42
+ if (p.kind === 'new-ref' && refInit) {
43
+ createdRefs.push({ refId, kind: refInit.kind ?? 'fact', text: refInit.text ?? '' });
44
+ }
45
+ }
46
+ return { emitted, createdRefs, skipped };
47
+ }
48
+ /**
49
+ * Translate a SignalProposal into the (refId, EvidenceEvent, refInit?) tuple
50
+ * to be appended. Returns null if the proposal is invalid.
51
+ */
52
+ translateProposal(p, input) {
53
+ const { message, arcId } = input;
54
+ if (p.kind === 'new-ref') {
55
+ if (!p.propositionText || !p.refKind)
56
+ return null;
57
+ const refId = `ref-${randomUUID()}`;
58
+ const evKind = message.fromUser ? 'extract-user' : 'extract-agent';
59
+ const ev = buildEvent(refId, evKind, message.id, { at: message.at });
60
+ return {
61
+ refId,
62
+ ev,
63
+ refInit: { text: p.propositionText, kind: p.refKind, arcId },
64
+ };
65
+ }
66
+ // For reref / affirm / contradict, the proposal must point to an existing refId
67
+ if (!p.refId)
68
+ return null;
69
+ const existing = input.existingRefs.find(r => r.refId === p.refId);
70
+ if (!existing)
71
+ return null;
72
+ let evKind;
73
+ if (p.kind === 'reref') {
74
+ evKind = message.fromUser ? 'user-reref' : 'agent-reref';
75
+ }
76
+ else if (p.kind === 'affirm') {
77
+ // Only user messages produce affirm signals; agent messages mapping to "affirm" are bookkeeping reref
78
+ if (!message.fromUser)
79
+ return null;
80
+ evKind = 'user-affirm';
81
+ }
82
+ else {
83
+ // contradict — only user-authored
84
+ if (!message.fromUser)
85
+ return null;
86
+ evKind = 'contradiction';
87
+ }
88
+ const ev = buildEvent(p.refId, evKind, message.id, { at: message.at });
89
+ return { refId: p.refId, ev };
90
+ }
91
+ }
92
+ /**
93
+ * Build the extractor prompt for production use. Separated so prompt
94
+ * tuning can iterate without touching the extractor logic.
95
+ *
96
+ * The actual LLM provider call is wired in by the caller; this function
97
+ * returns the prompt string + the JSON schema description for the
98
+ * structured response.
99
+ */
100
+ export function buildExtractorPrompt(input) {
101
+ const systemPrompt = `You are an arc-tracking extractor for a multi-turn conversation. Your job is to read one new message and identify candidate facts and decisions that the conversation is establishing, plus references / affirmations / contradictions of previously-tracked items.
102
+
103
+ Output a JSON array of signal proposals. Each item is one of:
104
+ - {"kind":"new-ref","propositionText":"<the candidate fact or decision in 1-2 sentences>","refKind":"fact"|"decision"}
105
+ - {"kind":"reref","refId":"<existing refId>"}
106
+ - {"kind":"affirm","refId":"<existing refId>"}
107
+ - {"kind":"contradict","refId":"<existing refId>"}
108
+
109
+ Rules:
110
+ - Be CONSERVATIVE. Most messages produce zero or one signal. Don't extract trivia.
111
+ - Anchor "reref"/"affirm"/"contradict" to an existing refId only if the message clearly references the same proposition.
112
+ - "affirm" is for explicit agreement ("yes", "exactly", "agreed").
113
+ - "contradict" is for explicit disagreement ("actually no", "we switched to X").
114
+ - "new-ref" is reserved for SIGNIFICANT items that warrant tracking — not every passing remark.
115
+ - If unsure, return [].`;
116
+ const refsBlock = input.existingRefs.length === 0
117
+ ? '(no existing refs tracked yet)'
118
+ : input.existingRefs.map(r => `- refId=${r.refId} kind=${r.kind} text="${r.text}" tier=${r.confidence >= 0.7 ? 'authoritative' : r.confidence >= 0.3 ? 'tentative' : 'observation'}`).join('\n');
119
+ const userPrompt = `New message (fromUser=${input.message.fromUser}, turn=${input.message.turn}):
120
+ ${input.message.text}
121
+
122
+ Currently tracked refs on this topic:
123
+ ${refsBlock}
124
+
125
+ Return JSON array of signal proposals.`;
126
+ return { systemPrompt, userPrompt };
127
+ }
128
+ /**
129
+ * Parse the LLM's response into SignalProposal[]. Tolerates the LLM
130
+ * wrapping the JSON in code fences or prose preamble.
131
+ */
132
+ export function parseExtractorResponse(raw) {
133
+ // Strip code fences if present
134
+ let cleaned = raw.trim();
135
+ const fenceMatch = cleaned.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
136
+ if (fenceMatch)
137
+ cleaned = fenceMatch[1];
138
+ // Find the first [ and matching final ]
139
+ const start = cleaned.indexOf('[');
140
+ const end = cleaned.lastIndexOf(']');
141
+ if (start === -1 || end === -1 || end <= start)
142
+ return [];
143
+ try {
144
+ const parsed = JSON.parse(cleaned.slice(start, end + 1));
145
+ if (!Array.isArray(parsed))
146
+ return [];
147
+ return parsed.filter(p => p && typeof p === 'object' && typeof p.kind === 'string');
148
+ }
149
+ catch {
150
+ return [];
151
+ }
152
+ }
153
+ //# sourceMappingURL=TopicIntentExtractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicIntentExtractor.js","sourceRoot":"","sources":["../../src/core/TopicIntentExtractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAEL,UAAU,GAMX,MAAM,kBAAkB,CAAC;AA4C1B,MAAM,OAAO,oBAAoB;IAErB;IACA;IAFV,YACU,KAAuB,EACvB,SAAoB;QADpB,UAAK,GAAL,KAAK,CAAkB;QACvB,cAAS,GAAT,SAAS,CAAW;IAC3B,CAAC;IAEJ;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,KAAqB;QAChC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAE9C,MAAM,OAAO,GAAoB,EAAE,CAAC;QACpC,MAAM,WAAW,GAA0D,EAAE,CAAC;QAC9E,IAAI,OAAO,GAAG,CAAC,CAAC;QAEhB,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YACpD,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,OAAO,EAAE,CAAC;gBACV,SAAS;YACX,CAAC;YACD,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;YAC1C,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;YAC7D,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,EAAE,CAAC;gBACpC,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;YACtF,CAAC;QACH,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;IAC3C,CAAC;IAED;;;OAGG;IACK,iBAAiB,CACvB,CAAiB,EACjB,KAAqB;QAErB,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;QAEjC,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACzB,IAAI,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAC;YAClD,MAAM,KAAK,GAAG,OAAO,UAAU,EAAE,EAAE,CAAC;YACpC,MAAM,MAAM,GAAiB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,eAAe,CAAC;YACjF,MAAM,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;YACrE,OAAO;gBACL,KAAK;gBACL,EAAE;gBACF,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE;aAC7D,CAAC;QACJ,CAAC;QAED,gFAAgF;QAChF,IAAI,CAAC,CAAC,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAC1B,MAAM,QAAQ,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;QACnE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,IAAI,MAAoB,CAAC;QACzB,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACvB,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC;QAC3D,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC/B,sGAAsG;YACtG,IAAI,CAAC,OAAO,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAC;YACnC,MAAM,GAAG,aAAa,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,kCAAkC;YAClC,IAAI,CAAC,OAAO,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAC;YACnC,MAAM,GAAG,eAAe,CAAC;QAC3B,CAAC;QAED,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QACvE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC;IAChC,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAqB;IACxD,MAAM,YAAY,GAAG;;;;;;;;;;;;;;wBAcC,CAAC;IAEvB,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;QAC/C,CAAC,CAAC,gCAAgC;QAClC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEnM,MAAM,UAAU,GAAG,yBAAyB,KAAK,CAAC,OAAO,CAAC,QAAQ,UAAU,KAAK,CAAC,OAAO,CAAC,IAAI;EAC9F,KAAK,CAAC,OAAO,CAAC,IAAI;;;EAGlB,SAAS;;uCAE4B,CAAC;IAEtC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAChD,+BAA+B;IAC/B,IAAI,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACzB,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACpE,IAAI,UAAU;QAAE,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAExC,wCAAwC;IACxC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,KAAK;QAAE,OAAO,EAAE,CAAC;IAE1D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAqB,CAAC;IAC1G,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * PendingConfirmationManager — lifecycle for conversational confirmation records.
3
+ *
4
+ * Layer 1 component. Manages the queue, TTL, dedup, retry-with-sharpening,
5
+ * and revalidation rules for pending confirmations. Per spec v14 section
6
+ * "Pending-Confirmation Records (v9 — addresses GPT round-on-v8 finding #2)":
7
+ *
8
+ * - At most one outstanding PendingConfirmation per topic
9
+ * - Queue depth: 3; 4th queued is dropped silently with telemetry
10
+ * - Queue dedup by refId
11
+ * - TTL: 5 substantive user turns OR 24h, whichever first
12
+ * - TTL clock starts at DEQUEUE time, not queue-entry time
13
+ * - Revalidation at dequeue: refId still tentative + proposition still
14
+ * semantically relevant; stale → drop silently with telemetry
15
+ * - Answer interpretation by Tier 1 LLM (positive / negative / ambiguous /
16
+ * non-responsive); ambiguous triggers sharpen + retry up to maxRetries=2,
17
+ * then status=abandoned
18
+ *
19
+ * Framework-agnostic: pure logic over the JSON store. No transport coupling.
20
+ */
21
+ import { TopicIntentStore, type PendingConfirmation, type TopicIntentFile } from './TopicIntent.js';
22
+ export type AnswerVerdict = 'positive' | 'negative' | 'ambiguous' | 'non-responsive';
23
+ export interface CreateOpts {
24
+ topicId: number;
25
+ arcId: string;
26
+ refId: string;
27
+ propositionText: string;
28
+ questionText: string;
29
+ currentUserTurn: number;
30
+ now?: string;
31
+ ttl?: {
32
+ turns: number;
33
+ hours: number;
34
+ };
35
+ maxRetries?: number;
36
+ }
37
+ export interface RevalidationResult {
38
+ valid: boolean;
39
+ reason?: 'no-longer-tentative' | 'proposition-stale' | 'ref-missing' | 'ok';
40
+ }
41
+ /**
42
+ * Operator-supplied callbacks. Both are deterministic from the manager's
43
+ * point of view — the caller routes them to whatever LLM/lexical check
44
+ * makes sense in context.
45
+ */
46
+ export interface PendingConfirmCallbacks {
47
+ /**
48
+ * Asked at DEQUEUE time. Returns whether the queued proposition is still
49
+ * semantically relevant given more recent events on the topic.
50
+ * Default: always valid (no-op stub for tests / pre-Layer-3).
51
+ */
52
+ revalidateProposition?: (queued: PendingConfirmation, file: TopicIntentFile) => RevalidationResult;
53
+ }
54
+ export interface CreateResult {
55
+ status: 'outstanding' | 'queued' | 'dropped-duplicate' | 'dropped-queue-full';
56
+ pendingId?: string;
57
+ reason?: string;
58
+ }
59
+ export declare class PendingConfirmationManager {
60
+ private store;
61
+ private callbacks;
62
+ constructor(store: TopicIntentStore, callbacks?: PendingConfirmCallbacks);
63
+ /**
64
+ * Create a new pending confirmation. May go outstanding immediately,
65
+ * queue, or be dropped (duplicate / queue full).
66
+ */
67
+ create(opts: CreateOpts): CreateResult;
68
+ /**
69
+ * Interpret an incoming user-authored message as an answer to the current
70
+ * outstanding pending confirmation, if any.
71
+ *
72
+ * The caller is responsible for running the Tier-1 LLM verdict; this
73
+ * method takes the verdict and applies the state machine.
74
+ */
75
+ interpretAnswer(topicId: number, verdict: AnswerVerdict, sourceMessageId: string, now?: string): {
76
+ applied: boolean;
77
+ outcome: 'positive' | 'negative' | 'sharpen-retry' | 'abandoned' | 'no-outstanding';
78
+ };
79
+ /**
80
+ * Sweep for TTL expiry on the outstanding pending. Called by the caller
81
+ * after every substantive user turn (turn-based TTL) and periodically
82
+ * (clock-based TTL).
83
+ *
84
+ * Returns the dropped pending if one was swept.
85
+ */
86
+ sweepTtl(topicId: number, currentUserTurn: number, now?: string): PendingConfirmation | null;
87
+ /** Force-cancel the outstanding pending (e.g. on emergency stop / corrective input). */
88
+ cancelOutstanding(topicId: number, now?: string): PendingConfirmation | null;
89
+ /**
90
+ * Promote the next queued item to outstanding (if any).
91
+ * Runs revalidation at dequeue time. Stale items are dropped silently.
92
+ */
93
+ private dequeueNext;
94
+ private revalidate;
95
+ }
96
+ //# sourceMappingURL=TopicIntentPendingConfirm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicIntentPendingConfirm.d.ts","sourceRoot":"","sources":["../../src/core/TopicIntentPendingConfirm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EACL,gBAAgB,EAEhB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAO1B,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAErF,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,qBAAqB,GAAG,mBAAmB,GAAG,aAAa,GAAG,IAAI,CAAC;CAC7E;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,EAAE,IAAI,EAAE,eAAe,KAAK,kBAAkB,CAAC;CACpG;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,aAAa,GAAG,QAAQ,GAAG,mBAAmB,GAAG,oBAAoB,CAAC;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,0BAA0B;IAEnC,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;gBADT,KAAK,EAAE,gBAAgB,EACvB,SAAS,GAAE,uBAA4B;IAGjD;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY;IAmDtC;;;;;;OAMG;IACH,eAAe,CACb,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,aAAa,EACtB,eAAe,EAAE,MAAM,EACvB,GAAG,CAAC,EAAE,MAAM,GACX;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,UAAU,GAAG,UAAU,GAAG,eAAe,GAAG,WAAW,GAAG,gBAAgB,CAAA;KAAE;IAsD5G;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI;IAqB5F,wFAAwF;IACxF,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI;IAa5E;;;OAGG;IACH,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,UAAU;CAiBnB"}