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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +19 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/TopicIntent.d.ts +162 -0
- package/dist/core/TopicIntent.d.ts.map +1 -0
- package/dist/core/TopicIntent.js +355 -0
- package/dist/core/TopicIntent.js.map +1 -0
- package/dist/core/TopicIntentArcCheck.d.ts +101 -0
- package/dist/core/TopicIntentArcCheck.d.ts.map +1 -0
- package/dist/core/TopicIntentArcCheck.js +146 -0
- package/dist/core/TopicIntentArcCheck.js.map +1 -0
- package/dist/core/TopicIntentBriefing.d.ts +44 -0
- package/dist/core/TopicIntentBriefing.d.ts.map +1 -0
- package/dist/core/TopicIntentBriefing.js +88 -0
- package/dist/core/TopicIntentBriefing.js.map +1 -0
- package/dist/core/TopicIntentExtractor.d.ts +90 -0
- package/dist/core/TopicIntentExtractor.d.ts.map +1 -0
- package/dist/core/TopicIntentExtractor.js +153 -0
- package/dist/core/TopicIntentExtractor.js.map +1 -0
- package/dist/core/TopicIntentPendingConfirm.d.ts +96 -0
- package/dist/core/TopicIntentPendingConfirm.d.ts.map +1 -0
- package/dist/core/TopicIntentPendingConfirm.js +223 -0
- package/dist/core/TopicIntentPendingConfirm.js.map +1 -0
- package/dist/messaging/TelegramAdapter.d.ts +45 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +52 -0
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/server/AgentServer.d.ts +3 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +10 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/topicIntentRoutes.d.ts +27 -0
- package/dist/server/topicIntentRoutes.d.ts.map +1 -0
- package/dist/server/topicIntentRoutes.js +189 -0
- package/dist/server/topicIntentRoutes.js.map +1 -0
- package/dist/tunnel/TunnelManager.d.ts +32 -1
- package/dist/tunnel/TunnelManager.d.ts.map +1 -1
- package/dist/tunnel/TunnelManager.js +36 -0
- package/dist/tunnel/TunnelManager.js.map +1 -1
- package/dist/tunnel/TunnelNotifier.d.ts +28 -0
- package/dist/tunnel/TunnelNotifier.d.ts.map +1 -1
- package/dist/tunnel/TunnelNotifier.js +38 -4
- package/dist/tunnel/TunnelNotifier.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +4 -4
- package/src/templates/hooks/telegram-topic-context.sh +19 -0
- package/upgrades/1.2.38.md +103 -0
- package/upgrades/1.2.39.md +54 -0
- package/upgrades/side-effects/owner-dm-channel.md +152 -0
- 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":"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"}
|
|
@@ -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"}
|