@stjbrown/agent-knowledge 0.1.0-beta.4 → 0.1.0-beta.5
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/OBSERVABILITY.md +163 -0
- package/README.md +29 -1
- package/dist/{chunk-H4H6S4AV.js → chunk-EBMACGJV.js} +478 -40
- package/dist/chunk-EBMACGJV.js.map +1 -0
- package/dist/{chunk-RDL2AHSN.js → chunk-GWV4DABI.js} +16 -4
- package/dist/chunk-GWV4DABI.js.map +1 -0
- package/dist/{chunk-PBRTNSWS.js → chunk-PUTDZMUI.js} +4 -41
- package/dist/chunk-PUTDZMUI.js.map +1 -0
- package/dist/headless.js +2 -2
- package/dist/index.js +2 -2
- package/dist/main.js +7 -16
- package/dist/main.js.map +1 -1
- package/dist/{tui-6LNJ6LCX.js → tui-6F3IBI3J.js} +456 -43
- package/dist/tui-6F3IBI3J.js.map +1 -0
- package/package.json +6 -2
- package/dist/chunk-H4H6S4AV.js.map +0 -1
- package/dist/chunk-PBRTNSWS.js.map +0 -1
- package/dist/chunk-RDL2AHSN.js.map +0 -1
- package/dist/tui-6LNJ6LCX.js.map +0 -1
|
@@ -3,17 +3,21 @@ import { createRequire as __janetCreateRequire } from "node:module";
|
|
|
3
3
|
const require = __janetCreateRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
5
|
availableModels,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
normalizeModelSelection,
|
|
9
|
-
rememberModel
|
|
10
|
-
} from "./chunk-PBRTNSWS.js";
|
|
6
|
+
normalizeModelSelection
|
|
7
|
+
} from "./chunk-PUTDZMUI.js";
|
|
11
8
|
import {
|
|
12
9
|
GREETING,
|
|
13
10
|
bootJanet,
|
|
11
|
+
completeOnboarding,
|
|
12
|
+
formatObservabilityStatus,
|
|
14
13
|
getAuthStorage,
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
loadSettings,
|
|
15
|
+
messageText,
|
|
16
|
+
rememberModel,
|
|
17
|
+
rememberObservability,
|
|
18
|
+
resolveObservabilityConfig,
|
|
19
|
+
safeObservabilityEndpoint
|
|
20
|
+
} from "./chunk-EBMACGJV.js";
|
|
17
21
|
|
|
18
22
|
// src/tui/index.ts
|
|
19
23
|
import {
|
|
@@ -25,7 +29,8 @@ import {
|
|
|
25
29
|
SelectList,
|
|
26
30
|
Spacer,
|
|
27
31
|
TUI,
|
|
28
|
-
Text
|
|
32
|
+
Text,
|
|
33
|
+
matchesKey
|
|
29
34
|
} from "@earendil-works/pi-tui";
|
|
30
35
|
|
|
31
36
|
// src/tui/activity.ts
|
|
@@ -72,6 +77,80 @@ function toolErrorLabel(result) {
|
|
|
72
77
|
return `Tool error: ${detail.slice(0, 140)}`;
|
|
73
78
|
}
|
|
74
79
|
|
|
80
|
+
// src/tui/interrupt.ts
|
|
81
|
+
function createInterruptController(actions, options = {}) {
|
|
82
|
+
const doublePressMs = options.doublePressMs ?? 800;
|
|
83
|
+
const now = options.now ?? Date.now;
|
|
84
|
+
let lastCtrlC;
|
|
85
|
+
const cancelRun = () => {
|
|
86
|
+
if (!actions.isRunning()) return "ignored";
|
|
87
|
+
actions.abortRun();
|
|
88
|
+
actions.notify("cancelled");
|
|
89
|
+
return "cancelled";
|
|
90
|
+
};
|
|
91
|
+
return {
|
|
92
|
+
handleCtrlC() {
|
|
93
|
+
const pressedAt = now();
|
|
94
|
+
if (lastCtrlC !== void 0 && pressedAt - lastCtrlC < doublePressMs) {
|
|
95
|
+
actions.notify("exit");
|
|
96
|
+
actions.exit();
|
|
97
|
+
return "exit";
|
|
98
|
+
}
|
|
99
|
+
lastCtrlC = pressedAt;
|
|
100
|
+
const cancelled = cancelRun();
|
|
101
|
+
if (cancelled !== "ignored") return cancelled;
|
|
102
|
+
if (actions.hasInput()) {
|
|
103
|
+
actions.clearInput();
|
|
104
|
+
actions.notify("cleared");
|
|
105
|
+
return "cleared";
|
|
106
|
+
}
|
|
107
|
+
actions.notify("exit-hint");
|
|
108
|
+
return "exit-hint";
|
|
109
|
+
},
|
|
110
|
+
handleEscape() {
|
|
111
|
+
return cancelRun();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/tui/traces.ts
|
|
117
|
+
function duration(span) {
|
|
118
|
+
if (!span.endedAt) return "running";
|
|
119
|
+
const elapsed = Math.max(0, span.endedAt.getTime() - span.startedAt.getTime());
|
|
120
|
+
return elapsed >= 1e3 ? `${(elapsed / 1e3).toFixed(1)}s` : `${elapsed}ms`;
|
|
121
|
+
}
|
|
122
|
+
function traceStatus(span) {
|
|
123
|
+
if (span.error) return "error";
|
|
124
|
+
return span.endedAt ? "ok" : "running";
|
|
125
|
+
}
|
|
126
|
+
function formatTraceTree(spans) {
|
|
127
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
128
|
+
const ids = new Set(spans.map((span) => span.spanId));
|
|
129
|
+
for (const span of spans) {
|
|
130
|
+
const parent = span.parentSpanId && ids.has(span.parentSpanId) ? span.parentSpanId : null;
|
|
131
|
+
const children = byParent.get(parent) ?? [];
|
|
132
|
+
children.push(span);
|
|
133
|
+
byParent.set(parent, children);
|
|
134
|
+
}
|
|
135
|
+
for (const children of byParent.values()) {
|
|
136
|
+
children.sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime());
|
|
137
|
+
}
|
|
138
|
+
const lines = [];
|
|
139
|
+
const visited = /* @__PURE__ */ new Set();
|
|
140
|
+
const visit = (span, depth) => {
|
|
141
|
+
if (visited.has(span.spanId)) return;
|
|
142
|
+
visited.add(span.spanId);
|
|
143
|
+
const status = traceStatus(span);
|
|
144
|
+
const marker = status === "error" ? "\u2717" : status === "running" ? "\u2026" : "\u2713";
|
|
145
|
+
lines.push(
|
|
146
|
+
`${" ".repeat(depth)}${marker} ${span.name} \xB7 ${span.spanType} \xB7 ${duration(span)}`
|
|
147
|
+
);
|
|
148
|
+
for (const child of byParent.get(span.spanId) ?? []) visit(child, depth + 1);
|
|
149
|
+
};
|
|
150
|
+
for (const root of byParent.get(null) ?? []) visit(root, 0);
|
|
151
|
+
return lines;
|
|
152
|
+
}
|
|
153
|
+
|
|
75
154
|
// src/tui/theme.ts
|
|
76
155
|
import chalk from "chalk";
|
|
77
156
|
var c = {
|
|
@@ -113,16 +192,6 @@ var markdownTheme = {
|
|
|
113
192
|
|
|
114
193
|
// src/tui/index.ts
|
|
115
194
|
var OAUTH_PROVIDERS = ["anthropic", "openai-codex"];
|
|
116
|
-
var JanetEditor = class extends Editor {
|
|
117
|
-
onCtrlC;
|
|
118
|
-
handleInput(data) {
|
|
119
|
-
if (data === "") {
|
|
120
|
-
this.onCtrlC?.();
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
super.handleInput(data);
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
195
|
var HELP_TEXT = `Commands:
|
|
127
196
|
/models Pick a model from a list (arrow keys)
|
|
128
197
|
/model [provider/id] Open the picker, or switch directly by id
|
|
@@ -130,9 +199,13 @@ var HELP_TEXT = `Commands:
|
|
|
130
199
|
Log in; OpenAI mode is browser or device
|
|
131
200
|
/logout <provider> Remove stored credentials for a provider
|
|
132
201
|
/auth Show which providers are authenticated
|
|
202
|
+
/observability Configure opt-in tracing
|
|
203
|
+
/traces Browse recent local traces
|
|
204
|
+
/cancel Cancel the active run
|
|
133
205
|
/help This help
|
|
134
206
|
/quit Exit (double Ctrl+C also works)
|
|
135
207
|
|
|
208
|
+
While Janet is working, Esc or Ctrl+C cancels the active run.
|
|
136
209
|
Anything else is a message to Janet.`;
|
|
137
210
|
function resolveAnswer(q, text) {
|
|
138
211
|
if (!q.options?.length) return text;
|
|
@@ -153,7 +226,10 @@ function resolveAnswer(q, text) {
|
|
|
153
226
|
return pick(text);
|
|
154
227
|
}
|
|
155
228
|
async function runTui(opts) {
|
|
156
|
-
const { controller, session, paths, herdrDetach } = await bootJanet({
|
|
229
|
+
const { controller, session, paths, herdrDetach, observability } = await bootJanet({
|
|
230
|
+
...opts,
|
|
231
|
+
interactive: true
|
|
232
|
+
});
|
|
157
233
|
const persistedModel = process.env["JANET_MODEL"] || loadSettings().defaultModelId;
|
|
158
234
|
const presetModel = persistedModel ? normalizeModelSelection(persistedModel, availableModels()) : void 0;
|
|
159
235
|
if (!session.model.hasSelection() && presetModel) {
|
|
@@ -163,7 +239,7 @@ async function runTui(opts) {
|
|
|
163
239
|
const ui = new TUI(terminal);
|
|
164
240
|
const chat = new Container();
|
|
165
241
|
const status = new Text("", 1, 0);
|
|
166
|
-
const editor = new
|
|
242
|
+
const editor = new Editor(ui, editorTheme);
|
|
167
243
|
const loader = new Loader(ui, c.accent, c.dim, "Janet is thinking\u2026");
|
|
168
244
|
ui.addChild(chat);
|
|
169
245
|
ui.addChild(new Spacer(1));
|
|
@@ -176,11 +252,15 @@ async function runTui(opts) {
|
|
|
176
252
|
let pendingInput = null;
|
|
177
253
|
let activeSelect = null;
|
|
178
254
|
let active = null;
|
|
255
|
+
let cancelRequested = false;
|
|
179
256
|
const activeTools = /* @__PURE__ */ new Map();
|
|
180
257
|
const updateStatus = () => {
|
|
181
258
|
const model = session.model.hasSelection() ? session.model.get() : "no model \u2014 /model <id>";
|
|
182
|
-
const
|
|
183
|
-
|
|
259
|
+
const tracing = observability.status.enabled ? c.dim(` \xB7 trace:${observability.status.capture}`) : "";
|
|
260
|
+
const state = pendingInput ? "enter the requested value" : pendingQuestion || activeSelect ? "answer Janet's question" : pendingApproval ? "awaiting approval" : cancelRequested ? "cancelling" : running ? "working \xB7 Esc/Ctrl+C cancels" : "idle";
|
|
261
|
+
status.setText(
|
|
262
|
+
c.dim(`${paths.projectPath} \xB7 `) + c.accent(model) + c.dim(` \xB7 ${state}`) + tracing
|
|
263
|
+
);
|
|
184
264
|
ui.requestRender();
|
|
185
265
|
};
|
|
186
266
|
const appendToChat = (comp) => {
|
|
@@ -227,6 +307,7 @@ async function runTui(opts) {
|
|
|
227
307
|
switch (event.type) {
|
|
228
308
|
case "agent_start":
|
|
229
309
|
running = true;
|
|
310
|
+
cancelRequested = false;
|
|
230
311
|
active = null;
|
|
231
312
|
activeTools.clear();
|
|
232
313
|
loader.setMessage("Janet is thinking\u2026");
|
|
@@ -328,6 +409,7 @@ async function runTui(opts) {
|
|
|
328
409
|
break;
|
|
329
410
|
case "agent_end":
|
|
330
411
|
running = false;
|
|
412
|
+
cancelRequested = false;
|
|
331
413
|
activeTools.clear();
|
|
332
414
|
loader.setMessage("Janet is thinking\u2026");
|
|
333
415
|
if (event.reason !== "suspended") pendingQuestion = null;
|
|
@@ -337,20 +419,84 @@ async function runTui(opts) {
|
|
|
337
419
|
}
|
|
338
420
|
};
|
|
339
421
|
const unsubscribe = session.subscribe(onEvent);
|
|
422
|
+
let removeInputListener = () => {
|
|
423
|
+
};
|
|
424
|
+
let sigintHandler;
|
|
340
425
|
const shutdown = async (code) => {
|
|
426
|
+
removeInputListener();
|
|
427
|
+
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
341
428
|
unsubscribe();
|
|
342
429
|
herdrDetach();
|
|
343
430
|
ui.stop();
|
|
431
|
+
await observability.flush().catch(() => {
|
|
432
|
+
});
|
|
344
433
|
await controller.destroy().catch(() => {
|
|
345
434
|
});
|
|
346
435
|
process.exit(code);
|
|
347
436
|
};
|
|
437
|
+
const notifyInterrupt = (result) => {
|
|
438
|
+
switch (result) {
|
|
439
|
+
case "cancelled":
|
|
440
|
+
addLine(c.dim(" Cancelling the active run\u2026"));
|
|
441
|
+
break;
|
|
442
|
+
case "cleared":
|
|
443
|
+
break;
|
|
444
|
+
case "exit":
|
|
445
|
+
break;
|
|
446
|
+
case "exit-hint":
|
|
447
|
+
addLine(c.dim(" Press Ctrl+C again to quit."));
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
updateStatus();
|
|
451
|
+
};
|
|
452
|
+
const abortActiveRun = () => {
|
|
453
|
+
if (cancelRequested) return;
|
|
454
|
+
cancelRequested = true;
|
|
455
|
+
pendingApproval = null;
|
|
456
|
+
pendingQuestion = null;
|
|
457
|
+
activeTools.clear();
|
|
458
|
+
if (activeSelect) {
|
|
459
|
+
chat.removeChild(activeSelect);
|
|
460
|
+
activeSelect = null;
|
|
461
|
+
}
|
|
462
|
+
ui.setFocus(editor);
|
|
463
|
+
loader.setMessage("Cancelling\u2026");
|
|
464
|
+
session.abort();
|
|
465
|
+
};
|
|
466
|
+
const interrupts = createInterruptController({
|
|
467
|
+
isRunning: () => running,
|
|
468
|
+
hasInput: () => editor.getText().length > 0,
|
|
469
|
+
abortRun: abortActiveRun,
|
|
470
|
+
clearInput: () => {
|
|
471
|
+
editor.setText("");
|
|
472
|
+
ui.requestRender();
|
|
473
|
+
},
|
|
474
|
+
exit: () => {
|
|
475
|
+
void shutdown(0);
|
|
476
|
+
},
|
|
477
|
+
notify: notifyInterrupt
|
|
478
|
+
});
|
|
479
|
+
removeInputListener = ui.addInputListener((data) => {
|
|
480
|
+
if (matchesKey(data, "ctrl+c")) {
|
|
481
|
+
interrupts.handleCtrlC();
|
|
482
|
+
return { consume: true };
|
|
483
|
+
}
|
|
484
|
+
if (matchesKey(data, "escape") && running) {
|
|
485
|
+
interrupts.handleEscape();
|
|
486
|
+
return { consume: true };
|
|
487
|
+
}
|
|
488
|
+
return void 0;
|
|
489
|
+
});
|
|
490
|
+
sigintHandler = () => {
|
|
491
|
+
interrupts.handleCtrlC();
|
|
492
|
+
};
|
|
493
|
+
process.on("SIGINT", sigintHandler);
|
|
348
494
|
const promptInput = (message, placeholder) => {
|
|
349
495
|
addLine(c.accentBold(` ${message}`));
|
|
350
496
|
if (placeholder) addLine(c.dim(` (${placeholder})`));
|
|
351
|
-
updateStatus();
|
|
352
497
|
return new Promise((resolve) => {
|
|
353
498
|
pendingInput = resolve;
|
|
499
|
+
updateStatus();
|
|
354
500
|
});
|
|
355
501
|
};
|
|
356
502
|
const showModelPicker = (intro) => {
|
|
@@ -390,6 +536,254 @@ async function runTui(opts) {
|
|
|
390
536
|
ui.setFocus(select);
|
|
391
537
|
updateStatus();
|
|
392
538
|
};
|
|
539
|
+
const savedObservabilitySummary = () => {
|
|
540
|
+
const saved = loadSettings().observability;
|
|
541
|
+
const resolved = resolveObservabilityConfig(saved, {});
|
|
542
|
+
return formatObservabilityStatus({
|
|
543
|
+
enabled: resolved.enabled,
|
|
544
|
+
capture: resolved.capture,
|
|
545
|
+
sampleRate: resolved.sampleRate,
|
|
546
|
+
destinations: [
|
|
547
|
+
...resolved.local.enabled ? ["local"] : [],
|
|
548
|
+
...resolved.remote ? [
|
|
549
|
+
resolved.remote.kind === "phoenix" ? `phoenix (${safeObservabilityEndpoint(resolved.remote.endpoint)})` : `otlp (${safeObservabilityEndpoint(resolved.remote.endpoint)})`
|
|
550
|
+
] : []
|
|
551
|
+
],
|
|
552
|
+
warnings: resolved.warnings
|
|
553
|
+
});
|
|
554
|
+
};
|
|
555
|
+
const persistObservability = (settings) => {
|
|
556
|
+
rememberObservability(settings);
|
|
557
|
+
addLine(c.accentBold(" \u2713 Observability settings saved."));
|
|
558
|
+
addLine(c.dim(` Saved: ${savedObservabilitySummary()}`));
|
|
559
|
+
addLine(c.dim(" Restart Janet to apply the new setting."));
|
|
560
|
+
updateStatus();
|
|
561
|
+
};
|
|
562
|
+
const closeActiveSelect = (select) => {
|
|
563
|
+
chat.removeChild(select);
|
|
564
|
+
if (activeSelect === select) activeSelect = null;
|
|
565
|
+
ui.setFocus(editor);
|
|
566
|
+
};
|
|
567
|
+
const confirmFullCapture = (base) => {
|
|
568
|
+
addLine(
|
|
569
|
+
c.warn(
|
|
570
|
+
" Full capture includes prompts, responses, and tool payloads. Do not use it with sensitive material."
|
|
571
|
+
)
|
|
572
|
+
);
|
|
573
|
+
const select = new SelectList(
|
|
574
|
+
[
|
|
575
|
+
{
|
|
576
|
+
value: "no",
|
|
577
|
+
label: "Keep metadata-only capture",
|
|
578
|
+
description: "Recommended. Content stays out of traces."
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
value: "yes",
|
|
582
|
+
label: "Enable full capture",
|
|
583
|
+
description: "I understand trace content may contain sensitive data."
|
|
584
|
+
}
|
|
585
|
+
],
|
|
586
|
+
2,
|
|
587
|
+
editorTheme.selectList
|
|
588
|
+
);
|
|
589
|
+
select.onSelect = (item) => {
|
|
590
|
+
closeActiveSelect(select);
|
|
591
|
+
persistObservability({
|
|
592
|
+
...base,
|
|
593
|
+
capture: item.value === "yes" ? "full" : "metadata"
|
|
594
|
+
});
|
|
595
|
+
};
|
|
596
|
+
activeSelect = select;
|
|
597
|
+
chat.addChild(select);
|
|
598
|
+
ui.setFocus(select);
|
|
599
|
+
updateStatus();
|
|
600
|
+
};
|
|
601
|
+
const chooseCaptureMode = (base) => {
|
|
602
|
+
addLine(c.accentBold(" What may Janet include in traces?"));
|
|
603
|
+
const select = new SelectList(
|
|
604
|
+
[
|
|
605
|
+
{
|
|
606
|
+
value: "metadata",
|
|
607
|
+
label: "Metadata only",
|
|
608
|
+
description: "Timing, tool names, model, tokens, status, and errors."
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
value: "full",
|
|
612
|
+
label: "Full content",
|
|
613
|
+
description: "Also includes prompts, responses, and tool payloads."
|
|
614
|
+
}
|
|
615
|
+
],
|
|
616
|
+
2,
|
|
617
|
+
editorTheme.selectList
|
|
618
|
+
);
|
|
619
|
+
select.onSelect = (item) => {
|
|
620
|
+
closeActiveSelect(select);
|
|
621
|
+
const capture = item.value;
|
|
622
|
+
if (capture === "full") {
|
|
623
|
+
confirmFullCapture(base);
|
|
624
|
+
} else {
|
|
625
|
+
persistObservability({ ...base, capture });
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
activeSelect = select;
|
|
629
|
+
chat.addChild(select);
|
|
630
|
+
ui.setFocus(select);
|
|
631
|
+
updateStatus();
|
|
632
|
+
};
|
|
633
|
+
const showObservabilityPicker = () => {
|
|
634
|
+
if (running) {
|
|
635
|
+
addLine(c.dim(" Cancel the active run before changing observability settings."));
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
addLine(c.accentBold(" Configure observability"));
|
|
639
|
+
addLine(c.dim(` Active now: ${formatObservabilityStatus(observability.status)}`));
|
|
640
|
+
addLine(c.dim(" Tracing is opt-in and changes apply after restart."));
|
|
641
|
+
const select = new SelectList(
|
|
642
|
+
[
|
|
643
|
+
{
|
|
644
|
+
value: "off",
|
|
645
|
+
label: "Off",
|
|
646
|
+
description: "No spans, trace database, or network export."
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
value: "local",
|
|
650
|
+
label: "Local trace history",
|
|
651
|
+
description: "Store traces in ~/.agent-knowledge/observability.db."
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
value: "phoenix",
|
|
655
|
+
label: "Phoenix",
|
|
656
|
+
description: "Send OTLP traces to http://localhost:6006."
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
value: "otlp",
|
|
660
|
+
label: "Custom OTLP",
|
|
661
|
+
description: "Send OTLP/HTTP protobuf traces to your endpoint."
|
|
662
|
+
}
|
|
663
|
+
],
|
|
664
|
+
4,
|
|
665
|
+
editorTheme.selectList
|
|
666
|
+
);
|
|
667
|
+
select.onSelect = (item) => {
|
|
668
|
+
closeActiveSelect(select);
|
|
669
|
+
if (item.value === "off") {
|
|
670
|
+
persistObservability({
|
|
671
|
+
capture: "off",
|
|
672
|
+
sampleRate: 1,
|
|
673
|
+
local: { enabled: false, retentionDays: 7 }
|
|
674
|
+
});
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
if (item.value === "local") {
|
|
678
|
+
chooseCaptureMode({
|
|
679
|
+
sampleRate: 1,
|
|
680
|
+
local: { enabled: true, retentionDays: 7 }
|
|
681
|
+
});
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (item.value === "phoenix") {
|
|
685
|
+
chooseCaptureMode({
|
|
686
|
+
sampleRate: 1,
|
|
687
|
+
local: { enabled: false, retentionDays: 7 },
|
|
688
|
+
remote: {
|
|
689
|
+
kind: "phoenix",
|
|
690
|
+
endpoint: "http://localhost:6006",
|
|
691
|
+
projectName: "janet"
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
void promptInput(
|
|
697
|
+
"OTLP endpoint (for example, http://localhost:4318):"
|
|
698
|
+
).then((endpoint) => {
|
|
699
|
+
try {
|
|
700
|
+
const parsed = new URL(endpoint);
|
|
701
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error();
|
|
702
|
+
if (parsed.username || parsed.password || parsed.search || parsed.hash) {
|
|
703
|
+
addLine(
|
|
704
|
+
c.error(
|
|
705
|
+
" Do not put credentials or query parameters in the saved endpoint. Use OTEL_EXPORTER_OTLP_HEADERS."
|
|
706
|
+
)
|
|
707
|
+
);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
} catch {
|
|
711
|
+
addLine(c.error(" Endpoint must be a valid HTTP or HTTPS URL."));
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
chooseCaptureMode({
|
|
715
|
+
sampleRate: 1,
|
|
716
|
+
local: { enabled: false, retentionDays: 7 },
|
|
717
|
+
remote: {
|
|
718
|
+
kind: "otlp",
|
|
719
|
+
endpoint
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
};
|
|
724
|
+
activeSelect = select;
|
|
725
|
+
chat.addChild(select);
|
|
726
|
+
ui.setFocus(select);
|
|
727
|
+
updateStatus();
|
|
728
|
+
};
|
|
729
|
+
const showLocalTraces = async () => {
|
|
730
|
+
if (running) {
|
|
731
|
+
addLine(c.dim(" Cancel the active run before browsing traces."));
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (!observability.config.local.enabled) {
|
|
735
|
+
addLine(c.dim(" Local trace history is not active. Use /observability to enable it."));
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
await observability.flush().catch(() => {
|
|
739
|
+
});
|
|
740
|
+
const store = await observability.storage.getStore("observability");
|
|
741
|
+
if (!store) {
|
|
742
|
+
addLine(c.error(" Local trace storage is unavailable."));
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
const recent = await store.listTraces({
|
|
746
|
+
pagination: { page: 0, perPage: 10 },
|
|
747
|
+
orderBy: { field: "startedAt", direction: "DESC" }
|
|
748
|
+
});
|
|
749
|
+
if (!recent.spans.length) {
|
|
750
|
+
addLine(c.dim(" No local traces yet."));
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
addLine(c.accentBold(" Recent local traces"));
|
|
754
|
+
const select = new SelectList(
|
|
755
|
+
recent.spans.map((span) => {
|
|
756
|
+
const state = traceStatus(span);
|
|
757
|
+
const marker = state === "error" ? "\u2717" : state === "running" ? "\u2026" : "\u2713";
|
|
758
|
+
return {
|
|
759
|
+
value: span.traceId,
|
|
760
|
+
label: `${marker} ${span.name}`,
|
|
761
|
+
description: `${span.startedAt.toLocaleString()} \xB7 ${span.traceId}`
|
|
762
|
+
};
|
|
763
|
+
}),
|
|
764
|
+
Math.min(recent.spans.length, 10),
|
|
765
|
+
editorTheme.selectList
|
|
766
|
+
);
|
|
767
|
+
select.onSelect = (item) => {
|
|
768
|
+
closeActiveSelect(select);
|
|
769
|
+
void store.getTrace({ traceId: item.value }).then((trace) => {
|
|
770
|
+
if (!trace) {
|
|
771
|
+
addLine(c.error(` Trace not found: ${item.value}`));
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
addLine(c.accentBold(` Trace ${trace.traceId}`));
|
|
775
|
+
for (const line of formatTraceTree(trace.spans)) {
|
|
776
|
+
addLine(c.dim(` ${line}`));
|
|
777
|
+
}
|
|
778
|
+
}).catch((error) => {
|
|
779
|
+
addLine(c.error(` Could not read trace: ${error.message}`));
|
|
780
|
+
});
|
|
781
|
+
};
|
|
782
|
+
activeSelect = select;
|
|
783
|
+
chat.addChild(select);
|
|
784
|
+
ui.setFocus(select);
|
|
785
|
+
updateStatus();
|
|
786
|
+
};
|
|
393
787
|
const handleCommand = async (text) => {
|
|
394
788
|
const [cmd, ...rest] = text.slice(1).split(/\s+/);
|
|
395
789
|
switch (cmd) {
|
|
@@ -400,6 +794,32 @@ async function runTui(opts) {
|
|
|
400
794
|
case "help":
|
|
401
795
|
addLine(c.dim(HELP_TEXT));
|
|
402
796
|
break;
|
|
797
|
+
case "cancel":
|
|
798
|
+
if (interrupts.handleEscape() === "ignored") {
|
|
799
|
+
addLine(c.dim(" No active run to cancel."));
|
|
800
|
+
}
|
|
801
|
+
break;
|
|
802
|
+
case "observability": {
|
|
803
|
+
const action = rest[0]?.trim().toLowerCase();
|
|
804
|
+
if (action === "status") {
|
|
805
|
+
addLine(c.dim(` Active: ${formatObservabilityStatus(observability.status)}`));
|
|
806
|
+
addLine(c.dim(` Saved: ${savedObservabilitySummary()}`));
|
|
807
|
+
} else if (action === "off") {
|
|
808
|
+
persistObservability({
|
|
809
|
+
capture: "off",
|
|
810
|
+
sampleRate: 1,
|
|
811
|
+
local: { enabled: false, retentionDays: 7 }
|
|
812
|
+
});
|
|
813
|
+
} else if (!action) {
|
|
814
|
+
showObservabilityPicker();
|
|
815
|
+
} else {
|
|
816
|
+
addLine(c.dim("Usage: /observability [status | off]"));
|
|
817
|
+
}
|
|
818
|
+
break;
|
|
819
|
+
}
|
|
820
|
+
case "traces":
|
|
821
|
+
await showLocalTraces();
|
|
822
|
+
break;
|
|
403
823
|
case "login": {
|
|
404
824
|
const providerId = (rest[0] || "anthropic").trim();
|
|
405
825
|
if (!OAUTH_PROVIDERS.includes(providerId)) {
|
|
@@ -533,31 +953,21 @@ async function runTui(opts) {
|
|
|
533
953
|
showModelPicker(" Pick a model first:");
|
|
534
954
|
return;
|
|
535
955
|
}
|
|
536
|
-
void session.sendMessage({
|
|
956
|
+
void session.sendMessage({
|
|
957
|
+
content: text,
|
|
958
|
+
tracingOptions: observability.tracingOptionsForTurn({
|
|
959
|
+
interactive: true,
|
|
960
|
+
operation: "chat",
|
|
961
|
+
resourceId: paths.resourceId,
|
|
962
|
+
threadId: session.thread.getId() ?? void 0
|
|
963
|
+
})
|
|
964
|
+
}).catch((err) => {
|
|
537
965
|
running = false;
|
|
538
966
|
setLoader(false);
|
|
539
967
|
addLine(c.error(` \u2717 ${err.message}`));
|
|
540
968
|
updateStatus();
|
|
541
969
|
});
|
|
542
970
|
};
|
|
543
|
-
let lastCtrlC = 0;
|
|
544
|
-
editor.onCtrlC = () => {
|
|
545
|
-
const now = Date.now();
|
|
546
|
-
if (now - lastCtrlC < 800) {
|
|
547
|
-
void shutdown(0);
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
lastCtrlC = now;
|
|
551
|
-
if (running) {
|
|
552
|
-
void session.abort();
|
|
553
|
-
addLine(c.dim(" (aborted \u2014 Ctrl+C again to quit)"));
|
|
554
|
-
} else if (editor.getText()) {
|
|
555
|
-
editor.setText("");
|
|
556
|
-
ui.requestRender();
|
|
557
|
-
} else {
|
|
558
|
-
addLine(c.dim(" (Ctrl+C again to quit)"));
|
|
559
|
-
}
|
|
560
|
-
};
|
|
561
971
|
addLine(c.accentBold(GREETING));
|
|
562
972
|
addLine(
|
|
563
973
|
c.dim(
|
|
@@ -565,6 +975,9 @@ async function runTui(opts) {
|
|
|
565
975
|
Ask me anything in the bundle, or say what to ingest. /help for commands.`
|
|
566
976
|
)
|
|
567
977
|
);
|
|
978
|
+
for (const warning of observability.status.warnings) {
|
|
979
|
+
addLine(c.warn(`Observability: ${warning}`));
|
|
980
|
+
}
|
|
568
981
|
updateStatus();
|
|
569
982
|
ui.start();
|
|
570
983
|
ui.setFocus(editor);
|
|
@@ -576,4 +989,4 @@ Ask me anything in the bundle, or say what to ingest. /help for commands.`
|
|
|
576
989
|
export {
|
|
577
990
|
runTui
|
|
578
991
|
};
|
|
579
|
-
//# sourceMappingURL=tui-
|
|
992
|
+
//# sourceMappingURL=tui-6F3IBI3J.js.map
|