tracer-sh 0.2.5 → 0.2.7
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/package.json +1 -1
- package/packages/server/dist/index.js +361 -24
- package/packages/web/dist/assets/{SearchableSelect-B006wjjM.js → SearchableSelect-BPzDtKhB.js} +1 -1
- package/packages/web/dist/assets/{Settings-DMPQXaLL.js → Settings-ntDADPDR.js} +1 -1
- package/packages/web/dist/assets/{highlighted-body-OFNGDK62-C3igFa7L.js → highlighted-body-OFNGDK62-tPjSIaIA.js} +1 -1
- package/packages/web/dist/assets/index-B4Hwftt4.css +1 -0
- package/packages/web/dist/assets/{index-B7y1naN3.js → index-IihLiR83.js} +8 -8
- package/packages/web/dist/assets/mermaid-GHXKKRXX-C9jh9K1i.js +191 -0
- package/packages/web/dist/index.html +2 -2
- package/packages/web/dist/assets/index-CxrnanDH.css +0 -1
- package/packages/web/dist/assets/mermaid-GHXKKRXX-Cga6EKax.js +0 -191
package/package.json
CHANGED
|
@@ -16548,6 +16548,49 @@ var TOOL_NAMES = {
|
|
|
16548
16548
|
var CLIENT_TOOL_NAMES = Object.fromEntries(
|
|
16549
16549
|
Object.entries(TOOL_NAMES).map(([k, v]) => [k, `tool-${v}`])
|
|
16550
16550
|
);
|
|
16551
|
+
var ANALYSIS_MARKER = "<analysis>";
|
|
16552
|
+
function isAnalysisMessage(msg) {
|
|
16553
|
+
if (msg.role !== "assistant" || !msg.parts) return false;
|
|
16554
|
+
return msg.parts.some((p) => {
|
|
16555
|
+
if (p.type === CLIENT_TOOL_NAMES.BEGIN_ANALYSIS) return true;
|
|
16556
|
+
if (p.type !== "text") return false;
|
|
16557
|
+
const text3 = p.text;
|
|
16558
|
+
return typeof text3 === "string" && text3.includes(ANALYSIS_MARKER);
|
|
16559
|
+
});
|
|
16560
|
+
}
|
|
16561
|
+
function compactionUpTo(messages, boundaryIdx) {
|
|
16562
|
+
const boundary = messages[boundaryIdx];
|
|
16563
|
+
if (!boundary || boundary.role !== "assistant") return null;
|
|
16564
|
+
if (!isAnalysisMessage(boundary)) return boundaryIdx + 1;
|
|
16565
|
+
return boundaryIdx >= 1 ? boundaryIdx : null;
|
|
16566
|
+
}
|
|
16567
|
+
function findAnalysisMarker(parts) {
|
|
16568
|
+
const toolIdx = parts.findIndex((p) => p.type === CLIENT_TOOL_NAMES.BEGIN_ANALYSIS);
|
|
16569
|
+
if (toolIdx !== -1) return { kind: "tool", partIdx: toolIdx };
|
|
16570
|
+
for (let i = 0; i < parts.length; i++) {
|
|
16571
|
+
const p = parts[i];
|
|
16572
|
+
if (p.type !== "text") continue;
|
|
16573
|
+
const text3 = p.text;
|
|
16574
|
+
if (typeof text3 !== "string") continue;
|
|
16575
|
+
const idx = text3.indexOf(ANALYSIS_MARKER);
|
|
16576
|
+
if (idx !== -1) return { kind: "text", partIdx: i, charIdx: idx };
|
|
16577
|
+
}
|
|
16578
|
+
return null;
|
|
16579
|
+
}
|
|
16580
|
+
function splitAtAnalysis(parts) {
|
|
16581
|
+
const marker26 = findAnalysisMarker(parts);
|
|
16582
|
+
if (!marker26) return null;
|
|
16583
|
+
if (marker26.kind === "tool") {
|
|
16584
|
+
return { before: parts.slice(0, marker26.partIdx), analysis: parts.slice(marker26.partIdx + 1) };
|
|
16585
|
+
}
|
|
16586
|
+
const p = parts[marker26.partIdx];
|
|
16587
|
+
const beforeText = p.text.slice(0, marker26.charIdx);
|
|
16588
|
+
const afterText = p.text.slice(marker26.charIdx + ANALYSIS_MARKER.length);
|
|
16589
|
+
return {
|
|
16590
|
+
before: [...parts.slice(0, marker26.partIdx), ...beforeText.trim() ? [{ ...p, text: beforeText }] : []],
|
|
16591
|
+
analysis: [...afterText.trim() ? [{ ...p, text: afterText }] : [], ...parts.slice(marker26.partIdx + 1)]
|
|
16592
|
+
};
|
|
16593
|
+
}
|
|
16551
16594
|
var ImportedAnalysisSchema = external_exports.object({
|
|
16552
16595
|
v: external_exports.literal(1),
|
|
16553
16596
|
kind: external_exports.literal("analysis"),
|
|
@@ -20371,6 +20414,13 @@ var chatSessions = sqliteTable("chat_sessions", {
|
|
|
20371
20414
|
status: text("status").notNull().default("idle"),
|
|
20372
20415
|
/** Null for normal sessions. "imported" for sessions re-hydrated from a dropped analysis PNG. */
|
|
20373
20416
|
kind: text("kind"),
|
|
20417
|
+
/** Compaction: LLM-generated (possibly user-edited) summary of the first summaryUpTo messages. */
|
|
20418
|
+
summary: text("summary"),
|
|
20419
|
+
/** Count of leading messages covered by the summary. Index-based because
|
|
20420
|
+
* assistant messages can carry empty ids; prefixes are stable here (messages
|
|
20421
|
+
* are only appended or suffix-truncated, never reordered). */
|
|
20422
|
+
summaryUpTo: integer2("summary_up_to"),
|
|
20423
|
+
summaryCreatedAt: integer2("summary_created_at"),
|
|
20374
20424
|
createdAt: integer2("created_at").notNull().$defaultFn(() => unixNow()),
|
|
20375
20425
|
updatedAt: integer2("updated_at").notNull().$defaultFn(() => unixNow())
|
|
20376
20426
|
}, (t2) => [
|
|
@@ -20514,6 +20564,9 @@ function runSetup() {
|
|
|
20514
20564
|
messages TEXT NOT NULL,
|
|
20515
20565
|
status TEXT NOT NULL DEFAULT 'idle',
|
|
20516
20566
|
kind TEXT,
|
|
20567
|
+
summary TEXT,
|
|
20568
|
+
summary_up_to INTEGER,
|
|
20569
|
+
summary_created_at INTEGER,
|
|
20517
20570
|
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
20518
20571
|
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
20519
20572
|
);
|
|
@@ -20617,7 +20670,12 @@ function runSetup() {
|
|
|
20617
20670
|
for (const ddl of [
|
|
20618
20671
|
`ALTER TABLE sub_agent_runs ADD COLUMN session_id TEXT`,
|
|
20619
20672
|
`ALTER TABLE tool_memories ADD COLUMN review_note TEXT`,
|
|
20620
|
-
`ALTER TABLE chat_sessions ADD COLUMN kind TEXT
|
|
20673
|
+
`ALTER TABLE chat_sessions ADD COLUMN kind TEXT`,
|
|
20674
|
+
`ALTER TABLE chat_sessions ADD COLUMN summary TEXT`,
|
|
20675
|
+
`ALTER TABLE chat_sessions ADD COLUMN summary_up_to INTEGER`,
|
|
20676
|
+
`ALTER TABLE chat_sessions ADD COLUMN summary_created_at INTEGER`,
|
|
20677
|
+
// Drops the short-lived id-based boundary column (never shipped in a release).
|
|
20678
|
+
`ALTER TABLE chat_sessions DROP COLUMN summary_up_to_id`
|
|
20621
20679
|
]) {
|
|
20622
20680
|
try {
|
|
20623
20681
|
sqlite.exec(ddl);
|
|
@@ -37940,7 +37998,7 @@ function injectMemories(prompt, memoryContext) {
|
|
|
37940
37998
|
const memoryBlock = `
|
|
37941
37999
|
|
|
37942
38000
|
## ${MEMORY_SECTION_NAME}
|
|
37943
|
-
These OVERRIDE any conflicting instructions above \u2014 they are
|
|
38001
|
+
These OVERRIDE any conflicting instructions above \u2014 they are lessons recorded from real failures in past sessions:
|
|
37944
38002
|
${lines.join("\n")}
|
|
37945
38003
|
`;
|
|
37946
38004
|
const firstBreak = prompt.indexOf("\n\n");
|
|
@@ -46233,6 +46291,8 @@ You MUST save a memory for every query failure:
|
|
|
46233
46291
|
- Wrong field names \u2192 "Don't use X, use Y for [purpose]"
|
|
46234
46292
|
- Wrong event types \u2192 "Don't query X for [goal], use Y instead"
|
|
46235
46293
|
|
|
46294
|
+
**Corrections must be demonstrated, not guessed.** Only write "use Y instead" if Y was actually run in this session and succeeded. If no working alternative was demonstrated, save only the mistake ("Don't use X in [context]") \u2014 a wrong correction is worse than none, because memories override future instructions.
|
|
46295
|
+
|
|
46236
46296
|
### From struggle patterns (evaluated)
|
|
46237
46297
|
Review the full session timeline for patterns where the agent struggled \u2014 multiple attempts with variations before finding the correct approach. Look for:
|
|
46238
46298
|
- Repeated EMPTY results with name/field/value variations followed by eventual success
|
|
@@ -46591,7 +46651,7 @@ function sanitizeNrqlRows(rows) {
|
|
|
46591
46651
|
}
|
|
46592
46652
|
|
|
46593
46653
|
// src/lib/shared-prompts.ts
|
|
46594
|
-
var UNIFIED_ROLE_INTRO = `You are Tracer, an observability expert in a direct conversation with a developer. You have DIRECT access to the query tools of multiple providers at once \u2014 each provider's syntax, fields, and debugging guidance are documented below. Pick the right provider(s) for each question; when a question spans providers, query them and correlate across the results in one investigation. You have full conversation history and can reference previous messages. You run as an AUTONOMOUS MULTI-STEP AGENT \u2014 after each tool call you automatically receive results and CAN (and often SHOULD) make additional tool calls before finishing.`;
|
|
46654
|
+
var UNIFIED_ROLE_INTRO = `You are Tracer, an observability expert in a direct conversation with a developer. You have DIRECT access to the query tools of multiple providers at once \u2014 each provider's syntax, common fields, and debugging guidance are documented below. Pick the right provider(s) for each question; when a question spans providers, query them and correlate across the results in one investigation. You have full conversation history and can reference previous messages. You run as an AUTONOMOUS MULTI-STEP AGENT \u2014 after each tool call you automatically receive results and CAN (and often SHOULD) make additional tool calls before finishing.`;
|
|
46595
46655
|
function buildUnifiedModePrompt(providerFragments, maxSteps) {
|
|
46596
46656
|
return `${UNIFIED_ROLE_INTRO}
|
|
46597
46657
|
|
|
@@ -46600,6 +46660,8 @@ ${buildRules({ investigation: true })}
|
|
|
46600
46660
|
|
|
46601
46661
|
${DETECTIVE_MINDSET}
|
|
46602
46662
|
|
|
46663
|
+
${EVIDENCE_GROUNDING}
|
|
46664
|
+
|
|
46603
46665
|
${EXECUTION_DISCIPLINE}
|
|
46604
46666
|
|
|
46605
46667
|
${providerFragments.join("\n\n---\n\n")}
|
|
@@ -46641,13 +46703,20 @@ You have limited steps. Every query must earn its place. Your goal is the **fast
|
|
|
46641
46703
|
3. **"Is there a single query that could answer multiple questions at once?"** \u2014 Combine work. Pack information density per query.
|
|
46642
46704
|
|
|
46643
46705
|
**"Good enough" beats "complete."** The user can always ask follow-up questions. Don't anticipate them \u2014 answer what was asked.`;
|
|
46706
|
+
var EVIDENCE_GROUNDING = `## Grounded in Evidence
|
|
46707
|
+
|
|
46708
|
+
Your only sources of truth are the literal text of tool results from this session, what the user has stated in the conversation, and what this prompt documents. Anything else is unknown \u2014 including the meaning of the data you retrieve.
|
|
46709
|
+
|
|
46710
|
+
1. **Field names and values are opaque labels.** Never translate or assign meaning to a field name, enum value, code, or flag beyond its literal text \u2014 systems attach internal meanings you cannot know. Report the raw value; if its meaning matters and is undocumented, say so.
|
|
46711
|
+
2. **Absence requires an empty probe.** Only claim something is missing, absent, or "not on file" if a query that would have returned it came back empty. Not having looked is not evidence of absence.
|
|
46712
|
+
3. **Separate facts, deductions, and gaps.** Facts restate query results. Deductions must follow from stated facts alone \u2014 present them as deductions and name the supporting results; correlation across results is not causation. Gaps are reported as "the data does not show X" \u2014 never filled with a plausible story.`;
|
|
46644
46713
|
var NO_FIXES_RULE = `**NEVER suggest fixes, remediation, next steps, or actions.** Forbidden phrasings include: "consider," "you should," "try," "might want to," "recommend," "could help," "suggests [action]," "would resolve," "to fix this." Any sentence about what to DO about the problem is forbidden, regardless of phrasing. Your job ends at "here is what happened and the evidence." The developer decides what to do.`;
|
|
46645
46714
|
var EXECUTION_DISCIPLINE = `## Execution Discipline
|
|
46646
46715
|
|
|
46647
46716
|
For multi-step investigations:
|
|
46648
46717
|
1. **Step N: [Goal]** \u2014 state what gap this fills
|
|
46649
46718
|
2. **Tool call** \u2192 ONE query
|
|
46650
|
-
3. **\u2192 Found:** [data] **\u2192 So what:** [
|
|
46719
|
+
3. **\u2192 Found:** [data] **\u2192 So what:** [only what this data supports \u2014 if it needs an assumption, it's a gap, not a finding]
|
|
46651
46720
|
4. **\u2192 Can I answer now?** \u2014 If YES: respond. If NO: state what's missing.
|
|
46652
46721
|
|
|
46653
46722
|
For simple questions (counts, lookups), skip this \u2014 just answer directly.`;
|
|
@@ -46661,6 +46730,7 @@ function analysisBlock() {
|
|
|
46661
46730
|
|
|
46662
46731
|
1. **Think first** \u2014 before writing anything, plan the evidence chain in your head:
|
|
46663
46732
|
- Known facts from query results, inferences that follow from them, and remaining gaps.
|
|
46733
|
+
- Self-audit each claim against the tool results already in this session: if no specific result backs it, drop it or present it explicitly as unverified. This is an in-head check \u2014 never run extra investigation queries for it.
|
|
46664
46734
|
- Which queries best VISUALIZE each finding \u2014 these become the tool calls you will run in this section.
|
|
46665
46735
|
- Do not start writing until you have a clear chain and a concrete list of visuals to run.
|
|
46666
46736
|
2. ${markerStep}
|
|
@@ -46686,7 +46756,7 @@ You have a maximum of ${maxSteps} steps. Most investigations should finish in 3-
|
|
|
46686
46756
|
|
|
46687
46757
|
## Final Reminders
|
|
46688
46758
|
- **Tool calls are the evidence.** Every substantive claim in your response needs a visual \u2014 even if the same query already ran during investigation, re-run it here. The analysis section must be self-contained.
|
|
46689
|
-
- **
|
|
46759
|
+
- **Stay Grounded in Evidence:** every claim maps to a specific tool result; values mean only what their literal text says; absence claims need an empty probe; gaps are stated as "the data does not show". No fixes.`;
|
|
46690
46760
|
}
|
|
46691
46761
|
|
|
46692
46762
|
// src/lib/prompt-builder.ts
|
|
@@ -46715,6 +46785,8 @@ ${buildRules({ investigation: true, extraRules: config2.extraRules })}
|
|
|
46715
46785
|
|
|
46716
46786
|
${DETECTIVE_MINDSET}
|
|
46717
46787
|
|
|
46788
|
+
${EVIDENCE_GROUNDING}
|
|
46789
|
+
|
|
46718
46790
|
${config2.insideOutDebugging}
|
|
46719
46791
|
|
|
46720
46792
|
${EXECUTION_DISCIPLINE}
|
|
@@ -56769,6 +56841,137 @@ var memoryRouter = router({
|
|
|
56769
56841
|
})
|
|
56770
56842
|
});
|
|
56771
56843
|
|
|
56844
|
+
// src/agents/utility/summary.ts
|
|
56845
|
+
var SUMMARY_SYSTEM_PROMPT = `You are compacting an AI debugging-assistant conversation into a detailed summary. Your summary will permanently REPLACE the original messages as the assistant's only memory of them, so anything you omit is lost forever. The assistant must be able to continue the investigation from your summary alone without redoing any completed work.
|
|
56846
|
+
|
|
56847
|
+
Write the summary as markdown with exactly these sections:
|
|
56848
|
+
|
|
56849
|
+
## Original intent
|
|
56850
|
+
What the user set out to do, in their own framing. Include later refinements or pivots of the goal.
|
|
56851
|
+
|
|
56852
|
+
## User requests & decisions
|
|
56853
|
+
Chronological list of every instruction, question, constraint, correction, and approval or rejection the user gave, and whether each was fulfilled. The assistant must be able to tell from this section alone what the user has and has not asked for.
|
|
56854
|
+
|
|
56855
|
+
## Investigation log
|
|
56856
|
+
Numbered, chronological. Each entry pairs an action with its result:
|
|
56857
|
+
1. One line on what was done and why, then the exact tool call, query, or API call (with its parameters and the time range it covered) in a fenced code block.
|
|
56858
|
+
|
|
56859
|
+
\u2192 Result: what it returned, with the key values verbatim. Render a result with more than one row (facets, top-N lists, table rows) as a markdown table; copy up to roughly 20 rows verbatim and note what was cut.
|
|
56860
|
+
|
|
56861
|
+
Never record an action without its result \u2014 an unpaired action forces the assistant to re-run it.
|
|
56862
|
+
|
|
56863
|
+
## Key findings & data (verbatim)
|
|
56864
|
+
The distilled facts the investigation established, with exact values copied verbatim \u2014 never paraphrase these:
|
|
56865
|
+
- IDs of any kind (trace IDs, entity GUIDs, account/project IDs, session IDs)
|
|
56866
|
+
- Exact error messages and stack-trace lines
|
|
56867
|
+
- File paths, service names, host names, URLs
|
|
56868
|
+
- Numbers: counts, rates, percentages, latencies, timestamps, time ranges
|
|
56869
|
+
Do not re-copy queries or result tables that already appear in the Investigation log \u2014 state the facts they established and name the log entry they came from.
|
|
56870
|
+
|
|
56871
|
+
## What did NOT work (dead ends)
|
|
56872
|
+
Approaches tried and abandoned, queries that errored or returned empty, hypotheses ruled out \u2014 and WHY each failed. This prevents the assistant from repeating them. If nothing failed, write "None."
|
|
56873
|
+
|
|
56874
|
+
## Conclusions & current state
|
|
56875
|
+
Each conclusion the assistant reached, stated together with the evidence supporting it, so it is never re-derived. What was communicated or delivered to the user (answers, recommendations, reports), and any artifacts produced.
|
|
56876
|
+
|
|
56877
|
+
## Open items
|
|
56878
|
+
Unresolved questions, pending next steps, anything the user asked for that has not been delivered yet. If none, write "None."
|
|
56879
|
+
|
|
56880
|
+
Rules:
|
|
56881
|
+
- Be detailed. Length is not a concern; losing information is. A long, precise summary is always better than a short, vague one.
|
|
56882
|
+
- Copy identifiers, queries, errors, and numbers character-for-character from the conversation.
|
|
56883
|
+
- Format for scanning: queries and commands go in fenced code blocks, multi-row results in markdown tables, and inline identifiers (service names, error classes, IDs, paths) in backticks.
|
|
56884
|
+
- State each piece of data in full exactly once, in the section where it belongs; later mentions reference it instead of repeating it.
|
|
56885
|
+
- Always pair what was run with what it returned.
|
|
56886
|
+
- If the conversation contains an analysis or post-mortem report (the begin_analysis tool marks where one starts), carry its content through verbatim in the relevant sections instead of re-summarizing it.
|
|
56887
|
+
- If an existing summary of older messages is provided, merge it with the new segment into ONE self-contained summary covering everything. Preserve all verbatim data from the existing summary unless the new segment explicitly supersedes it.
|
|
56888
|
+
- Do not add commentary, advice, or information that is not in the conversation.
|
|
56889
|
+
- Output only the summary markdown, nothing else.`;
|
|
56890
|
+
var TOOL_INPUT_CHAR_LIMIT = 2e3;
|
|
56891
|
+
var TOOL_OUTPUT_CHAR_LIMIT = 6e3;
|
|
56892
|
+
var GENERATION_TIMEOUT_MS = 5 * 6e4;
|
|
56893
|
+
function truncate(value, limit) {
|
|
56894
|
+
if (value === void 0) return "(none)";
|
|
56895
|
+
let text3;
|
|
56896
|
+
try {
|
|
56897
|
+
text3 = typeof value === "string" ? value : JSON.stringify(
|
|
56898
|
+
value,
|
|
56899
|
+
(_key, v) => typeof v === "string" && v.length > limit ? v.slice(0, limit) : v
|
|
56900
|
+
);
|
|
56901
|
+
} catch {
|
|
56902
|
+
text3 = String(value);
|
|
56903
|
+
}
|
|
56904
|
+
return text3.length > limit ? `${text3.slice(0, limit)}\u2026 (truncated)` : text3;
|
|
56905
|
+
}
|
|
56906
|
+
function serializeMessagesForSummary(messages) {
|
|
56907
|
+
const blocks = [];
|
|
56908
|
+
messages.forEach((msg, i) => {
|
|
56909
|
+
const lines = [`### Message ${i + 1} \u2014 ${msg.role}`];
|
|
56910
|
+
for (const part of msg.parts) {
|
|
56911
|
+
if (part.type === "text") {
|
|
56912
|
+
lines.push(part.text);
|
|
56913
|
+
continue;
|
|
56914
|
+
}
|
|
56915
|
+
if (part.type === "reasoning" || part.type === "step-start" || part.type.startsWith("data-") || part.type === "file") {
|
|
56916
|
+
continue;
|
|
56917
|
+
}
|
|
56918
|
+
const p = part;
|
|
56919
|
+
if (p.toolCallId) {
|
|
56920
|
+
const name26 = p.type.startsWith("tool-") ? p.type.slice(5) : p.type;
|
|
56921
|
+
const output = p.output !== void 0 ? p.output : p.errorText !== void 0 ? `error: ${p.errorText}` : void 0;
|
|
56922
|
+
lines.push(
|
|
56923
|
+
`[tool: ${name26}]`,
|
|
56924
|
+
`input: ${truncate(p.input, TOOL_INPUT_CHAR_LIMIT)}`,
|
|
56925
|
+
`output: ${truncate(output, TOOL_OUTPUT_CHAR_LIMIT)}`
|
|
56926
|
+
);
|
|
56927
|
+
}
|
|
56928
|
+
}
|
|
56929
|
+
blocks.push(lines.join("\n"));
|
|
56930
|
+
});
|
|
56931
|
+
return blocks.join("\n\n");
|
|
56932
|
+
}
|
|
56933
|
+
async function generateSessionSummary(db2, opts) {
|
|
56934
|
+
const resolved = resolveModel(db2);
|
|
56935
|
+
if ("error" in resolved) {
|
|
56936
|
+
console.warn("[summary] Cannot generate summary:", resolved.error);
|
|
56937
|
+
return { error: resolved.error, config: true };
|
|
56938
|
+
}
|
|
56939
|
+
const serialized = serializeMessagesForSummary(opts.messages);
|
|
56940
|
+
let userContent = opts.priorSummary ? `## Existing summary of older messages (merge into your output)
|
|
56941
|
+
${opts.priorSummary}
|
|
56942
|
+
|
|
56943
|
+
## New conversation segment to incorporate
|
|
56944
|
+
${serialized}` : `## Conversation to summarize
|
|
56945
|
+
${serialized}`;
|
|
56946
|
+
if (opts.keptAnalysis) {
|
|
56947
|
+
userContent += `
|
|
56948
|
+
|
|
56949
|
+
Note: the assistant's final analysis of the last exchange is preserved verbatim in the conversation right after your summary \u2014 record the work and results above without inventing or restating its conclusions.`;
|
|
56950
|
+
}
|
|
56951
|
+
try {
|
|
56952
|
+
const { text: text3, usage } = await generateText({
|
|
56953
|
+
model: resolved.model,
|
|
56954
|
+
temperature: 0,
|
|
56955
|
+
system: SUMMARY_SYSTEM_PROMPT,
|
|
56956
|
+
messages: [{ role: "user", content: userContent }],
|
|
56957
|
+
providerOptions: resolved.providerOptions,
|
|
56958
|
+
abortSignal: AbortSignal.timeout(GENERATION_TIMEOUT_MS)
|
|
56959
|
+
});
|
|
56960
|
+
const summary = text3.trim();
|
|
56961
|
+
if (!summary) return { error: "Summary generation returned no content" };
|
|
56962
|
+
recordAgentRun(db2, {
|
|
56963
|
+
sessionId: opts.sessionId,
|
|
56964
|
+
agentType: "summary",
|
|
56965
|
+
model: resolved.modelId,
|
|
56966
|
+
usage: extractUsage(usage, resolved.modelId)
|
|
56967
|
+
});
|
|
56968
|
+
return { summary };
|
|
56969
|
+
} catch (err) {
|
|
56970
|
+
console.warn("[summary] Failed to generate summary:", err);
|
|
56971
|
+
return { error: "Summary generation failed" };
|
|
56972
|
+
}
|
|
56973
|
+
}
|
|
56974
|
+
|
|
56772
56975
|
// src/trpc/routers/sessions.router.ts
|
|
56773
56976
|
var AGENT_TYPE_LABELS = {
|
|
56774
56977
|
chat: "Chat",
|
|
@@ -56776,7 +56979,8 @@ var AGENT_TYPE_LABELS = {
|
|
|
56776
56979
|
gcp: "GCP sub-agent",
|
|
56777
56980
|
posthog: "PostHog sub-agent",
|
|
56778
56981
|
title: "Title gen",
|
|
56779
|
-
memory: "Memory"
|
|
56982
|
+
memory: "Memory",
|
|
56983
|
+
summary: "Compaction"
|
|
56780
56984
|
};
|
|
56781
56985
|
var sessionsRouter = router({
|
|
56782
56986
|
list: publicProcedure.query(({ ctx }) => {
|
|
@@ -56811,7 +57015,10 @@ var sessionsRouter = router({
|
|
|
56811
57015
|
status: row.status,
|
|
56812
57016
|
kind: row.kind,
|
|
56813
57017
|
messages,
|
|
56814
|
-
updatedAt: row.updatedAt
|
|
57018
|
+
updatedAt: row.updatedAt,
|
|
57019
|
+
summary: row.summary,
|
|
57020
|
+
summaryUpTo: row.summaryUpTo,
|
|
57021
|
+
summaryCreatedAt: row.summaryCreatedAt
|
|
56815
57022
|
};
|
|
56816
57023
|
}),
|
|
56817
57024
|
getCost: publicProcedure.input(external_exports.object({ id: external_exports.string() })).query(({ ctx, input }) => {
|
|
@@ -56904,19 +57111,104 @@ var sessionsRouter = router({
|
|
|
56904
57111
|
return { id };
|
|
56905
57112
|
}),
|
|
56906
57113
|
truncateMessages: publicProcedure.input(external_exports.object({ id: external_exports.string(), keepCount: external_exports.number().int().min(0) })).mutation(({ ctx, input }) => {
|
|
56907
|
-
const row = ctx.db.select({ messages: chatSessions.messages }).from(chatSessions).where(eq(chatSessions.id, input.id)).get();
|
|
56908
|
-
if (!row) return { success: false };
|
|
57114
|
+
const row = ctx.db.select({ messages: chatSessions.messages, summaryUpTo: chatSessions.summaryUpTo }).from(chatSessions).where(eq(chatSessions.id, input.id)).get();
|
|
57115
|
+
if (!row) return { success: false, summaryCleared: false };
|
|
56909
57116
|
let messages = [];
|
|
56910
57117
|
try {
|
|
56911
57118
|
messages = JSON.parse(row.messages);
|
|
56912
57119
|
} catch {
|
|
56913
|
-
return { success: false };
|
|
57120
|
+
return { success: false, summaryCleared: false };
|
|
56914
57121
|
}
|
|
56915
57122
|
const truncated = messages.slice(0, input.keepCount);
|
|
57123
|
+
const keptAnalysisBoundary = row.summaryUpTo != null && isAnalysisMessage(messages[row.summaryUpTo] ?? { role: "" });
|
|
57124
|
+
const sourceUpTo = row.summaryUpTo != null ? row.summaryUpTo + (keptAnalysisBoundary ? 1 : 0) : 0;
|
|
57125
|
+
const summaryStale = row.summaryUpTo != null && input.keepCount < sourceUpTo;
|
|
56916
57126
|
ctx.db.update(chatSessions).set({
|
|
56917
57127
|
messages: JSON.stringify(truncated),
|
|
56918
|
-
updatedAt: unixNow()
|
|
57128
|
+
updatedAt: unixNow(),
|
|
57129
|
+
...summaryStale ? { summary: null, summaryUpTo: null, summaryCreatedAt: null } : {}
|
|
56919
57130
|
}).where(eq(chatSessions.id, input.id)).run();
|
|
57131
|
+
return { success: true, summaryCleared: summaryStale };
|
|
57132
|
+
}),
|
|
57133
|
+
compact: publicProcedure.input(external_exports.object({ id: external_exports.string(), upToIndex: external_exports.number().int().min(0) })).mutation(async ({ ctx, input }) => {
|
|
57134
|
+
const row = ctx.db.select().from(chatSessions).where(eq(chatSessions.id, input.id)).get();
|
|
57135
|
+
if (!row) throw new TRPCError({ code: "NOT_FOUND", message: "Session not found" });
|
|
57136
|
+
if (row.status === "streaming" || ctx.activeStreams.has(input.id)) {
|
|
57137
|
+
throw new TRPCError({ code: "CONFLICT", message: "Cannot compact while a response is in progress" });
|
|
57138
|
+
}
|
|
57139
|
+
let messages;
|
|
57140
|
+
try {
|
|
57141
|
+
messages = JSON.parse(row.messages);
|
|
57142
|
+
} catch {
|
|
57143
|
+
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Session messages are corrupted" });
|
|
57144
|
+
}
|
|
57145
|
+
const boundaryIdx = input.upToIndex;
|
|
57146
|
+
if (boundaryIdx >= messages.length) {
|
|
57147
|
+
throw new TRPCError({ code: "BAD_REQUEST", message: "Message not found in session" });
|
|
57148
|
+
}
|
|
57149
|
+
if (messages[boundaryIdx].role !== "assistant") {
|
|
57150
|
+
throw new TRPCError({ code: "BAD_REQUEST", message: "Can only compact up to an assistant message" });
|
|
57151
|
+
}
|
|
57152
|
+
const summaryUpTo = compactionUpTo(messages, boundaryIdx);
|
|
57153
|
+
if (summaryUpTo == null) {
|
|
57154
|
+
throw new TRPCError({ code: "BAD_REQUEST", message: "There are no messages to summarize before the analysis" });
|
|
57155
|
+
}
|
|
57156
|
+
const keptAnalysis = isAnalysisMessage(messages[boundaryIdx]);
|
|
57157
|
+
const incremental = !!row.summary && row.summaryUpTo != null && row.summaryUpTo < summaryUpTo;
|
|
57158
|
+
const segment = messages.slice(incremental ? row.summaryUpTo : 0, boundaryIdx + 1);
|
|
57159
|
+
if (incremental) {
|
|
57160
|
+
const head = segment[0];
|
|
57161
|
+
if (isAnalysisMessage(head)) {
|
|
57162
|
+
const split = splitAtAnalysis(head.parts);
|
|
57163
|
+
if (split) segment[0] = { ...head, parts: split.analysis };
|
|
57164
|
+
}
|
|
57165
|
+
}
|
|
57166
|
+
if (keptAnalysis) {
|
|
57167
|
+
const last = segment[segment.length - 1];
|
|
57168
|
+
const split = splitAtAnalysis(last.parts);
|
|
57169
|
+
if (split) segment[segment.length - 1] = { ...last, parts: split.before };
|
|
57170
|
+
}
|
|
57171
|
+
const result = await generateSessionSummary(ctx.db, {
|
|
57172
|
+
sessionId: input.id,
|
|
57173
|
+
priorSummary: incremental ? row.summary : void 0,
|
|
57174
|
+
messages: segment,
|
|
57175
|
+
keptAnalysis
|
|
57176
|
+
});
|
|
57177
|
+
if ("error" in result) {
|
|
57178
|
+
throw new TRPCError({
|
|
57179
|
+
code: result.config ? "PRECONDITION_FAILED" : "INTERNAL_SERVER_ERROR",
|
|
57180
|
+
message: result.error
|
|
57181
|
+
});
|
|
57182
|
+
}
|
|
57183
|
+
const fresh = ctx.db.select({ messages: chatSessions.messages, status: chatSessions.status }).from(chatSessions).where(eq(chatSessions.id, input.id)).get();
|
|
57184
|
+
const sourceUpTo = boundaryIdx + 1;
|
|
57185
|
+
let prefixUnchanged = false;
|
|
57186
|
+
if (fresh && fresh.status !== "streaming" && !ctx.activeStreams.has(input.id)) {
|
|
57187
|
+
try {
|
|
57188
|
+
const freshMessages = JSON.parse(fresh.messages);
|
|
57189
|
+
prefixUnchanged = JSON.stringify(freshMessages.slice(0, sourceUpTo)) === JSON.stringify(messages.slice(0, sourceUpTo));
|
|
57190
|
+
} catch {
|
|
57191
|
+
}
|
|
57192
|
+
}
|
|
57193
|
+
if (!prefixUnchanged) {
|
|
57194
|
+
throw new TRPCError({ code: "CONFLICT", message: "The conversation changed while the summary was being generated \u2014 try again" });
|
|
57195
|
+
}
|
|
57196
|
+
const summary = result.summary;
|
|
57197
|
+
const summaryCreatedAt = unixNow();
|
|
57198
|
+
ctx.db.update(chatSessions).set({ summary, summaryUpTo, summaryCreatedAt }).where(eq(chatSessions.id, input.id)).run();
|
|
57199
|
+
return { summary, summaryUpTo, summaryCreatedAt };
|
|
57200
|
+
}),
|
|
57201
|
+
updateSummary: publicProcedure.input(external_exports.object({ id: external_exports.string(), summary: external_exports.string().min(1) })).mutation(({ ctx, input }) => {
|
|
57202
|
+
const row = ctx.db.select({ summaryUpTo: chatSessions.summaryUpTo }).from(chatSessions).where(eq(chatSessions.id, input.id)).get();
|
|
57203
|
+
if (!row) throw new TRPCError({ code: "NOT_FOUND", message: "Session not found" });
|
|
57204
|
+
if (row.summaryUpTo == null) {
|
|
57205
|
+
throw new TRPCError({ code: "CONFLICT", message: "The summary no longer exists" });
|
|
57206
|
+
}
|
|
57207
|
+
ctx.db.update(chatSessions).set({ summary: input.summary }).where(eq(chatSessions.id, input.id)).run();
|
|
57208
|
+
return { success: true };
|
|
57209
|
+
}),
|
|
57210
|
+
clearSummary: publicProcedure.input(external_exports.object({ id: external_exports.string() })).mutation(({ ctx, input }) => {
|
|
57211
|
+
ctx.db.update(chatSessions).set({ summary: null, summaryUpTo: null, summaryCreatedAt: null }).where(eq(chatSessions.id, input.id)).run();
|
|
56920
57212
|
return { success: true };
|
|
56921
57213
|
})
|
|
56922
57214
|
});
|
|
@@ -57495,7 +57787,11 @@ function loadSessionMessages(db2, sessionId, newMessage) {
|
|
|
57495
57787
|
console.warn(`[chat] Corrupted session ${sessionId}, starting fresh`);
|
|
57496
57788
|
}
|
|
57497
57789
|
}
|
|
57498
|
-
return
|
|
57790
|
+
return {
|
|
57791
|
+
messages: [...sanitizeMessages(previous), newMessage],
|
|
57792
|
+
summary: existing?.summary ?? null,
|
|
57793
|
+
summaryUpTo: existing?.summaryUpTo ?? null
|
|
57794
|
+
};
|
|
57499
57795
|
}
|
|
57500
57796
|
function finalizeSession(sessionId, context2, broadcaster) {
|
|
57501
57797
|
if (!context2.activeStreams.has(sessionId)) return;
|
|
@@ -57503,7 +57799,7 @@ function finalizeSession(sessionId, context2, broadcaster) {
|
|
|
57503
57799
|
broadcaster.finish();
|
|
57504
57800
|
context2.activeStreams.delete(sessionId);
|
|
57505
57801
|
}
|
|
57506
|
-
async function processLLMStream(sessionId, messages, context2, broadcaster, serverAbort, collectTools, sessionTitle, model, modelId, providerOptions) {
|
|
57802
|
+
async function processLLMStream(sessionId, messages, context2, broadcaster, serverAbort, collectTools, sessionTitle, model, modelId, providerOptions, compaction) {
|
|
57507
57803
|
const writer = {
|
|
57508
57804
|
write: (part) => {
|
|
57509
57805
|
const p = part;
|
|
@@ -57514,7 +57810,29 @@ async function processLLMStream(sessionId, messages, context2, broadcaster, serv
|
|
|
57514
57810
|
};
|
|
57515
57811
|
const collected = collectTools(writer);
|
|
57516
57812
|
const tools = collected.tools;
|
|
57517
|
-
|
|
57813
|
+
let modelInput = messages;
|
|
57814
|
+
let summaryForPrompt = null;
|
|
57815
|
+
if (compaction.summary && compaction.summaryUpTo && compaction.summaryUpTo < messages.length) {
|
|
57816
|
+
const tail = messages.slice(compaction.summaryUpTo);
|
|
57817
|
+
if (tail[0].role === "user") {
|
|
57818
|
+
modelInput = tail;
|
|
57819
|
+
summaryForPrompt = compaction.summary;
|
|
57820
|
+
} else {
|
|
57821
|
+
const split = splitAtAnalysis(tail[0].parts);
|
|
57822
|
+
if (split && split.analysis.length > 0) {
|
|
57823
|
+
tail[0] = { ...tail[0], parts: split.analysis };
|
|
57824
|
+
modelInput = [
|
|
57825
|
+
{ id: "", role: "user", parts: [{ type: "text", text: "(The conversation up to this point was compacted into the summary in your instructions.)" }] },
|
|
57826
|
+
...tail
|
|
57827
|
+
];
|
|
57828
|
+
summaryForPrompt = compaction.summary;
|
|
57829
|
+
}
|
|
57830
|
+
}
|
|
57831
|
+
}
|
|
57832
|
+
if (compaction.summary && !summaryForPrompt) {
|
|
57833
|
+
console.warn(`[chat] Ignoring stale compaction boundary for ${sessionId} (summaryUpTo=${compaction.summaryUpTo}, messages=${messages.length})`);
|
|
57834
|
+
}
|
|
57835
|
+
const modelMessages = await convertToModelMessages(modelInput, {
|
|
57518
57836
|
tools,
|
|
57519
57837
|
convertDataPart: () => void 0
|
|
57520
57838
|
});
|
|
@@ -57535,6 +57853,16 @@ ${fragments.join("\n\n")}` : `${basePrompt}
|
|
|
57535
57853
|
No observability providers are currently configured. If the user asks about observability data, let them know they can connect providers in the Settings page.`;
|
|
57536
57854
|
}
|
|
57537
57855
|
systemPrompt += "\n\n" + getCurrentDateBlock(context2.db);
|
|
57856
|
+
if (summaryForPrompt) {
|
|
57857
|
+
systemPrompt += `
|
|
57858
|
+
|
|
57859
|
+
## Earlier conversation summary
|
|
57860
|
+
The earlier part of this conversation was compacted to save context. The summary below replaces those messages and is authoritative: the work it describes is already done \u2014 do NOT redo it. Reuse its recorded results, identifiers, queries, and conclusions.
|
|
57861
|
+
|
|
57862
|
+
<conversation_summary>
|
|
57863
|
+
${summaryForPrompt}
|
|
57864
|
+
</conversation_summary>`;
|
|
57865
|
+
}
|
|
57538
57866
|
const result = streamText({
|
|
57539
57867
|
model,
|
|
57540
57868
|
temperature: 0,
|
|
@@ -57648,7 +57976,7 @@ No observability providers are currently configured. If the user asks about obse
|
|
|
57648
57976
|
clearTimeout(timeoutId);
|
|
57649
57977
|
finalizeSession(sessionId, context2, broadcaster);
|
|
57650
57978
|
}
|
|
57651
|
-
async function runChatAgent({ sessionId, messages, context: context2, collectTools, sessionTitle, modelOverride }) {
|
|
57979
|
+
async function runChatAgent({ sessionId, messages, summary, summaryUpTo, context: context2, collectTools, sessionTitle, modelOverride }) {
|
|
57652
57980
|
const resolved = modelOverride ?? resolveModel(context2.db);
|
|
57653
57981
|
if ("error" in resolved) return { error: resolved.error };
|
|
57654
57982
|
const { model, modelId, providerOptions } = resolved;
|
|
@@ -57680,7 +58008,8 @@ async function runChatAgent({ sessionId, messages, context: context2, collectToo
|
|
|
57680
58008
|
sessionTitle,
|
|
57681
58009
|
model,
|
|
57682
58010
|
modelId,
|
|
57683
|
-
providerOptions
|
|
58011
|
+
providerOptions,
|
|
58012
|
+
{ summary, summaryUpTo }
|
|
57684
58013
|
).catch((err) => {
|
|
57685
58014
|
console.error(`[chat] Unhandled error in LLM processing for ${sessionId}:`, err);
|
|
57686
58015
|
finalizeSession(sessionId, context2, broadcaster);
|
|
@@ -57804,7 +58133,7 @@ function nextYPosition(db2, dashboardId) {
|
|
|
57804
58133
|
}
|
|
57805
58134
|
function collectDashboardTools(registry2, db2, writer, dashboardId) {
|
|
57806
58135
|
const dbId = dashboardId ?? "";
|
|
57807
|
-
const { tools, promptFragments, connectedProviders } = collectBaseTools(registry2, db2, writer);
|
|
58136
|
+
const { tools, promptFragments, connectedProviders } = collectBaseTools(registry2, db2, writer, "unified");
|
|
57808
58137
|
const defaultProvider = connectedProviders[0];
|
|
57809
58138
|
tools.create_widget = tool({
|
|
57810
58139
|
description: "Create a new dashboard widget. The query will be validated by executing it first. The widget auto-positions below existing widgets.",
|
|
@@ -57943,7 +58272,7 @@ The global date picker already shows the active time range, so titles should des
|
|
|
57943
58272
|
|
|
57944
58273
|
## Scope
|
|
57945
58274
|
You are managing widgets for the current dashboard only. The widget list above shows only this dashboard's widgets.`;
|
|
57946
|
-
const systemPrompt = [basePrompt, providerContext, widgetContext, ...promptFragments].join("\n\n");
|
|
58275
|
+
const systemPrompt = [basePrompt, EVIDENCE_GROUNDING, providerContext, widgetContext, ...promptFragments].join("\n\n");
|
|
57947
58276
|
return { tools, systemPrompt };
|
|
57948
58277
|
}
|
|
57949
58278
|
|
|
@@ -58250,7 +58579,7 @@ function getMonitorContext(db2) {
|
|
|
58250
58579
|
${lines.join("\n")}`;
|
|
58251
58580
|
}
|
|
58252
58581
|
function collectMonitorTools(registry2, db2, writer) {
|
|
58253
|
-
const { tools, promptFragments, connectedProviders } = collectBaseTools(registry2, db2, writer);
|
|
58582
|
+
const { tools, promptFragments, connectedProviders } = collectBaseTools(registry2, db2, writer, "unified");
|
|
58254
58583
|
const defaultProvider = connectedProviders[0];
|
|
58255
58584
|
tools.create_monitor = tool({
|
|
58256
58585
|
description: "Create a new monitor that periodically checks a query and alerts when a condition is met.",
|
|
@@ -58408,7 +58737,7 @@ The condition is a JS expression evaluated against the query \`result\` array. E
|
|
|
58408
58737
|
|
|
58409
58738
|
## Scope
|
|
58410
58739
|
You are managing all monitors. The monitor list above shows all existing monitors.`;
|
|
58411
|
-
const systemPrompt = [basePrompt, providerContext, monitorContext, ...promptFragments].join("\n\n");
|
|
58740
|
+
const systemPrompt = [basePrompt, EVIDENCE_GROUNDING, providerContext, monitorContext, ...promptFragments].join("\n\n");
|
|
58412
58741
|
return { tools, systemPrompt };
|
|
58413
58742
|
}
|
|
58414
58743
|
|
|
@@ -58448,7 +58777,7 @@ function generateSessionTitle(db2, sessionId, userMessage) {
|
|
|
58448
58777
|
function registerChatRoutes(app, context2) {
|
|
58449
58778
|
app.post("/api/chat", async (c) => {
|
|
58450
58779
|
const { id, message, activeProvider } = await c.req.json();
|
|
58451
|
-
const messages = loadSessionMessages(context2.db, id, message);
|
|
58780
|
+
const { messages, summary, summaryUpTo } = loadSessionMessages(context2.db, id, message);
|
|
58452
58781
|
if (messages.length === 1) {
|
|
58453
58782
|
const textPart = message.parts?.find((p) => p.type === "text");
|
|
58454
58783
|
if (textPart) {
|
|
@@ -58462,6 +58791,8 @@ function registerChatRoutes(app, context2) {
|
|
|
58462
58791
|
const result = await runChatAgent({
|
|
58463
58792
|
sessionId: id,
|
|
58464
58793
|
messages,
|
|
58794
|
+
summary,
|
|
58795
|
+
summaryUpTo,
|
|
58465
58796
|
context: context2,
|
|
58466
58797
|
collectTools: (writer) => collectChatTools(context2.providers, context2.db, writer, scopedProvider, mode),
|
|
58467
58798
|
sessionTitle: (updatedMessages) => {
|
|
@@ -58477,10 +58808,12 @@ function registerChatRoutes(app, context2) {
|
|
|
58477
58808
|
app.post("/api/dashboard-chat", async (c) => {
|
|
58478
58809
|
const { id, message, dashboardId } = await c.req.json();
|
|
58479
58810
|
const sessionId = dashboardSessionId(dashboardId);
|
|
58480
|
-
const messages = loadSessionMessages(context2.db, sessionId, message);
|
|
58811
|
+
const { messages, summary, summaryUpTo } = loadSessionMessages(context2.db, sessionId, message);
|
|
58481
58812
|
const result = await runChatAgent({
|
|
58482
58813
|
sessionId,
|
|
58483
58814
|
messages,
|
|
58815
|
+
summary,
|
|
58816
|
+
summaryUpTo,
|
|
58484
58817
|
context: context2,
|
|
58485
58818
|
collectTools: (writer) => collectDashboardTools(context2.providers, context2.db, writer, dashboardId),
|
|
58486
58819
|
sessionTitle: () => "Dashboard Builder"
|
|
@@ -58491,10 +58824,12 @@ function registerChatRoutes(app, context2) {
|
|
|
58491
58824
|
app.post("/api/monitor-chat", async (c) => {
|
|
58492
58825
|
const { message } = await c.req.json();
|
|
58493
58826
|
const sessionId = SESSION_PREFIX.MONITORS;
|
|
58494
|
-
const messages = loadSessionMessages(context2.db, sessionId, message);
|
|
58827
|
+
const { messages, summary, summaryUpTo } = loadSessionMessages(context2.db, sessionId, message);
|
|
58495
58828
|
const result = await runChatAgent({
|
|
58496
58829
|
sessionId,
|
|
58497
58830
|
messages,
|
|
58831
|
+
summary,
|
|
58832
|
+
summaryUpTo,
|
|
58498
58833
|
context: context2,
|
|
58499
58834
|
collectTools: (writer) => collectMonitorTools(context2.providers, context2.db, writer),
|
|
58500
58835
|
sessionTitle: () => "Monitor Builder"
|
|
@@ -58645,7 +58980,7 @@ function registerApiRoutes(app, context2) {
|
|
|
58645
58980
|
role: "user",
|
|
58646
58981
|
parts: [{ type: "text", text: message }]
|
|
58647
58982
|
};
|
|
58648
|
-
const messages = loadSessionMessages(context2.db, sessionId, userMessage);
|
|
58983
|
+
const { messages, summary, summaryUpTo } = loadSessionMessages(context2.db, sessionId, userMessage);
|
|
58649
58984
|
if (messages.length === 1) {
|
|
58650
58985
|
generateSessionTitle(context2.db, sessionId, message);
|
|
58651
58986
|
}
|
|
@@ -58658,6 +58993,8 @@ function registerApiRoutes(app, context2) {
|
|
|
58658
58993
|
const result = await runChatAgent({
|
|
58659
58994
|
sessionId,
|
|
58660
58995
|
messages,
|
|
58996
|
+
summary,
|
|
58997
|
+
summaryUpTo,
|
|
58661
58998
|
context: context2,
|
|
58662
58999
|
collectTools: (writer) => {
|
|
58663
59000
|
const collected = collectChatTools(context2.providers, context2.db, writer, scopedProvider, mode);
|
package/packages/web/dist/assets/{SearchableSelect-B006wjjM.js → SearchableSelect-BPzDtKhB.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as q,r as s,j as r}from"./index-
|
|
1
|
+
import{a as q,r as s,j as r}from"./index-IihLiR83.js";var P=q();function $(c){const n=c?`tracer:starred:${c}`:null,[i,S]=s.useState(()=>{if(!n)return new Set;try{const a=localStorage.getItem(n);return a?new Set(JSON.parse(a)):new Set}catch{return new Set}});return[i,a=>{S(u=>{const t=new Set(u);return t.has(a)?t.delete(a):t.add(a),n&&localStorage.setItem(n,JSON.stringify([...t])),t})}]}function I({options:c,value:n,onChange:i,placeholder:S="Select...",storageKey:v,fitContent:a,disabled:u}){const[t,l]=s.useState(!1),[w,y]=s.useState(""),f=s.useRef(null),j=s.useRef(null),N=s.useRef(null),g=s.useRef(null),[x,C]=$(v),[m,L]=s.useState({top:0,left:0,minWidth:0});s.useEffect(()=>{if(!t)return;const e=d=>{f.current?.contains(d.target)||j.current?.contains(d.target)||l(!1)};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[t]);const R=s.useCallback(()=>{if(!f.current)return;const e=f.current.getBoundingClientRect();L({top:e.bottom+4,left:e.left,minWidth:e.width})},[]);s.useEffect(()=>{t&&(R(),y(""),requestAnimationFrame(()=>N.current?.focus()))},[t,R]);const h=c.find(e=>e.value===n),p=s.useMemo(()=>{const e=w.toLowerCase();return[...e?c.filter(o=>o.label.toLowerCase().includes(e)||o.value.toLowerCase().includes(e)):c].sort((o,b)=>{const k=x.has(o.value)?0:1,E=x.has(b.value)?0:1;return k!==E?k-E:o.label.localeCompare(b.label)})},[c,w,x]);s.useEffect(()=>{if(t&&n&&g.current){const e=g.current.querySelector(`[data-value="${CSS.escape(n)}"]`);e&&e.scrollIntoView({block:"nearest"})}},[t,n]);const W=t?P.createPortal(r.jsxs("div",{ref:j,className:"fixed z-[100] bg-white border border-[#d4d2cd] rounded shadow-lg",style:{top:m.top,left:m.left,minWidth:m.minWidth,width:a?"max-content":m.minWidth},children:[r.jsx("div",{className:"p-1.5 border-b border-[#e8e6e1]",children:r.jsx("input",{ref:N,type:"text",value:w,onChange:e=>y(e.target.value),placeholder:"Search...",className:"w-full px-2 py-1.5 text-xs text-[#2c2c2c] font-sans bg-[#f5f4f0] border border-[#e8e6e1] rounded focus:outline-none focus:border-[#2b5ea7] placeholder:text-[#9c9890]",onKeyDown:e=>{e.key==="Escape"&&l(!1),e.key==="Enter"&&p.length>0&&(i(p[0].value),l(!1))}})}),r.jsx("div",{ref:g,className:"max-h-[280px] overflow-y-auto",children:p.length===0?r.jsx("div",{className:"px-3 py-3 text-xs text-[#9c9890] text-center",children:"No projects found"}):p.map(e=>{const d=e.value===n,o=x.has(e.value);return r.jsxs("div",{"data-value":e.value,className:`flex items-center gap-1.5 px-2 py-1.5 text-xs font-sans cursor-pointer transition-colors ${d?"bg-[#2b5ea7]/10 text-[#2b5ea7]":"text-[#2c2c2c] hover:bg-[#f5f4f0]"}`,onClick:()=>{i(e.value),l(!1)},children:[r.jsx("button",{type:"button",onClick:b=>{b.stopPropagation(),C(e.value)},className:`shrink-0 w-4 h-4 flex items-center justify-center text-[10px] transition-colors ${o?"text-[#d4a017]":"text-[#d4d2cd] hover:text-[#9c9890]"}`,title:o?"Unstar":"Star to pin to top",children:o?"★":"☆"}),r.jsx("span",{className:a?"whitespace-nowrap":"truncate flex-1",children:e.label})]},e.value)})})]}),document.body):null;return r.jsxs(r.Fragment,{children:[r.jsxs("button",{ref:f,type:"button",onClick:()=>!u&&l(!t),disabled:u,className:"w-full bg-white border border-[#d4d2cd] rounded px-3 py-2 text-xs text-[#2c2c2c] font-sans text-left flex items-center justify-between focus:outline-none focus:border-[#2b5ea7] hover:border-[#b0ada6] transition-colors disabled:opacity-50 disabled:cursor-not-allowed",children:[r.jsx("span",{className:h?"text-[#2c2c2c] truncate":"text-[#9c9890]",children:h?h.displayLabel??h.label:S}),r.jsx("span",{className:"text-[#9c9890] text-[10px] ml-2 shrink-0 transition-transform duration-200",style:{transform:t?"rotate(180deg)":"rotate(0deg)"},children:"▾"})]}),W]})}export{I as S,P as r,$ as u};
|