@stjbrown/agent-knowledge 0.1.0-beta.3 → 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-GCLV5CZQ.js → tui-6F3IBI3J.js} +467 -44
- package/dist/tui-6F3IBI3J.js.map +1 -0
- package/package.json +6 -2
- package/skills/kb/SKILL.md +5 -0
- package/skills/kb/example-bundle/spec/types.md +3 -2
- package/skills/kb-ingest/SKILL.md +21 -3
- package/skills/kb-init/SKILL.md +16 -5
- package/skills/kb-lint/SKILL.md +6 -1
- 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-GCLV5CZQ.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
|
|
@@ -61,6 +66,90 @@ function toolActivityLabel(toolName) {
|
|
|
61
66
|
}
|
|
62
67
|
return "Janet is working\u2026";
|
|
63
68
|
}
|
|
69
|
+
function toolErrorLabel(result) {
|
|
70
|
+
const detail = String(result);
|
|
71
|
+
const readRequired = detail.match(
|
|
72
|
+
/File "([^"]+)" (?:has not been read|was modified since last read)/
|
|
73
|
+
);
|
|
74
|
+
if (readRequired) {
|
|
75
|
+
return `Update paused: Janet needs to re-read "${readRequired[1]}" first.`;
|
|
76
|
+
}
|
|
77
|
+
return `Tool error: ${detail.slice(0, 140)}`;
|
|
78
|
+
}
|
|
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
|
+
}
|
|
64
153
|
|
|
65
154
|
// src/tui/theme.ts
|
|
66
155
|
import chalk from "chalk";
|
|
@@ -103,16 +192,6 @@ var markdownTheme = {
|
|
|
103
192
|
|
|
104
193
|
// src/tui/index.ts
|
|
105
194
|
var OAUTH_PROVIDERS = ["anthropic", "openai-codex"];
|
|
106
|
-
var JanetEditor = class extends Editor {
|
|
107
|
-
onCtrlC;
|
|
108
|
-
handleInput(data) {
|
|
109
|
-
if (data === "") {
|
|
110
|
-
this.onCtrlC?.();
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
super.handleInput(data);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
195
|
var HELP_TEXT = `Commands:
|
|
117
196
|
/models Pick a model from a list (arrow keys)
|
|
118
197
|
/model [provider/id] Open the picker, or switch directly by id
|
|
@@ -120,9 +199,13 @@ var HELP_TEXT = `Commands:
|
|
|
120
199
|
Log in; OpenAI mode is browser or device
|
|
121
200
|
/logout <provider> Remove stored credentials for a provider
|
|
122
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
|
|
123
205
|
/help This help
|
|
124
206
|
/quit Exit (double Ctrl+C also works)
|
|
125
207
|
|
|
208
|
+
While Janet is working, Esc or Ctrl+C cancels the active run.
|
|
126
209
|
Anything else is a message to Janet.`;
|
|
127
210
|
function resolveAnswer(q, text) {
|
|
128
211
|
if (!q.options?.length) return text;
|
|
@@ -143,7 +226,10 @@ function resolveAnswer(q, text) {
|
|
|
143
226
|
return pick(text);
|
|
144
227
|
}
|
|
145
228
|
async function runTui(opts) {
|
|
146
|
-
const { controller, session, paths, herdrDetach } = await bootJanet({
|
|
229
|
+
const { controller, session, paths, herdrDetach, observability } = await bootJanet({
|
|
230
|
+
...opts,
|
|
231
|
+
interactive: true
|
|
232
|
+
});
|
|
147
233
|
const persistedModel = process.env["JANET_MODEL"] || loadSettings().defaultModelId;
|
|
148
234
|
const presetModel = persistedModel ? normalizeModelSelection(persistedModel, availableModels()) : void 0;
|
|
149
235
|
if (!session.model.hasSelection() && presetModel) {
|
|
@@ -153,7 +239,7 @@ async function runTui(opts) {
|
|
|
153
239
|
const ui = new TUI(terminal);
|
|
154
240
|
const chat = new Container();
|
|
155
241
|
const status = new Text("", 1, 0);
|
|
156
|
-
const editor = new
|
|
242
|
+
const editor = new Editor(ui, editorTheme);
|
|
157
243
|
const loader = new Loader(ui, c.accent, c.dim, "Janet is thinking\u2026");
|
|
158
244
|
ui.addChild(chat);
|
|
159
245
|
ui.addChild(new Spacer(1));
|
|
@@ -166,11 +252,15 @@ async function runTui(opts) {
|
|
|
166
252
|
let pendingInput = null;
|
|
167
253
|
let activeSelect = null;
|
|
168
254
|
let active = null;
|
|
255
|
+
let cancelRequested = false;
|
|
169
256
|
const activeTools = /* @__PURE__ */ new Map();
|
|
170
257
|
const updateStatus = () => {
|
|
171
258
|
const model = session.model.hasSelection() ? session.model.get() : "no model \u2014 /model <id>";
|
|
172
|
-
const
|
|
173
|
-
|
|
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
|
+
);
|
|
174
264
|
ui.requestRender();
|
|
175
265
|
};
|
|
176
266
|
const appendToChat = (comp) => {
|
|
@@ -217,6 +307,7 @@ async function runTui(opts) {
|
|
|
217
307
|
switch (event.type) {
|
|
218
308
|
case "agent_start":
|
|
219
309
|
running = true;
|
|
310
|
+
cancelRequested = false;
|
|
220
311
|
active = null;
|
|
221
312
|
activeTools.clear();
|
|
222
313
|
loader.setMessage("Janet is thinking\u2026");
|
|
@@ -257,7 +348,7 @@ async function runTui(opts) {
|
|
|
257
348
|
);
|
|
258
349
|
if (event.isError) {
|
|
259
350
|
closeSegment();
|
|
260
|
-
addLine(c.warn(`
|
|
351
|
+
addLine(c.warn(` ${toolErrorLabel(event.result)}`));
|
|
261
352
|
}
|
|
262
353
|
break;
|
|
263
354
|
case "tool_suspended": {
|
|
@@ -318,6 +409,7 @@ async function runTui(opts) {
|
|
|
318
409
|
break;
|
|
319
410
|
case "agent_end":
|
|
320
411
|
running = false;
|
|
412
|
+
cancelRequested = false;
|
|
321
413
|
activeTools.clear();
|
|
322
414
|
loader.setMessage("Janet is thinking\u2026");
|
|
323
415
|
if (event.reason !== "suspended") pendingQuestion = null;
|
|
@@ -327,20 +419,84 @@ async function runTui(opts) {
|
|
|
327
419
|
}
|
|
328
420
|
};
|
|
329
421
|
const unsubscribe = session.subscribe(onEvent);
|
|
422
|
+
let removeInputListener = () => {
|
|
423
|
+
};
|
|
424
|
+
let sigintHandler;
|
|
330
425
|
const shutdown = async (code) => {
|
|
426
|
+
removeInputListener();
|
|
427
|
+
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
331
428
|
unsubscribe();
|
|
332
429
|
herdrDetach();
|
|
333
430
|
ui.stop();
|
|
431
|
+
await observability.flush().catch(() => {
|
|
432
|
+
});
|
|
334
433
|
await controller.destroy().catch(() => {
|
|
335
434
|
});
|
|
336
435
|
process.exit(code);
|
|
337
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);
|
|
338
494
|
const promptInput = (message, placeholder) => {
|
|
339
495
|
addLine(c.accentBold(` ${message}`));
|
|
340
496
|
if (placeholder) addLine(c.dim(` (${placeholder})`));
|
|
341
|
-
updateStatus();
|
|
342
497
|
return new Promise((resolve) => {
|
|
343
498
|
pendingInput = resolve;
|
|
499
|
+
updateStatus();
|
|
344
500
|
});
|
|
345
501
|
};
|
|
346
502
|
const showModelPicker = (intro) => {
|
|
@@ -380,6 +536,254 @@ async function runTui(opts) {
|
|
|
380
536
|
ui.setFocus(select);
|
|
381
537
|
updateStatus();
|
|
382
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
|
+
};
|
|
383
787
|
const handleCommand = async (text) => {
|
|
384
788
|
const [cmd, ...rest] = text.slice(1).split(/\s+/);
|
|
385
789
|
switch (cmd) {
|
|
@@ -390,6 +794,32 @@ async function runTui(opts) {
|
|
|
390
794
|
case "help":
|
|
391
795
|
addLine(c.dim(HELP_TEXT));
|
|
392
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;
|
|
393
823
|
case "login": {
|
|
394
824
|
const providerId = (rest[0] || "anthropic").trim();
|
|
395
825
|
if (!OAUTH_PROVIDERS.includes(providerId)) {
|
|
@@ -523,31 +953,21 @@ async function runTui(opts) {
|
|
|
523
953
|
showModelPicker(" Pick a model first:");
|
|
524
954
|
return;
|
|
525
955
|
}
|
|
526
|
-
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) => {
|
|
527
965
|
running = false;
|
|
528
966
|
setLoader(false);
|
|
529
967
|
addLine(c.error(` \u2717 ${err.message}`));
|
|
530
968
|
updateStatus();
|
|
531
969
|
});
|
|
532
970
|
};
|
|
533
|
-
let lastCtrlC = 0;
|
|
534
|
-
editor.onCtrlC = () => {
|
|
535
|
-
const now = Date.now();
|
|
536
|
-
if (now - lastCtrlC < 800) {
|
|
537
|
-
void shutdown(0);
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
lastCtrlC = now;
|
|
541
|
-
if (running) {
|
|
542
|
-
void session.abort();
|
|
543
|
-
addLine(c.dim(" (aborted \u2014 Ctrl+C again to quit)"));
|
|
544
|
-
} else if (editor.getText()) {
|
|
545
|
-
editor.setText("");
|
|
546
|
-
ui.requestRender();
|
|
547
|
-
} else {
|
|
548
|
-
addLine(c.dim(" (Ctrl+C again to quit)"));
|
|
549
|
-
}
|
|
550
|
-
};
|
|
551
971
|
addLine(c.accentBold(GREETING));
|
|
552
972
|
addLine(
|
|
553
973
|
c.dim(
|
|
@@ -555,6 +975,9 @@ async function runTui(opts) {
|
|
|
555
975
|
Ask me anything in the bundle, or say what to ingest. /help for commands.`
|
|
556
976
|
)
|
|
557
977
|
);
|
|
978
|
+
for (const warning of observability.status.warnings) {
|
|
979
|
+
addLine(c.warn(`Observability: ${warning}`));
|
|
980
|
+
}
|
|
558
981
|
updateStatus();
|
|
559
982
|
ui.start();
|
|
560
983
|
ui.setFocus(editor);
|
|
@@ -566,4 +989,4 @@ Ask me anything in the bundle, or say what to ingest. /help for commands.`
|
|
|
566
989
|
export {
|
|
567
990
|
runTui
|
|
568
991
|
};
|
|
569
|
-
//# sourceMappingURL=tui-
|
|
992
|
+
//# sourceMappingURL=tui-6F3IBI3J.js.map
|