open-agents-ai 0.187.224 → 0.187.225
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1200 -797
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -313404,210 +313404,643 @@ var init_task_manager_singleton = __esm({
|
|
|
313404
313404
|
}
|
|
313405
313405
|
});
|
|
313406
313406
|
|
|
313407
|
-
// packages/cli/src/
|
|
313408
|
-
var
|
|
313409
|
-
__export(
|
|
313410
|
-
|
|
313407
|
+
// packages/cli/src/api/chat-session.ts
|
|
313408
|
+
var chat_session_exports = {};
|
|
313409
|
+
__export(chat_session_exports, {
|
|
313410
|
+
addAssistantMessage: () => addAssistantMessage,
|
|
313411
|
+
addCheckinMessage: () => addCheckinMessage,
|
|
313412
|
+
addToolCallMessage: () => addToolCallMessage,
|
|
313413
|
+
addToolResultMessage: () => addToolResultMessage,
|
|
313414
|
+
addTriageResponseMessage: () => addTriageResponseMessage,
|
|
313415
|
+
addUserMessage: () => addUserMessage,
|
|
313416
|
+
appendCheckin: () => appendCheckin,
|
|
313417
|
+
appendInFlightOutput: () => appendInFlightOutput,
|
|
313418
|
+
compactSession: () => compactSession,
|
|
313419
|
+
deleteSession: () => deleteSession2,
|
|
313420
|
+
drainCheckins: () => drainCheckins,
|
|
313421
|
+
finishInFlightChat: () => finishInFlightChat,
|
|
313422
|
+
getInFlightChat: () => getInFlightChat,
|
|
313423
|
+
getSession: () => getSession,
|
|
313424
|
+
listSessions: () => listSessions2,
|
|
313425
|
+
loadPersistedSessions: () => loadPersistedSessions,
|
|
313426
|
+
lookupSession: () => lookupSession,
|
|
313427
|
+
startInFlightChat: () => startInFlightChat,
|
|
313428
|
+
trackSessionTokens: () => trackSessionTokens
|
|
313411
313429
|
});
|
|
313412
|
-
import {
|
|
313413
|
-
|
|
313414
|
-
|
|
313415
|
-
|
|
313416
|
-
|
|
313417
|
-
|
|
313418
|
-
|
|
313419
|
-
|
|
313420
|
-
|
|
313421
|
-
|
|
313422
|
-
|
|
313423
|
-
|
|
313424
|
-
|
|
313425
|
-
|
|
313426
|
-
|
|
313427
|
-
|
|
313428
|
-
|
|
313429
|
-
|
|
313430
|
-
|
|
313431
|
-
|
|
313432
|
-
|
|
313433
|
-
|
|
313434
|
-
|
|
313435
|
-
|
|
313436
|
-
|
|
313437
|
-
|
|
313438
|
-
|
|
313439
|
-
|
|
313440
|
-
|
|
313441
|
-
|
|
313442
|
-
|
|
313443
|
-
|
|
313444
|
-
|
|
313445
|
-
|
|
313446
|
-
|
|
313447
|
-
|
|
313448
|
-
|
|
313449
|
-
|
|
313450
|
-
|
|
313451
|
-
|
|
313452
|
-
|
|
313453
|
-
|
|
313454
|
-
|
|
313455
|
-
|
|
313456
|
-
|
|
313457
|
-
|
|
313458
|
-
|
|
313459
|
-
|
|
313460
|
-
|
|
313430
|
+
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
313431
|
+
import {
|
|
313432
|
+
existsSync as existsSync74,
|
|
313433
|
+
readFileSync as readFileSync58,
|
|
313434
|
+
readdirSync as readdirSync24,
|
|
313435
|
+
writeFileSync as writeFileSync39,
|
|
313436
|
+
renameSync as renameSync4,
|
|
313437
|
+
mkdirSync as mkdirSync44,
|
|
313438
|
+
unlinkSync as unlinkSync20
|
|
313439
|
+
} from "node:fs";
|
|
313440
|
+
import { join as join91 } from "node:path";
|
|
313441
|
+
import { homedir as homedir31 } from "node:os";
|
|
313442
|
+
function sessionsDir() {
|
|
313443
|
+
return join91(homedir31(), ".open-agents", "chat-sessions");
|
|
313444
|
+
}
|
|
313445
|
+
function sessionPath(id) {
|
|
313446
|
+
const safe = id.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
313447
|
+
return join91(sessionsDir(), `${safe}.json`);
|
|
313448
|
+
}
|
|
313449
|
+
function inFlightPath(id) {
|
|
313450
|
+
const safe = id.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
313451
|
+
return join91(sessionsDir(), `${safe}.inflight.json`);
|
|
313452
|
+
}
|
|
313453
|
+
function persistSession(s2) {
|
|
313454
|
+
try {
|
|
313455
|
+
mkdirSync44(sessionsDir(), { recursive: true });
|
|
313456
|
+
const final2 = sessionPath(s2.id);
|
|
313457
|
+
const tmp = `${final2}.tmp.${process.pid}.${Date.now()}`;
|
|
313458
|
+
writeFileSync39(tmp, JSON.stringify(s2, null, 2), "utf-8");
|
|
313459
|
+
renameSync4(tmp, final2);
|
|
313460
|
+
} catch {
|
|
313461
|
+
}
|
|
313462
|
+
}
|
|
313463
|
+
function persistInFlight(j) {
|
|
313464
|
+
try {
|
|
313465
|
+
mkdirSync44(sessionsDir(), { recursive: true });
|
|
313466
|
+
const final2 = inFlightPath(j.sessionId);
|
|
313467
|
+
const tmp = `${final2}.tmp.${process.pid}.${Date.now()}`;
|
|
313468
|
+
writeFileSync39(tmp, JSON.stringify(j, null, 2), "utf-8");
|
|
313469
|
+
renameSync4(tmp, final2);
|
|
313470
|
+
} catch {
|
|
313471
|
+
}
|
|
313472
|
+
}
|
|
313473
|
+
function deleteInFlightFile(id) {
|
|
313474
|
+
try {
|
|
313475
|
+
const p2 = inFlightPath(id);
|
|
313476
|
+
if (existsSync74(p2)) unlinkSync20(p2);
|
|
313477
|
+
} catch {
|
|
313478
|
+
}
|
|
313479
|
+
}
|
|
313480
|
+
function loadPersistedSessions() {
|
|
313481
|
+
const report = { restored: 0, staleInFlight: 0 };
|
|
313482
|
+
try {
|
|
313483
|
+
const dir = sessionsDir();
|
|
313484
|
+
if (!existsSync74(dir)) return report;
|
|
313485
|
+
const cutoff = Date.now() - SESSION_TTL_MS;
|
|
313486
|
+
for (const f2 of readdirSync24(dir)) {
|
|
313487
|
+
if (!f2.endsWith(".json") || f2.includes(".tmp.")) continue;
|
|
313488
|
+
const fp = join91(dir, f2);
|
|
313489
|
+
try {
|
|
313490
|
+
const parsed = JSON.parse(readFileSync58(fp, "utf-8"));
|
|
313491
|
+
if (f2.endsWith(".inflight.json")) {
|
|
313492
|
+
if (parsed && parsed.pid && parsed.status === "running") {
|
|
313493
|
+
try {
|
|
313494
|
+
process.kill(parsed.pid, 0);
|
|
313495
|
+
} catch {
|
|
313496
|
+
parsed.status = "failed";
|
|
313497
|
+
parsed.error = "Daemon restart while subprocess was running";
|
|
313498
|
+
parsed.completedAt = Date.now();
|
|
313499
|
+
try {
|
|
313500
|
+
writeFileSync39(fp, JSON.stringify(parsed, null, 2), "utf-8");
|
|
313501
|
+
} catch {
|
|
313461
313502
|
}
|
|
313462
|
-
|
|
313463
|
-
}
|
|
313464
|
-
if (remaining.startsWith("\x1B[<") && remaining.length < 15) {
|
|
313465
|
-
break;
|
|
313466
|
-
}
|
|
313467
|
-
if (remaining.startsWith("\x1B[") && remaining.length === 2) {
|
|
313468
|
-
break;
|
|
313469
|
-
}
|
|
313470
|
-
if (remaining.length === 1) {
|
|
313471
|
-
break;
|
|
313503
|
+
report.staleInFlight++;
|
|
313472
313504
|
}
|
|
313473
313505
|
}
|
|
313474
|
-
|
|
313475
|
-
i2++;
|
|
313506
|
+
continue;
|
|
313476
313507
|
}
|
|
313477
|
-
|
|
313478
|
-
|
|
313479
|
-
|
|
313508
|
+
if (parsed && typeof parsed === "object" && parsed.id) {
|
|
313509
|
+
if ((parsed.lastActivity ?? 0) >= cutoff) {
|
|
313510
|
+
sessions.set(parsed.id, parsed);
|
|
313511
|
+
report.restored++;
|
|
313512
|
+
}
|
|
313480
313513
|
}
|
|
313481
|
-
|
|
313482
|
-
|
|
313483
|
-
|
|
313484
|
-
|
|
313485
|
-
|
|
313486
|
-
|
|
313487
|
-
|
|
313488
|
-
|
|
313489
|
-
|
|
313490
|
-
|
|
313491
|
-
|
|
313492
|
-
|
|
313493
|
-
|
|
313514
|
+
} catch {
|
|
313515
|
+
}
|
|
313516
|
+
}
|
|
313517
|
+
} catch {
|
|
313518
|
+
}
|
|
313519
|
+
return report;
|
|
313520
|
+
}
|
|
313521
|
+
function buildSystemPrompt(cwd4) {
|
|
313522
|
+
const parts = [];
|
|
313523
|
+
parts.push(
|
|
313524
|
+
"You are Open Agent (OA), an AI coding assistant running locally via Ollama. You have access to the user's workspace and can discuss code, files, and projects. Be helpful, concise, and technically precise. When asked about files or code, describe what you know from the conversation context."
|
|
313525
|
+
);
|
|
313526
|
+
parts.push(`\\nEnvironment: ${process.platform}, Node ${process.version}, CWD: ${cwd4}`);
|
|
313527
|
+
const diaryPath = join91(cwd4, ".oa", "context", "session-diary.md");
|
|
313528
|
+
if (existsSync74(diaryPath)) {
|
|
313529
|
+
try {
|
|
313530
|
+
const diary = readFileSync58(diaryPath, "utf-8").slice(0, 1e3);
|
|
313531
|
+
parts.push(`\\nPrevious session history:\\n${diary}`);
|
|
313532
|
+
} catch {
|
|
313533
|
+
}
|
|
313534
|
+
}
|
|
313535
|
+
const memDir = join91(cwd4, ".oa", "memory");
|
|
313536
|
+
if (existsSync74(memDir)) {
|
|
313537
|
+
try {
|
|
313538
|
+
const files = readdirSync24(memDir).filter((f2) => f2.endsWith(".json")).slice(0, 5);
|
|
313539
|
+
if (files.length > 0) {
|
|
313540
|
+
parts.push("\\nPersistent memory topics: " + files.map((f2) => f2.replace(".json", "")).join(", "));
|
|
313541
|
+
for (const f2 of files.slice(0, 3)) {
|
|
313542
|
+
try {
|
|
313543
|
+
const data = JSON.parse(readFileSync58(join91(memDir, f2), "utf-8"));
|
|
313544
|
+
const entries = Object.entries(data).slice(0, 3);
|
|
313545
|
+
if (entries.length > 0) {
|
|
313546
|
+
parts.push(`\\nMemory [${f2.replace(".json", "")}]: ` + entries.map(([k, v]) => `${k}: ${String(v.value ?? v).slice(0, 100)}`).join("; "));
|
|
313494
313547
|
}
|
|
313495
|
-
}
|
|
313548
|
+
} catch {
|
|
313549
|
+
}
|
|
313496
313550
|
}
|
|
313497
|
-
callback();
|
|
313498
313551
|
}
|
|
313499
|
-
|
|
313500
|
-
|
|
313501
|
-
|
|
313502
|
-
|
|
313503
|
-
|
|
313504
|
-
|
|
313505
|
-
|
|
313506
|
-
|
|
313507
|
-
}
|
|
313508
|
-
|
|
313552
|
+
} catch {
|
|
313553
|
+
}
|
|
313554
|
+
}
|
|
313555
|
+
for (const name10 of ["AGENTS.md", "OA.md", ".open-agents.md"]) {
|
|
313556
|
+
const p2 = join91(cwd4, name10);
|
|
313557
|
+
if (existsSync74(p2)) {
|
|
313558
|
+
try {
|
|
313559
|
+
const content = readFileSync58(p2, "utf-8").slice(0, 500);
|
|
313560
|
+
parts.push(`\\nProject instructions (${name10}):\\n${content}`);
|
|
313561
|
+
} catch {
|
|
313509
313562
|
}
|
|
313510
|
-
}
|
|
313563
|
+
}
|
|
313511
313564
|
}
|
|
313512
|
-
|
|
313513
|
-
|
|
313514
|
-
|
|
313515
|
-
|
|
313516
|
-
|
|
313517
|
-
|
|
313518
|
-
|
|
313519
|
-
|
|
313520
|
-
|
|
313521
|
-
|
|
313522
|
-
|
|
313523
|
-
|
|
313524
|
-
|
|
313525
|
-
|
|
313526
|
-
|
|
313527
|
-
|
|
313528
|
-
|
|
313529
|
-
|
|
313530
|
-
_historySize;
|
|
313531
|
-
_historyIndex = -1;
|
|
313532
|
-
_savedLine = "";
|
|
313533
|
-
// saved current input when navigating history
|
|
313534
|
-
_completer = null;
|
|
313535
|
-
_paused = false;
|
|
313536
|
-
_closed = false;
|
|
313537
|
-
_input;
|
|
313538
|
-
_buffer = "";
|
|
313539
|
-
// partial escape sequence buffer
|
|
313540
|
-
_flushTimer = null;
|
|
313541
|
-
constructor(input, options2) {
|
|
313542
|
-
super();
|
|
313543
|
-
this._input = input;
|
|
313544
|
-
this._history = [...options2?.history ?? []];
|
|
313545
|
-
this._historySize = options2?.historySize ?? 500;
|
|
313546
|
-
this._completer = options2?.completer ?? null;
|
|
313547
|
-
input.on("data", (chunk) => {
|
|
313548
|
-
if (this._paused || this._closed) return;
|
|
313549
|
-
this.feed(chunk.toString("utf8"));
|
|
313550
|
-
});
|
|
313551
|
-
input.on("end", () => {
|
|
313552
|
-
if (!this._closed) this.close();
|
|
313553
|
-
});
|
|
313565
|
+
return parts.join("");
|
|
313566
|
+
}
|
|
313567
|
+
function getSession(sessionId, model, cwd4) {
|
|
313568
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
313569
|
+
const s2 = sessions.get(sessionId);
|
|
313570
|
+
s2.lastActivity = Date.now();
|
|
313571
|
+
return s2;
|
|
313572
|
+
}
|
|
313573
|
+
if (sessionId) {
|
|
313574
|
+
try {
|
|
313575
|
+
const fp = sessionPath(sessionId);
|
|
313576
|
+
if (existsSync74(fp)) {
|
|
313577
|
+
const parsed = JSON.parse(readFileSync58(fp, "utf-8"));
|
|
313578
|
+
if (parsed && parsed.id === sessionId) {
|
|
313579
|
+
parsed.lastActivity = Date.now();
|
|
313580
|
+
sessions.set(sessionId, parsed);
|
|
313581
|
+
return parsed;
|
|
313582
|
+
}
|
|
313554
313583
|
}
|
|
313555
|
-
|
|
313556
|
-
|
|
313557
|
-
|
|
313558
|
-
|
|
313559
|
-
|
|
313560
|
-
|
|
313561
|
-
|
|
313562
|
-
|
|
313563
|
-
|
|
313564
|
-
|
|
313565
|
-
|
|
313566
|
-
|
|
313567
|
-
|
|
313568
|
-
|
|
313569
|
-
|
|
313570
|
-
|
|
313571
|
-
|
|
313572
|
-
|
|
313573
|
-
|
|
313574
|
-
|
|
313584
|
+
} catch {
|
|
313585
|
+
}
|
|
313586
|
+
}
|
|
313587
|
+
const id = sessionId || randomUUID10();
|
|
313588
|
+
const systemPrompt = buildSystemPrompt(cwd4);
|
|
313589
|
+
const session = {
|
|
313590
|
+
id,
|
|
313591
|
+
messages: [{ role: "system", content: systemPrompt }],
|
|
313592
|
+
model,
|
|
313593
|
+
createdAt: Date.now(),
|
|
313594
|
+
lastActivity: Date.now(),
|
|
313595
|
+
tokensIn: 0,
|
|
313596
|
+
tokensOut: 0
|
|
313597
|
+
};
|
|
313598
|
+
sessions.set(id, session);
|
|
313599
|
+
persistSession(session);
|
|
313600
|
+
return session;
|
|
313601
|
+
}
|
|
313602
|
+
function addUserMessage(session, content) {
|
|
313603
|
+
session.messages.push({ role: "user", content });
|
|
313604
|
+
session.lastActivity = Date.now();
|
|
313605
|
+
persistSession(session);
|
|
313606
|
+
return session.messages.filter((m2) => INFERENCE_ROLES.includes(m2.role));
|
|
313607
|
+
}
|
|
313608
|
+
function addAssistantMessage(session, content) {
|
|
313609
|
+
session.messages.push({ role: "assistant", content });
|
|
313610
|
+
session.lastActivity = Date.now();
|
|
313611
|
+
persistSession(session);
|
|
313612
|
+
}
|
|
313613
|
+
function addToolCallMessage(session, tool, args) {
|
|
313614
|
+
let cappedArgs = args;
|
|
313615
|
+
try {
|
|
313616
|
+
const json = JSON.stringify(args);
|
|
313617
|
+
if (json && json.length > 4e3) {
|
|
313618
|
+
if (args && typeof args === "object" && !Array.isArray(args)) {
|
|
313619
|
+
const pruned = {};
|
|
313620
|
+
for (const [k, v] of Object.entries(args)) {
|
|
313621
|
+
if (typeof v === "string" && v.length > 1e3) {
|
|
313622
|
+
pruned[k] = v.slice(0, 800) + `… [+${v.length - 800} chars truncated for storage]`;
|
|
313623
|
+
} else {
|
|
313624
|
+
pruned[k] = v;
|
|
313625
|
+
}
|
|
313575
313626
|
}
|
|
313576
|
-
|
|
313577
|
-
}
|
|
313578
|
-
|
|
313579
|
-
setPrompt(_prompt) {
|
|
313627
|
+
cappedArgs = pruned;
|
|
313628
|
+
} else {
|
|
313629
|
+
cappedArgs = { _truncated: true, _bytes: json.length };
|
|
313580
313630
|
}
|
|
313581
|
-
|
|
313582
|
-
|
|
313631
|
+
}
|
|
313632
|
+
} catch {
|
|
313633
|
+
}
|
|
313634
|
+
session.messages.push({
|
|
313635
|
+
role: "tool_call",
|
|
313636
|
+
content: "",
|
|
313637
|
+
tool,
|
|
313638
|
+
args: cappedArgs,
|
|
313639
|
+
ts: Date.now()
|
|
313640
|
+
});
|
|
313641
|
+
session.lastActivity = Date.now();
|
|
313642
|
+
persistSession(session);
|
|
313643
|
+
}
|
|
313644
|
+
function addToolResultMessage(session, tool, output, success) {
|
|
313645
|
+
const capped = output && output.length > 2048 ? output.slice(0, 2e3) + `… [+${output.length - 2e3} chars truncated for storage]` : output;
|
|
313646
|
+
session.messages.push({
|
|
313647
|
+
role: "tool_result",
|
|
313648
|
+
content: "",
|
|
313649
|
+
tool,
|
|
313650
|
+
output: capped,
|
|
313651
|
+
success,
|
|
313652
|
+
ts: Date.now()
|
|
313653
|
+
});
|
|
313654
|
+
session.lastActivity = Date.now();
|
|
313655
|
+
persistSession(session);
|
|
313656
|
+
}
|
|
313657
|
+
function addCheckinMessage(session, content) {
|
|
313658
|
+
session.messages.push({
|
|
313659
|
+
role: "user_checkin",
|
|
313660
|
+
content,
|
|
313661
|
+
ts: Date.now()
|
|
313662
|
+
});
|
|
313663
|
+
session.lastActivity = Date.now();
|
|
313664
|
+
persistSession(session);
|
|
313665
|
+
}
|
|
313666
|
+
function addTriageResponseMessage(session, acknowledgment, steering) {
|
|
313667
|
+
session.messages.push({
|
|
313668
|
+
role: "triage_response",
|
|
313669
|
+
content: acknowledgment,
|
|
313670
|
+
acknowledgment,
|
|
313671
|
+
steering,
|
|
313672
|
+
ts: Date.now()
|
|
313673
|
+
});
|
|
313674
|
+
session.lastActivity = Date.now();
|
|
313675
|
+
persistSession(session);
|
|
313676
|
+
}
|
|
313677
|
+
function checkinPath(sessionId) {
|
|
313678
|
+
const safe = sessionId.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
313679
|
+
return join91(sessionsDir(), `${safe}.checkins.jsonl`);
|
|
313680
|
+
}
|
|
313681
|
+
function appendCheckin(sessionId, steering) {
|
|
313682
|
+
try {
|
|
313683
|
+
mkdirSync44(sessionsDir(), { recursive: true });
|
|
313684
|
+
const fp = checkinPath(sessionId);
|
|
313685
|
+
const entry = JSON.stringify({ ts: Date.now(), steering }) + "\n";
|
|
313686
|
+
const { appendFileSync: appendFileSync7 } = __require("node:fs");
|
|
313687
|
+
appendFileSync7(fp, entry, "utf-8");
|
|
313688
|
+
} catch {
|
|
313689
|
+
}
|
|
313690
|
+
}
|
|
313691
|
+
function drainCheckins(sessionId) {
|
|
313692
|
+
const fp = checkinPath(sessionId);
|
|
313693
|
+
if (!existsSync74(fp)) return [];
|
|
313694
|
+
try {
|
|
313695
|
+
const raw = readFileSync58(fp, "utf-8");
|
|
313696
|
+
try {
|
|
313697
|
+
unlinkSync20(fp);
|
|
313698
|
+
} catch {
|
|
313699
|
+
}
|
|
313700
|
+
if (!raw.trim()) return [];
|
|
313701
|
+
const out = [];
|
|
313702
|
+
for (const line of raw.split("\n")) {
|
|
313703
|
+
if (!line.trim()) continue;
|
|
313704
|
+
try {
|
|
313705
|
+
const parsed = JSON.parse(line);
|
|
313706
|
+
if (typeof parsed.steering === "string" && parsed.steering) {
|
|
313707
|
+
out.push(parsed.steering);
|
|
313708
|
+
}
|
|
313709
|
+
} catch {
|
|
313583
313710
|
}
|
|
313584
|
-
|
|
313585
|
-
|
|
313586
|
-
|
|
313587
|
-
|
|
313711
|
+
}
|
|
313712
|
+
return out;
|
|
313713
|
+
} catch {
|
|
313714
|
+
return [];
|
|
313715
|
+
}
|
|
313716
|
+
}
|
|
313717
|
+
function trackSessionTokens(session, tokensIn, tokensOut) {
|
|
313718
|
+
session.tokensIn += tokensIn;
|
|
313719
|
+
session.tokensOut += tokensOut;
|
|
313720
|
+
}
|
|
313721
|
+
function listSessions2() {
|
|
313722
|
+
return Array.from(sessions.values()).map((s2) => ({
|
|
313723
|
+
id: s2.id,
|
|
313724
|
+
model: s2.model,
|
|
313725
|
+
messages: s2.messages.length - 1,
|
|
313726
|
+
// exclude system prompt
|
|
313727
|
+
tokensIn: s2.tokensIn,
|
|
313728
|
+
tokensOut: s2.tokensOut,
|
|
313729
|
+
lastActivity: new Date(s2.lastActivity).toISOString()
|
|
313730
|
+
}));
|
|
313731
|
+
}
|
|
313732
|
+
function deleteSession2(id) {
|
|
313733
|
+
try {
|
|
313734
|
+
const p2 = sessionPath(id);
|
|
313735
|
+
if (existsSync74(p2)) unlinkSync20(p2);
|
|
313736
|
+
} catch {
|
|
313737
|
+
}
|
|
313738
|
+
deleteInFlightFile(id);
|
|
313739
|
+
inFlight.delete(id);
|
|
313740
|
+
return sessions.delete(id);
|
|
313741
|
+
}
|
|
313742
|
+
function lookupSession(id) {
|
|
313743
|
+
const cached = sessions.get(id);
|
|
313744
|
+
if (cached) return cached;
|
|
313745
|
+
try {
|
|
313746
|
+
const fp = sessionPath(id);
|
|
313747
|
+
if (existsSync74(fp)) {
|
|
313748
|
+
const parsed = JSON.parse(readFileSync58(fp, "utf-8"));
|
|
313749
|
+
if (parsed && parsed.id === id) {
|
|
313750
|
+
sessions.set(id, parsed);
|
|
313751
|
+
return parsed;
|
|
313588
313752
|
}
|
|
313589
|
-
|
|
313590
|
-
|
|
313591
|
-
|
|
313592
|
-
|
|
313753
|
+
}
|
|
313754
|
+
} catch {
|
|
313755
|
+
}
|
|
313756
|
+
return null;
|
|
313757
|
+
}
|
|
313758
|
+
function startInFlightChat(opts) {
|
|
313759
|
+
const job = {
|
|
313760
|
+
sessionId: opts.sessionId,
|
|
313761
|
+
runId: opts.runId,
|
|
313762
|
+
pid: opts.pid,
|
|
313763
|
+
startedAt: Date.now(),
|
|
313764
|
+
partialOutput: "",
|
|
313765
|
+
status: "running",
|
|
313766
|
+
tailBytes: 0
|
|
313767
|
+
};
|
|
313768
|
+
inFlight.set(opts.sessionId, job);
|
|
313769
|
+
persistInFlight(job);
|
|
313770
|
+
return job;
|
|
313771
|
+
}
|
|
313772
|
+
function appendInFlightOutput(sessionId, chunk) {
|
|
313773
|
+
const job = inFlight.get(sessionId);
|
|
313774
|
+
if (!job) return;
|
|
313775
|
+
job.partialOutput += chunk;
|
|
313776
|
+
job.tailBytes += chunk.length;
|
|
313777
|
+
if (job.partialOutput.length > PARTIAL_TAIL_BUDGET) {
|
|
313778
|
+
job.partialOutput = job.partialOutput.slice(-PARTIAL_TAIL_BUDGET);
|
|
313779
|
+
}
|
|
313780
|
+
persistInFlight(job);
|
|
313781
|
+
}
|
|
313782
|
+
function finishInFlightChat(sessionId, status, finalContent, error) {
|
|
313783
|
+
const job = inFlight.get(sessionId);
|
|
313784
|
+
if (!job) return;
|
|
313785
|
+
job.status = status;
|
|
313786
|
+
job.completedAt = Date.now();
|
|
313787
|
+
if (finalContent !== void 0) job.finalContent = finalContent;
|
|
313788
|
+
if (error !== void 0) job.error = error;
|
|
313789
|
+
persistInFlight(job);
|
|
313790
|
+
setTimeout(() => {
|
|
313791
|
+
inFlight.delete(sessionId);
|
|
313792
|
+
deleteInFlightFile(sessionId);
|
|
313793
|
+
}, 3e4).unref?.();
|
|
313794
|
+
}
|
|
313795
|
+
function getInFlightChat(sessionId) {
|
|
313796
|
+
const cached = inFlight.get(sessionId);
|
|
313797
|
+
if (cached) return cached;
|
|
313798
|
+
try {
|
|
313799
|
+
const p2 = inFlightPath(sessionId);
|
|
313800
|
+
if (existsSync74(p2)) {
|
|
313801
|
+
const parsed = JSON.parse(readFileSync58(p2, "utf-8"));
|
|
313802
|
+
if (parsed && parsed.sessionId === sessionId) {
|
|
313803
|
+
return parsed;
|
|
313593
313804
|
}
|
|
313594
|
-
|
|
313595
|
-
|
|
313596
|
-
|
|
313597
|
-
|
|
313598
|
-
|
|
313599
|
-
|
|
313600
|
-
|
|
313601
|
-
|
|
313602
|
-
|
|
313603
|
-
|
|
313604
|
-
|
|
313805
|
+
}
|
|
313806
|
+
} catch {
|
|
313807
|
+
}
|
|
313808
|
+
return null;
|
|
313809
|
+
}
|
|
313810
|
+
function compactSession(session, maxMessages = 40) {
|
|
313811
|
+
if (session.messages.length <= maxMessages) return;
|
|
313812
|
+
const system = session.messages[0];
|
|
313813
|
+
const recent = session.messages.slice(-20);
|
|
313814
|
+
const middle = session.messages.slice(1, -20);
|
|
313815
|
+
const summary = `[Earlier conversation: ${middle.length} messages discussed ` + middle.filter((m2) => m2.role === "user").map((m2) => m2.content.slice(0, 50)).slice(0, 5).join(", ") + "...]";
|
|
313816
|
+
session.messages = [
|
|
313817
|
+
system,
|
|
313818
|
+
{ role: "system", content: summary },
|
|
313819
|
+
...recent
|
|
313820
|
+
];
|
|
313821
|
+
}
|
|
313822
|
+
var sessions, inFlight, SESSION_TTL_MS, INFERENCE_ROLES, PARTIAL_TAIL_BUDGET;
|
|
313823
|
+
var init_chat_session = __esm({
|
|
313824
|
+
"packages/cli/src/api/chat-session.ts"() {
|
|
313825
|
+
"use strict";
|
|
313826
|
+
sessions = /* @__PURE__ */ new Map();
|
|
313827
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
313828
|
+
SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
313829
|
+
setInterval(() => {
|
|
313830
|
+
const now = Date.now();
|
|
313831
|
+
for (const [id, s2] of sessions) {
|
|
313832
|
+
if (now - s2.lastActivity > SESSION_TTL_MS) sessions.delete(id);
|
|
313605
313833
|
}
|
|
313606
|
-
|
|
313607
|
-
|
|
313608
|
-
|
|
313609
|
-
|
|
313610
|
-
|
|
313834
|
+
}, 5 * 60 * 1e3);
|
|
313835
|
+
INFERENCE_ROLES = ["system", "user", "assistant"];
|
|
313836
|
+
PARTIAL_TAIL_BUDGET = 8 * 1024;
|
|
313837
|
+
}
|
|
313838
|
+
});
|
|
313839
|
+
|
|
313840
|
+
// packages/cli/src/tui/mouse-filter.ts
|
|
313841
|
+
var mouse_filter_exports = {};
|
|
313842
|
+
__export(mouse_filter_exports, {
|
|
313843
|
+
MouseFilterStream: () => MouseFilterStream
|
|
313844
|
+
});
|
|
313845
|
+
import { Transform } from "node:stream";
|
|
313846
|
+
var MouseFilterStream;
|
|
313847
|
+
var init_mouse_filter = __esm({
|
|
313848
|
+
"packages/cli/src/tui/mouse-filter.ts"() {
|
|
313849
|
+
"use strict";
|
|
313850
|
+
MouseFilterStream = class extends Transform {
|
|
313851
|
+
buffer = "";
|
|
313852
|
+
onScroll = null;
|
|
313853
|
+
onActivity = null;
|
|
313854
|
+
onPointer = null;
|
|
313855
|
+
flushTimer = null;
|
|
313856
|
+
constructor(scrollHandler, activityHandler, pointerHandler) {
|
|
313857
|
+
super();
|
|
313858
|
+
this.onScroll = scrollHandler;
|
|
313859
|
+
this.onActivity = activityHandler ?? null;
|
|
313860
|
+
this.onPointer = pointerHandler ?? null;
|
|
313861
|
+
}
|
|
313862
|
+
_transform(chunk, _encoding, callback) {
|
|
313863
|
+
this.buffer += chunk.toString();
|
|
313864
|
+
this.processBuffer(callback);
|
|
313865
|
+
}
|
|
313866
|
+
processBuffer(callback) {
|
|
313867
|
+
let output = "";
|
|
313868
|
+
let i2 = 0;
|
|
313869
|
+
while (i2 < this.buffer.length) {
|
|
313870
|
+
if (this.buffer[i2] === "\x1B") {
|
|
313871
|
+
const remaining = this.buffer.slice(i2);
|
|
313872
|
+
const mouseMatch = remaining.match(/^\x1B\[<(\d+);(\d+);(\d+)([Mm])/);
|
|
313873
|
+
if (mouseMatch) {
|
|
313874
|
+
const btn = parseInt(mouseMatch[1]);
|
|
313875
|
+
const col = parseInt(mouseMatch[2]);
|
|
313876
|
+
const row = parseInt(mouseMatch[3]);
|
|
313877
|
+
const suffix = mouseMatch[4];
|
|
313878
|
+
if ((btn === 64 || btn === 96) && this.onScroll) this.onScroll("up", 3, row);
|
|
313879
|
+
else if ((btn === 65 || btn === 97) && this.onScroll) this.onScroll("down", 3, row);
|
|
313880
|
+
else if (this.onPointer) {
|
|
313881
|
+
if ((btn === 0 || btn === 1 || btn === 2) && suffix === "M") this.onPointer("press", col, row);
|
|
313882
|
+
else if (btn >= 32 && btn <= 35 && suffix === "M") this.onPointer("drag", col, row);
|
|
313883
|
+
else if (suffix === "m") this.onPointer("release", col, row);
|
|
313884
|
+
}
|
|
313885
|
+
if (this.onActivity) this.onActivity();
|
|
313886
|
+
i2 += mouseMatch[0].length;
|
|
313887
|
+
continue;
|
|
313888
|
+
}
|
|
313889
|
+
if (remaining.startsWith("\x1B[M")) {
|
|
313890
|
+
if (remaining.length >= 6) {
|
|
313891
|
+
if (this.onActivity) this.onActivity();
|
|
313892
|
+
i2 += 6;
|
|
313893
|
+
continue;
|
|
313894
|
+
}
|
|
313895
|
+
break;
|
|
313896
|
+
}
|
|
313897
|
+
if (remaining.startsWith("\x1B[<") && remaining.length < 15) {
|
|
313898
|
+
break;
|
|
313899
|
+
}
|
|
313900
|
+
if (remaining.startsWith("\x1B[") && remaining.length === 2) {
|
|
313901
|
+
break;
|
|
313902
|
+
}
|
|
313903
|
+
if (remaining.length === 1) {
|
|
313904
|
+
break;
|
|
313905
|
+
}
|
|
313906
|
+
}
|
|
313907
|
+
output += this.buffer[i2];
|
|
313908
|
+
i2++;
|
|
313909
|
+
}
|
|
313910
|
+
this.buffer = this.buffer.slice(i2);
|
|
313911
|
+
if (output.length > 0) {
|
|
313912
|
+
this.push(output);
|
|
313913
|
+
}
|
|
313914
|
+
if (this.buffer.length > 0) {
|
|
313915
|
+
if (this.flushTimer) clearTimeout(this.flushTimer);
|
|
313916
|
+
this.flushTimer = setTimeout(() => {
|
|
313917
|
+
if (this.buffer.length > 0) {
|
|
313918
|
+
if (this.buffer.startsWith("\x1B[<") || this.buffer.startsWith("\x1B[M")) {
|
|
313919
|
+
this.buffer = "";
|
|
313920
|
+
} else if (this.buffer === "\x1B" || this.buffer === "\x1B[") {
|
|
313921
|
+
this.push(this.buffer);
|
|
313922
|
+
this.buffer = "";
|
|
313923
|
+
} else {
|
|
313924
|
+
this.push(this.buffer);
|
|
313925
|
+
this.buffer = "";
|
|
313926
|
+
}
|
|
313927
|
+
}
|
|
313928
|
+
}, 50);
|
|
313929
|
+
}
|
|
313930
|
+
callback();
|
|
313931
|
+
}
|
|
313932
|
+
_flush(callback) {
|
|
313933
|
+
if (this.flushTimer) {
|
|
313934
|
+
clearTimeout(this.flushTimer);
|
|
313935
|
+
this.flushTimer = null;
|
|
313936
|
+
}
|
|
313937
|
+
if (this.buffer.length > 0) {
|
|
313938
|
+
this.push(this.buffer);
|
|
313939
|
+
this.buffer = "";
|
|
313940
|
+
}
|
|
313941
|
+
callback();
|
|
313942
|
+
}
|
|
313943
|
+
};
|
|
313944
|
+
}
|
|
313945
|
+
});
|
|
313946
|
+
|
|
313947
|
+
// packages/cli/src/tui/direct-input.ts
|
|
313948
|
+
var direct_input_exports = {};
|
|
313949
|
+
__export(direct_input_exports, {
|
|
313950
|
+
DirectInput: () => DirectInput
|
|
313951
|
+
});
|
|
313952
|
+
import { EventEmitter as EventEmitter9 } from "node:events";
|
|
313953
|
+
var DirectInput;
|
|
313954
|
+
var init_direct_input = __esm({
|
|
313955
|
+
"packages/cli/src/tui/direct-input.ts"() {
|
|
313956
|
+
"use strict";
|
|
313957
|
+
DirectInput = class extends EventEmitter9 {
|
|
313958
|
+
/** Current input line text */
|
|
313959
|
+
line = "";
|
|
313960
|
+
/** Cursor position within .line (0-based) */
|
|
313961
|
+
cursor = 0;
|
|
313962
|
+
_history;
|
|
313963
|
+
_historySize;
|
|
313964
|
+
_historyIndex = -1;
|
|
313965
|
+
_savedLine = "";
|
|
313966
|
+
// saved current input when navigating history
|
|
313967
|
+
_completer = null;
|
|
313968
|
+
_paused = false;
|
|
313969
|
+
_closed = false;
|
|
313970
|
+
_input;
|
|
313971
|
+
_buffer = "";
|
|
313972
|
+
// partial escape sequence buffer
|
|
313973
|
+
_flushTimer = null;
|
|
313974
|
+
constructor(input, options2) {
|
|
313975
|
+
super();
|
|
313976
|
+
this._input = input;
|
|
313977
|
+
this._history = [...options2?.history ?? []];
|
|
313978
|
+
this._historySize = options2?.historySize ?? 500;
|
|
313979
|
+
this._completer = options2?.completer ?? null;
|
|
313980
|
+
input.on("data", (chunk) => {
|
|
313981
|
+
if (this._paused || this._closed) return;
|
|
313982
|
+
this.feed(chunk.toString("utf8"));
|
|
313983
|
+
});
|
|
313984
|
+
input.on("end", () => {
|
|
313985
|
+
if (!this._closed) this.close();
|
|
313986
|
+
});
|
|
313987
|
+
}
|
|
313988
|
+
/** Process raw input data — parse escape sequences and printable chars */
|
|
313989
|
+
feed(data) {
|
|
313990
|
+
this._buffer += data;
|
|
313991
|
+
this._processBuffer();
|
|
313992
|
+
}
|
|
313993
|
+
/** Pause input processing (for overlay transitions) */
|
|
313994
|
+
pause() {
|
|
313995
|
+
this._paused = true;
|
|
313996
|
+
}
|
|
313997
|
+
/** Resume input processing */
|
|
313998
|
+
resume() {
|
|
313999
|
+
this._paused = false;
|
|
314000
|
+
}
|
|
314001
|
+
/** Close the input handler */
|
|
314002
|
+
close() {
|
|
314003
|
+
if (this._closed) return;
|
|
314004
|
+
this._closed = true;
|
|
314005
|
+
if (this._flushTimer) {
|
|
314006
|
+
clearTimeout(this._flushTimer);
|
|
314007
|
+
this._flushTimer = null;
|
|
314008
|
+
}
|
|
314009
|
+
this.emit("close");
|
|
314010
|
+
}
|
|
314011
|
+
/** No-op — readline compat (StatusBar renders the prompt, not us) */
|
|
314012
|
+
setPrompt(_prompt) {
|
|
314013
|
+
}
|
|
314014
|
+
/** No-op — readline compat */
|
|
314015
|
+
prompt(_preserveCursor) {
|
|
314016
|
+
}
|
|
314017
|
+
/** Set the line content and cursor position (for Esc-to-recall or suggestion apply) */
|
|
314018
|
+
setLine(text, cursorPos) {
|
|
314019
|
+
this.line = text;
|
|
314020
|
+
this.cursor = cursorPos ?? text.length;
|
|
314021
|
+
}
|
|
314022
|
+
/** Pre-submit hook — called before Enter submits. Return true to consume Enter. */
|
|
314023
|
+
_preSubmit = null;
|
|
314024
|
+
setPreSubmit(hook) {
|
|
314025
|
+
this._preSubmit = hook;
|
|
314026
|
+
}
|
|
314027
|
+
/** Navigate history up (older) */
|
|
314028
|
+
historyUp() {
|
|
314029
|
+
if (this._history.length === 0) return;
|
|
314030
|
+
if (this._historyIndex === -1) {
|
|
314031
|
+
this._savedLine = this.line;
|
|
314032
|
+
}
|
|
314033
|
+
if (this._historyIndex < this._history.length - 1) {
|
|
314034
|
+
this._historyIndex++;
|
|
314035
|
+
this.line = this._history[this._historyIndex];
|
|
314036
|
+
this.cursor = this.line.length;
|
|
314037
|
+
}
|
|
314038
|
+
}
|
|
314039
|
+
/** Navigate history down (newer) */
|
|
314040
|
+
historyDown() {
|
|
314041
|
+
if (this._historyIndex <= -1) return;
|
|
314042
|
+
this._historyIndex--;
|
|
314043
|
+
if (this._historyIndex === -1) {
|
|
313611
314044
|
this.line = this._savedLine;
|
|
313612
314045
|
} else {
|
|
313613
314046
|
this.line = this._history[this._historyIndex];
|
|
@@ -313922,13 +314355,13 @@ __export(audit_log_exports, {
|
|
|
313922
314355
|
recordAudit: () => recordAudit,
|
|
313923
314356
|
sanitizeBody: () => sanitizeBody
|
|
313924
314357
|
});
|
|
313925
|
-
import { mkdirSync as
|
|
313926
|
-
import { join as
|
|
314358
|
+
import { mkdirSync as mkdirSync45, appendFileSync as appendFileSync5, readFileSync as readFileSync59, existsSync as existsSync75 } from "node:fs";
|
|
314359
|
+
import { join as join92 } from "node:path";
|
|
313927
314360
|
function initAuditLog(oaDir) {
|
|
313928
|
-
auditDir =
|
|
313929
|
-
auditFile =
|
|
314361
|
+
auditDir = join92(oaDir, "audit");
|
|
314362
|
+
auditFile = join92(auditDir, "audit.jsonl");
|
|
313930
314363
|
try {
|
|
313931
|
-
|
|
314364
|
+
mkdirSync45(auditDir, { recursive: true });
|
|
313932
314365
|
initialized = true;
|
|
313933
314366
|
} catch {
|
|
313934
314367
|
}
|
|
@@ -313957,9 +314390,9 @@ function sanitizeBody(body, maxLen = 200) {
|
|
|
313957
314390
|
return safe.length > maxLen ? safe.slice(0, maxLen) + "..." : safe;
|
|
313958
314391
|
}
|
|
313959
314392
|
function queryAudit(opts) {
|
|
313960
|
-
if (!initialized || !
|
|
314393
|
+
if (!initialized || !existsSync75(auditFile)) return [];
|
|
313961
314394
|
try {
|
|
313962
|
-
const raw =
|
|
314395
|
+
const raw = readFileSync59(auditFile, "utf-8");
|
|
313963
314396
|
const lines = raw.split("\n").filter(Boolean);
|
|
313964
314397
|
let records = lines.map((l2) => {
|
|
313965
314398
|
try {
|
|
@@ -313996,8 +314429,8 @@ var init_audit_log = __esm({
|
|
|
313996
314429
|
|
|
313997
314430
|
// packages/cli/src/api/disk-task-output.ts
|
|
313998
314431
|
import { open } from "node:fs/promises";
|
|
313999
|
-
import { existsSync as
|
|
314000
|
-
import { dirname as
|
|
314432
|
+
import { existsSync as existsSync76, mkdirSync as mkdirSync46, statSync as statSync21 } from "node:fs";
|
|
314433
|
+
import { dirname as dirname26 } from "node:path";
|
|
314001
314434
|
import * as fsConstants from "node:constants";
|
|
314002
314435
|
var O_NOFOLLOW2, O_APPEND2, O_CREAT2, O_WRONLY2, OPEN_FLAGS_WRITE, OPEN_MODE, DiskTaskOutput;
|
|
314003
314436
|
var init_disk_task_output = __esm({
|
|
@@ -314016,7 +314449,7 @@ var init_disk_task_output = __esm({
|
|
|
314016
314449
|
fileSize = 0;
|
|
314017
314450
|
constructor(outputPath) {
|
|
314018
314451
|
this.path = outputPath;
|
|
314019
|
-
|
|
314452
|
+
mkdirSync46(dirname26(outputPath), { recursive: true });
|
|
314020
314453
|
}
|
|
314021
314454
|
/** Queue content for async append. Non-blocking. */
|
|
314022
314455
|
append(chunk) {
|
|
@@ -314092,7 +314525,7 @@ var init_disk_task_output = __esm({
|
|
|
314092
314525
|
async readFrom(offset, limit = 65536) {
|
|
314093
314526
|
let handle2 = null;
|
|
314094
314527
|
try {
|
|
314095
|
-
if (!
|
|
314528
|
+
if (!existsSync76(this.path)) {
|
|
314096
314529
|
return { content: "", nextOffset: offset, eof: true, size: 0 };
|
|
314097
314530
|
}
|
|
314098
314531
|
const st = statSync21(this.path);
|
|
@@ -314294,19 +314727,19 @@ __export(aiwg_exports, {
|
|
|
314294
314727
|
resolveAiwgRoot: () => resolveAiwgRoot,
|
|
314295
314728
|
tryRouteAiwg: () => tryRouteAiwg
|
|
314296
314729
|
});
|
|
314297
|
-
import { existsSync as
|
|
314298
|
-
import { join as
|
|
314299
|
-
import { homedir as
|
|
314730
|
+
import { existsSync as existsSync77, readFileSync as readFileSync60, readdirSync as readdirSync25, statSync as statSync22 } from "node:fs";
|
|
314731
|
+
import { join as join93 } from "node:path";
|
|
314732
|
+
import { homedir as homedir32 } from "node:os";
|
|
314300
314733
|
import { execSync as execSync53 } from "node:child_process";
|
|
314301
314734
|
function resolveAiwgRoot() {
|
|
314302
314735
|
if (_cachedAiwgRoot !== void 0) return _cachedAiwgRoot;
|
|
314303
314736
|
const envRoot = process.env["OA_AIWG_ROOT"];
|
|
314304
|
-
if (envRoot &&
|
|
314737
|
+
if (envRoot && existsSync77(join93(envRoot, "package.json"))) {
|
|
314305
314738
|
_cachedAiwgRoot = envRoot;
|
|
314306
314739
|
return envRoot;
|
|
314307
314740
|
}
|
|
314308
|
-
const shareDir =
|
|
314309
|
-
if (
|
|
314741
|
+
const shareDir = join93(homedir32(), ".local", "share", "ai-writing-guide");
|
|
314742
|
+
if (existsSync77(join93(shareDir, "agentic"))) {
|
|
314310
314743
|
_cachedAiwgRoot = shareDir;
|
|
314311
314744
|
return shareDir;
|
|
314312
314745
|
}
|
|
@@ -314316,8 +314749,8 @@ function resolveAiwgRoot() {
|
|
|
314316
314749
|
timeout: 5e3,
|
|
314317
314750
|
stdio: ["pipe", "pipe", "pipe"]
|
|
314318
314751
|
}).trim();
|
|
314319
|
-
const candidate =
|
|
314320
|
-
if (
|
|
314752
|
+
const candidate = join93(globalRoot, "aiwg");
|
|
314753
|
+
if (existsSync77(join93(candidate, "package.json"))) {
|
|
314321
314754
|
_cachedAiwgRoot = candidate;
|
|
314322
314755
|
return candidate;
|
|
314323
314756
|
}
|
|
@@ -314328,22 +314761,22 @@ function resolveAiwgRoot() {
|
|
|
314328
314761
|
"/usr/lib/node_modules/aiwg",
|
|
314329
314762
|
"/opt/homebrew/lib/node_modules/aiwg"
|
|
314330
314763
|
]) {
|
|
314331
|
-
if (
|
|
314764
|
+
if (existsSync77(join93(p2, "package.json"))) {
|
|
314332
314765
|
_cachedAiwgRoot = p2;
|
|
314333
314766
|
return p2;
|
|
314334
314767
|
}
|
|
314335
314768
|
}
|
|
314336
314769
|
const versionDirs = [
|
|
314337
|
-
|
|
314338
|
-
|
|
314770
|
+
join93(homedir32(), ".nvm", "versions", "node"),
|
|
314771
|
+
join93(homedir32(), ".local", "share", "fnm", "node-versions")
|
|
314339
314772
|
];
|
|
314340
314773
|
for (const vdir of versionDirs) {
|
|
314341
|
-
if (!
|
|
314774
|
+
if (!existsSync77(vdir)) continue;
|
|
314342
314775
|
try {
|
|
314343
|
-
for (const ver of
|
|
314776
|
+
for (const ver of readdirSync25(vdir)) {
|
|
314344
314777
|
for (const prefix of ["lib/node_modules/aiwg", "installation/lib/node_modules/aiwg"]) {
|
|
314345
|
-
const cand =
|
|
314346
|
-
if (
|
|
314778
|
+
const cand = join93(vdir, ver, prefix);
|
|
314779
|
+
if (existsSync77(join93(cand, "package.json"))) {
|
|
314347
314780
|
_cachedAiwgRoot = cand;
|
|
314348
314781
|
return cand;
|
|
314349
314782
|
}
|
|
@@ -314361,11 +314794,11 @@ function resolveAiwgRoot() {
|
|
|
314361
314794
|
if (whichAiwg) {
|
|
314362
314795
|
let cur = whichAiwg;
|
|
314363
314796
|
for (let i2 = 0; i2 < 8; i2++) {
|
|
314364
|
-
cur =
|
|
314365
|
-
const pj =
|
|
314366
|
-
if (
|
|
314797
|
+
cur = join93(cur, "..");
|
|
314798
|
+
const pj = join93(cur, "package.json");
|
|
314799
|
+
if (existsSync77(pj)) {
|
|
314367
314800
|
try {
|
|
314368
|
-
const pkg = JSON.parse(
|
|
314801
|
+
const pkg = JSON.parse(readFileSync60(pj, "utf-8"));
|
|
314369
314802
|
if (pkg.name === "aiwg") {
|
|
314370
314803
|
_cachedAiwgRoot = cur;
|
|
314371
314804
|
return cur;
|
|
@@ -314387,14 +314820,14 @@ function listAiwgFrameworks() {
|
|
|
314387
314820
|
_cachedFrameworks = [];
|
|
314388
314821
|
return _cachedFrameworks;
|
|
314389
314822
|
}
|
|
314390
|
-
const frameworksDir =
|
|
314391
|
-
if (!
|
|
314823
|
+
const frameworksDir = join93(root, "agentic", "code", "frameworks");
|
|
314824
|
+
if (!existsSync77(frameworksDir)) {
|
|
314392
314825
|
_cachedFrameworks = [];
|
|
314393
314826
|
return _cachedFrameworks;
|
|
314394
314827
|
}
|
|
314395
314828
|
const out = [];
|
|
314396
|
-
for (const name10 of
|
|
314397
|
-
const p2 =
|
|
314829
|
+
for (const name10 of readdirSync25(frameworksDir)) {
|
|
314830
|
+
const p2 = join93(frameworksDir, name10);
|
|
314398
314831
|
try {
|
|
314399
314832
|
const st = statSync22(p2);
|
|
314400
314833
|
if (!st.isDirectory()) continue;
|
|
@@ -314420,9 +314853,9 @@ function aggregateDir(dir, depth = 0) {
|
|
|
314420
314853
|
const out = { files: 0, bytes: 0, mdChars: 0, skills: 0, agents: 0, commands: 0 };
|
|
314421
314854
|
if (depth > 8) return out;
|
|
314422
314855
|
try {
|
|
314423
|
-
for (const e2 of
|
|
314856
|
+
for (const e2 of readdirSync25(dir, { withFileTypes: true })) {
|
|
314424
314857
|
if (e2.name.startsWith(".") || e2.name === "node_modules") continue;
|
|
314425
|
-
const p2 =
|
|
314858
|
+
const p2 = join93(dir, e2.name);
|
|
314426
314859
|
if (e2.isDirectory()) {
|
|
314427
314860
|
const sub = aggregateDir(p2, depth + 1);
|
|
314428
314861
|
out.files += sub.files;
|
|
@@ -314452,10 +314885,10 @@ function aggregateDir(dir, depth = 0) {
|
|
|
314452
314885
|
}
|
|
314453
314886
|
function readFirstLineDescription(dir) {
|
|
314454
314887
|
for (const candidate of ["README.md", "SKILL.md", "INDEX.md"]) {
|
|
314455
|
-
const p2 =
|
|
314456
|
-
if (!
|
|
314888
|
+
const p2 = join93(dir, candidate);
|
|
314889
|
+
if (!existsSync77(p2)) continue;
|
|
314457
314890
|
try {
|
|
314458
|
-
const txt =
|
|
314891
|
+
const txt = readFileSync60(p2, "utf-8");
|
|
314459
314892
|
const descMatch = txt.match(/^description:\s*(.+)$/m);
|
|
314460
314893
|
if (descMatch) return descMatch[1].trim().slice(0, 200);
|
|
314461
314894
|
for (const line of txt.split("\n")) {
|
|
@@ -314477,12 +314910,12 @@ function listAiwgItems() {
|
|
|
314477
314910
|
}
|
|
314478
314911
|
const out = [];
|
|
314479
314912
|
const walkRoots = [
|
|
314480
|
-
|
|
314481
|
-
|
|
314482
|
-
|
|
314913
|
+
join93(root, "agentic", "code", "frameworks"),
|
|
314914
|
+
join93(root, "agentic", "code", "addons"),
|
|
314915
|
+
join93(root, "plugins")
|
|
314483
314916
|
];
|
|
314484
314917
|
for (const wr of walkRoots) {
|
|
314485
|
-
if (!
|
|
314918
|
+
if (!existsSync77(wr)) continue;
|
|
314486
314919
|
walkForItems(wr, out, 0);
|
|
314487
314920
|
}
|
|
314488
314921
|
_cachedItems = out;
|
|
@@ -314491,9 +314924,9 @@ function listAiwgItems() {
|
|
|
314491
314924
|
function walkForItems(dir, out, depth) {
|
|
314492
314925
|
if (depth > 10) return;
|
|
314493
314926
|
try {
|
|
314494
|
-
for (const e2 of
|
|
314927
|
+
for (const e2 of readdirSync25(dir, { withFileTypes: true })) {
|
|
314495
314928
|
if (e2.name.startsWith(".") || e2.name === "node_modules") continue;
|
|
314496
|
-
const p2 =
|
|
314929
|
+
const p2 = join93(dir, e2.name);
|
|
314497
314930
|
if (e2.isDirectory()) {
|
|
314498
314931
|
walkForItems(p2, out, depth + 1);
|
|
314499
314932
|
} else if (e2.isFile() && e2.name.endsWith(".md")) {
|
|
@@ -314506,7 +314939,7 @@ function walkForItems(dir, out, depth) {
|
|
|
314506
314939
|
}
|
|
314507
314940
|
function parseItem(p2) {
|
|
314508
314941
|
try {
|
|
314509
|
-
const raw =
|
|
314942
|
+
const raw = readFileSync60(p2, "utf-8");
|
|
314510
314943
|
const header = raw.slice(0, 3e3);
|
|
314511
314944
|
const nameMatch = header.match(/^name:\s*(.+)$/m);
|
|
314512
314945
|
const descMatch = header.match(/^description:\s*(.+)$/m);
|
|
@@ -314544,8 +314977,8 @@ function deriveSource(p2) {
|
|
|
314544
314977
|
}
|
|
314545
314978
|
function loadAiwgItemContent(path5, maxBytes = 2e4) {
|
|
314546
314979
|
try {
|
|
314547
|
-
if (!
|
|
314548
|
-
const raw =
|
|
314980
|
+
if (!existsSync77(path5)) return null;
|
|
314981
|
+
const raw = readFileSync60(path5, "utf-8");
|
|
314549
314982
|
return raw.length > maxBytes ? raw.slice(0, maxBytes) + "\n\n...(truncated for context budget)" : raw;
|
|
314550
314983
|
} catch {
|
|
314551
314984
|
return null;
|
|
@@ -314558,14 +314991,14 @@ function listAiwgAddons() {
|
|
|
314558
314991
|
_cachedAddons = [];
|
|
314559
314992
|
return _cachedAddons;
|
|
314560
314993
|
}
|
|
314561
|
-
const addonsDir =
|
|
314562
|
-
if (!
|
|
314994
|
+
const addonsDir = join93(root, "agentic", "code", "addons");
|
|
314995
|
+
if (!existsSync77(addonsDir)) {
|
|
314563
314996
|
_cachedAddons = [];
|
|
314564
314997
|
return _cachedAddons;
|
|
314565
314998
|
}
|
|
314566
314999
|
const out = [];
|
|
314567
|
-
for (const name10 of
|
|
314568
|
-
const p2 =
|
|
315000
|
+
for (const name10 of readdirSync25(addonsDir)) {
|
|
315001
|
+
const p2 = join93(addonsDir, name10);
|
|
314569
315002
|
try {
|
|
314570
315003
|
const st = statSync22(p2);
|
|
314571
315004
|
if (!st.isDirectory()) continue;
|
|
@@ -315047,9 +315480,9 @@ var init_aiwg = __esm({
|
|
|
315047
315480
|
});
|
|
315048
315481
|
|
|
315049
315482
|
// packages/cli/src/api/routes-v1.ts
|
|
315050
|
-
import { existsSync as
|
|
315051
|
-
import { join as
|
|
315052
|
-
import { homedir as
|
|
315483
|
+
import { existsSync as existsSync78, readFileSync as readFileSync61, readdirSync as readdirSync26, statSync as statSync23 } from "node:fs";
|
|
315484
|
+
import { join as join94, resolve as pathResolve } from "node:path";
|
|
315485
|
+
import { homedir as homedir33 } from "node:os";
|
|
315053
315486
|
async function tryRouteV1(ctx3) {
|
|
315054
315487
|
const { pathname, method } = ctx3;
|
|
315055
315488
|
if (pathname === "/v1/skills" && method === "GET") {
|
|
@@ -315254,11 +315687,11 @@ async function handleGetSkill(ctx3, name10) {
|
|
|
315254
315687
|
async function fallbackDiscoverSkills() {
|
|
315255
315688
|
return (_root) => {
|
|
315256
315689
|
const roots = [
|
|
315257
|
-
|
|
315690
|
+
join94(homedir33(), ".local", "share", "ai-writing-guide")
|
|
315258
315691
|
];
|
|
315259
315692
|
const out = [];
|
|
315260
315693
|
for (const root of roots) {
|
|
315261
|
-
if (!
|
|
315694
|
+
if (!existsSync78(root)) continue;
|
|
315262
315695
|
walkForSkills(root, out, 0);
|
|
315263
315696
|
}
|
|
315264
315697
|
return out;
|
|
@@ -315267,14 +315700,14 @@ async function fallbackDiscoverSkills() {
|
|
|
315267
315700
|
function walkForSkills(dir, out, depth) {
|
|
315268
315701
|
if (depth > 6) return;
|
|
315269
315702
|
try {
|
|
315270
|
-
for (const e2 of
|
|
315703
|
+
for (const e2 of readdirSync26(dir, { withFileTypes: true })) {
|
|
315271
315704
|
if (e2.name.startsWith(".") || e2.name === "node_modules") continue;
|
|
315272
|
-
const p2 =
|
|
315705
|
+
const p2 = join94(dir, e2.name);
|
|
315273
315706
|
if (e2.isDirectory()) {
|
|
315274
315707
|
walkForSkills(p2, out, depth + 1);
|
|
315275
315708
|
} else if (e2.isFile() && e2.name === "SKILL.md") {
|
|
315276
315709
|
try {
|
|
315277
|
-
const content =
|
|
315710
|
+
const content = readFileSync61(p2, "utf-8").slice(0, 2e3);
|
|
315278
315711
|
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
315279
315712
|
const descMatch = content.match(/^description:\s*(.+)$/m);
|
|
315280
315713
|
out.push({
|
|
@@ -315458,7 +315891,7 @@ async function getMemoryStores() {
|
|
|
315458
315891
|
if (memoryInitTried) return null;
|
|
315459
315892
|
memoryInitTried = true;
|
|
315460
315893
|
try {
|
|
315461
|
-
const dbPath =
|
|
315894
|
+
const dbPath = join94(homedir33(), ".open-agents", "memory.db");
|
|
315462
315895
|
const sharedDb = initDb(dbPath);
|
|
315463
315896
|
memoryStoresCache = {
|
|
315464
315897
|
episode: new EpisodeStore(dbPath),
|
|
@@ -315716,7 +316149,7 @@ async function handleFilesRead(ctx3) {
|
|
|
315716
316149
|
}));
|
|
315717
316150
|
return true;
|
|
315718
316151
|
}
|
|
315719
|
-
if (!
|
|
316152
|
+
if (!existsSync78(resolved)) {
|
|
315720
316153
|
sendProblem(res, problemDetails({
|
|
315721
316154
|
type: P.notFound,
|
|
315722
316155
|
status: 404,
|
|
@@ -315748,7 +316181,7 @@ async function handleFilesRead(ctx3) {
|
|
|
315748
316181
|
}));
|
|
315749
316182
|
return true;
|
|
315750
316183
|
}
|
|
315751
|
-
const content =
|
|
316184
|
+
const content = readFileSync61(resolved, "utf-8");
|
|
315752
316185
|
const offset = typeof body.offset === "number" && body.offset >= 0 ? body.offset : 0;
|
|
315753
316186
|
const limit = typeof body.limit === "number" && body.limit > 0 ? body.limit : content.length;
|
|
315754
316187
|
const slice2 = content.slice(offset, offset + limit);
|
|
@@ -315979,14 +316412,14 @@ async function handleNexusStatus(ctx3) {
|
|
|
315979
316412
|
const { res, requestId } = ctx3;
|
|
315980
316413
|
try {
|
|
315981
316414
|
const statePaths = [
|
|
315982
|
-
|
|
315983
|
-
|
|
316415
|
+
join94(process.cwd(), ".oa", "nexus-peer-state.json"),
|
|
316416
|
+
join94(homedir33(), ".open-agents", "nexus-peer-cache.json")
|
|
315984
316417
|
];
|
|
315985
316418
|
const states = [];
|
|
315986
316419
|
for (const p2 of statePaths) {
|
|
315987
|
-
if (!
|
|
316420
|
+
if (!existsSync78(p2)) continue;
|
|
315988
316421
|
try {
|
|
315989
|
-
const raw =
|
|
316422
|
+
const raw = readFileSync61(p2, "utf-8");
|
|
315990
316423
|
states.push({ source: p2, data: JSON.parse(raw) });
|
|
315991
316424
|
} catch (e2) {
|
|
315992
316425
|
states.push({ source: p2, error: String(e2) });
|
|
@@ -316013,8 +316446,8 @@ async function handleNexusStatus(ctx3) {
|
|
|
316013
316446
|
}
|
|
316014
316447
|
function loadAgentName() {
|
|
316015
316448
|
try {
|
|
316016
|
-
const p2 =
|
|
316017
|
-
if (
|
|
316449
|
+
const p2 = join94(homedir33(), ".open-agents", "agent-name");
|
|
316450
|
+
if (existsSync78(p2)) return readFileSync61(p2, "utf-8").trim();
|
|
316018
316451
|
} catch {
|
|
316019
316452
|
}
|
|
316020
316453
|
return null;
|
|
@@ -316023,14 +316456,14 @@ async function handleSponsors(ctx3) {
|
|
|
316023
316456
|
const { req: req2, res, url, requestId } = ctx3;
|
|
316024
316457
|
try {
|
|
316025
316458
|
const candidates = [
|
|
316026
|
-
|
|
316027
|
-
|
|
316459
|
+
join94(homedir33(), ".open-agents", "sponsor-cache.json"),
|
|
316460
|
+
join94(homedir33(), ".open-agents", "sponsors.json")
|
|
316028
316461
|
];
|
|
316029
316462
|
let sponsors = [];
|
|
316030
316463
|
for (const p2 of candidates) {
|
|
316031
|
-
if (!
|
|
316464
|
+
if (!existsSync78(p2)) continue;
|
|
316032
316465
|
try {
|
|
316033
|
-
const raw = JSON.parse(
|
|
316466
|
+
const raw = JSON.parse(readFileSync61(p2, "utf-8"));
|
|
316034
316467
|
if (Array.isArray(raw)) {
|
|
316035
316468
|
sponsors = raw;
|
|
316036
316469
|
break;
|
|
@@ -316099,8 +316532,8 @@ async function handleEvaluate(ctx3) {
|
|
|
316099
316532
|
}));
|
|
316100
316533
|
return true;
|
|
316101
316534
|
}
|
|
316102
|
-
const jobPath =
|
|
316103
|
-
if (!
|
|
316535
|
+
const jobPath = join94(process.cwd(), ".oa", "jobs", `${runId}.json`);
|
|
316536
|
+
if (!existsSync78(jobPath)) {
|
|
316104
316537
|
sendProblem(res, problemDetails({
|
|
316105
316538
|
type: P.notFound,
|
|
316106
316539
|
status: 404,
|
|
@@ -316110,7 +316543,7 @@ async function handleEvaluate(ctx3) {
|
|
|
316110
316543
|
}));
|
|
316111
316544
|
return true;
|
|
316112
316545
|
}
|
|
316113
|
-
const job = JSON.parse(
|
|
316546
|
+
const job = JSON.parse(readFileSync61(jobPath, "utf-8"));
|
|
316114
316547
|
sendJson(res, 200, {
|
|
316115
316548
|
run_id: runId,
|
|
316116
316549
|
task: job.task,
|
|
@@ -316248,17 +316681,17 @@ async function handleListAgentTypes(ctx3) {
|
|
|
316248
316681
|
}
|
|
316249
316682
|
async function handleListEngines(ctx3) {
|
|
316250
316683
|
const { res } = ctx3;
|
|
316251
|
-
const home =
|
|
316684
|
+
const home = homedir33();
|
|
316252
316685
|
sendJson(res, 200, {
|
|
316253
316686
|
engines: [
|
|
316254
|
-
{ name: "dream", state_file:
|
|
316255
|
-
{ name: "bless", state_file:
|
|
316256
|
-
{ name: "call", state_file:
|
|
316257
|
-
{ name: "listen", state_file:
|
|
316258
|
-
{ name: "telegram", state_file:
|
|
316259
|
-
{ name: "expose", state_file:
|
|
316260
|
-
{ name: "nexus", state_file:
|
|
316261
|
-
{ name: "ipfs", state_file:
|
|
316687
|
+
{ name: "dream", state_file: join94(process.cwd(), ".oa", "dreams"), controllable_via: "SSE + slash commands" },
|
|
316688
|
+
{ name: "bless", state_file: join94(process.cwd(), ".oa", "bless-state.json"), controllable_via: "slash commands" },
|
|
316689
|
+
{ name: "call", state_file: join94(process.cwd(), ".oa", "call-state.json"), controllable_via: "slash commands" },
|
|
316690
|
+
{ name: "listen", state_file: join94(process.cwd(), ".oa", "listen-state.json"), controllable_via: "slash commands" },
|
|
316691
|
+
{ name: "telegram", state_file: join94(home, ".open-agents", "telegram-state.json"), controllable_via: "slash commands" },
|
|
316692
|
+
{ name: "expose", state_file: join94(process.cwd(), ".oa", "expose-state.json"), controllable_via: "/expose commands" },
|
|
316693
|
+
{ name: "nexus", state_file: join94(home, ".open-agents", "nexus-peer-cache.json"), controllable_via: "/nexus commands" },
|
|
316694
|
+
{ name: "ipfs", state_file: join94(process.cwd(), ".oa", "ipfs"), controllable_via: "slash commands" }
|
|
316262
316695
|
],
|
|
316263
316696
|
note: "Engine instrumentation lives in the running TUI process. Full status + control requires the daemon↔TUI bridge (PT-07). See parity audit WO-PARITY-04."
|
|
316264
316697
|
});
|
|
@@ -316341,12 +316774,12 @@ async function tryAimsRoute(ctx3) {
|
|
|
316341
316774
|
return false;
|
|
316342
316775
|
}
|
|
316343
316776
|
function aimsDir() {
|
|
316344
|
-
return
|
|
316777
|
+
return join94(homedir33(), ".open-agents", "aims");
|
|
316345
316778
|
}
|
|
316346
316779
|
function readAimsFile(name10, fallback) {
|
|
316347
316780
|
try {
|
|
316348
|
-
const p2 =
|
|
316349
|
-
if (
|
|
316781
|
+
const p2 = join94(aimsDir(), name10);
|
|
316782
|
+
if (existsSync78(p2)) return JSON.parse(readFileSync61(p2, "utf-8"));
|
|
316350
316783
|
} catch {
|
|
316351
316784
|
}
|
|
316352
316785
|
return fallback;
|
|
@@ -316355,7 +316788,7 @@ function writeAimsFile(name10, data) {
|
|
|
316355
316788
|
const dir = aimsDir();
|
|
316356
316789
|
const { mkdirSync: mkdirSync54, writeFileSync: wf, renameSync: rn } = __require("node:fs");
|
|
316357
316790
|
mkdirSync54(dir, { recursive: true });
|
|
316358
|
-
const finalPath =
|
|
316791
|
+
const finalPath = join94(dir, name10);
|
|
316359
316792
|
const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
|
|
316360
316793
|
try {
|
|
316361
316794
|
wf(tmpPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
@@ -316685,12 +317118,12 @@ async function handleAimsSuppliers(ctx3) {
|
|
|
316685
317118
|
}
|
|
316686
317119
|
];
|
|
316687
317120
|
const sponsorPaths = [
|
|
316688
|
-
|
|
317121
|
+
join94(homedir33(), ".open-agents", "sponsor-cache.json")
|
|
316689
317122
|
];
|
|
316690
317123
|
for (const p2 of sponsorPaths) {
|
|
316691
|
-
if (!
|
|
317124
|
+
if (!existsSync78(p2)) continue;
|
|
316692
317125
|
try {
|
|
316693
|
-
const raw = JSON.parse(
|
|
317126
|
+
const raw = JSON.parse(readFileSync61(p2, "utf-8"));
|
|
316694
317127
|
const list = Array.isArray(raw) ? raw : raw?.sponsors ?? [];
|
|
316695
317128
|
for (const s2 of list) {
|
|
316696
317129
|
suppliers.push({
|
|
@@ -317536,6 +317969,11 @@ body {
|
|
|
317536
317969
|
<!-- Popover for process details (absolute positioned above the process row) -->
|
|
317537
317970
|
<div id="proc-popover"></div>
|
|
317538
317971
|
|
|
317972
|
+
<!-- WO-CHAT-AUTOSCROLL — tiny tag that appears far-right just above
|
|
317973
|
+
the tasks-row when the user has scrolled up from live. Click
|
|
317974
|
+
jumps to bottom and re-enables auto-scroll. -->
|
|
317975
|
+
<div id="scroll-bottom-tag" style="display:none;position:absolute;right:14px;top:-22px;background:#17171a;border:1px solid #b2920a;color:#b2920a;padding:2px 8px;border-radius:3px;font-size:0.6rem;cursor:pointer;z-index:5;line-height:1.4" onclick="scrollChatToBottom()">scroll to bottom ↓</div>
|
|
317976
|
+
|
|
317539
317977
|
<!-- WO-TASK-06: Compact aggregated pill label above the dots row.
|
|
317540
317978
|
Hidden when no processes are active; click to toggle dots row visibility. -->
|
|
317541
317979
|
<div id="processes-pill" style="display:none;padding:4px 16px;background:#17171a;border-bottom:1px solid #2a2a30;color:#666;font-size:0.62rem;cursor:pointer" onclick="toggleProcessesRow()">
|
|
@@ -317560,6 +317998,12 @@ body {
|
|
|
317560
317998
|
<textarea id="input-area" placeholder="Type a message..." rows="1"></textarea>
|
|
317561
317999
|
<button id="send-btn" onclick="sendMessage()">send</button>
|
|
317562
318000
|
<button id="stop-btn" onclick="stopChat()" style="display:none;background:#2a2a30;border:1px solid #ff4444;color:#ff4444;padding:10px 16px;border-radius:3px;font-family:inherit;font-size:0.75rem;cursor:pointer;flex-shrink:0">stop</button>
|
|
318001
|
+
<!-- WO-CHAT-CHECKIN — teal accent button that takes the place of stop
|
|
318002
|
+
when the user starts typing during an active run. Click sends a
|
|
318003
|
+
side-channel check-in to the triage sub-agent (route /v1/chat/check-in)
|
|
318004
|
+
which expands the input into a steering instruction the main agent
|
|
318005
|
+
picks up at its next turn. -->
|
|
318006
|
+
<button id="checkin-btn" onclick="sendCheckin()" style="display:none;background:#0a2a2e;border:1px solid #2db4b4;color:#2db4b4;padding:10px 16px;border-radius:3px;font-family:inherit;font-size:0.75rem;cursor:pointer;flex-shrink:0">check in</button>
|
|
317563
318007
|
</div>
|
|
317564
318008
|
</div>
|
|
317565
318009
|
|
|
@@ -317595,13 +318039,72 @@ let chatAbortController = null; // for stop button
|
|
|
317595
318039
|
// state OR the user navigates away.
|
|
317596
318040
|
let chatInFlightPoller = null;
|
|
317597
318041
|
|
|
317598
|
-
//
|
|
318042
|
+
// WO-CHAT-AUTOSCROLL — track whether the user is parked at the bottom.
|
|
318043
|
+
// When they scroll up by more than ~SCROLL_TOLERANCE px, we stop auto-
|
|
318044
|
+
// scrolling on new content and show the "scroll to bottom" tag. When
|
|
318045
|
+
// they scroll back to the bottom (or click the tag), auto-scroll
|
|
318046
|
+
// resumes. autoScrollPinned defaults to true so first-load works.
|
|
318047
|
+
let autoScrollPinned = true;
|
|
318048
|
+
const SCROLL_TOLERANCE = 80;
|
|
318049
|
+
function isAtBottom() {
|
|
318050
|
+
if (!conv) return true;
|
|
318051
|
+
return (conv.scrollHeight - conv.scrollTop - conv.clientHeight) < SCROLL_TOLERANCE;
|
|
318052
|
+
}
|
|
318053
|
+
function maybeAutoScroll() {
|
|
318054
|
+
if (autoScrollPinned && conv) conv.scrollTop = conv.scrollHeight;
|
|
318055
|
+
}
|
|
318056
|
+
function scrollChatToBottom() {
|
|
318057
|
+
if (!conv) return;
|
|
318058
|
+
conv.scrollTop = conv.scrollHeight;
|
|
318059
|
+
autoScrollPinned = true;
|
|
318060
|
+
const tag = document.getElementById('scroll-bottom-tag');
|
|
318061
|
+
if (tag) tag.style.display = 'none';
|
|
318062
|
+
}
|
|
318063
|
+
window.scrollChatToBottom = scrollChatToBottom;
|
|
318064
|
+
if (conv) {
|
|
318065
|
+
conv.addEventListener('scroll', () => {
|
|
318066
|
+
const atBottom = isAtBottom();
|
|
318067
|
+
if (atBottom) {
|
|
318068
|
+
autoScrollPinned = true;
|
|
318069
|
+
const tag = document.getElementById('scroll-bottom-tag');
|
|
318070
|
+
if (tag) tag.style.display = 'none';
|
|
318071
|
+
} else {
|
|
318072
|
+
autoScrollPinned = false;
|
|
318073
|
+
const tag = document.getElementById('scroll-bottom-tag');
|
|
318074
|
+
if (tag) tag.style.display = 'block';
|
|
318075
|
+
}
|
|
318076
|
+
}, { passive: true });
|
|
318077
|
+
}
|
|
318078
|
+
|
|
318079
|
+
// Auto-resize textarea + WO-CHAT-CHECKIN typing detection
|
|
317599
318080
|
input.addEventListener('input', () => {
|
|
317600
318081
|
input.style.height = 'auto';
|
|
317601
318082
|
input.style.height = Math.min(input.scrollHeight, 120) + 'px';
|
|
318083
|
+
// While a run is streaming, swap stop button → check-in button as
|
|
318084
|
+
// soon as the user types anything. When the input is cleared, swap back.
|
|
318085
|
+
if (streaming) {
|
|
318086
|
+
const checkinBtn = document.getElementById('checkin-btn');
|
|
318087
|
+
const stopBtn = document.getElementById('stop-btn');
|
|
318088
|
+
if (input.value.trim().length > 0) {
|
|
318089
|
+
if (checkinBtn) checkinBtn.style.display = 'inline-block';
|
|
318090
|
+
if (stopBtn) stopBtn.style.display = 'none';
|
|
318091
|
+
} else {
|
|
318092
|
+
if (checkinBtn) checkinBtn.style.display = 'none';
|
|
318093
|
+
if (stopBtn) stopBtn.style.display = 'inline-block';
|
|
318094
|
+
}
|
|
318095
|
+
}
|
|
317602
318096
|
});
|
|
317603
318097
|
input.addEventListener('keydown', (e) => {
|
|
317604
|
-
if (e.key === 'Enter' && !e.shiftKey) {
|
|
318098
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
318099
|
+
e.preventDefault();
|
|
318100
|
+
// While a run is streaming, Enter dispatches a check-in instead of
|
|
318101
|
+
// a new top-level send (which would block on streaming === true).
|
|
318102
|
+
if (streaming && input.value.trim().length > 0) {
|
|
318103
|
+
sendCheckin();
|
|
318104
|
+
} else {
|
|
318105
|
+
sendMessage();
|
|
318106
|
+
}
|
|
318107
|
+
}
|
|
317605
318108
|
});
|
|
317606
318109
|
|
|
317607
318110
|
function headers() {
|
|
@@ -317705,7 +318208,7 @@ function addMessage(role, content) {
|
|
|
317705
318208
|
div.appendChild(actions);
|
|
317706
318209
|
}
|
|
317707
318210
|
conv.appendChild(div);
|
|
317708
|
-
|
|
318211
|
+
maybeAutoScroll();
|
|
317709
318212
|
return div;
|
|
317710
318213
|
}
|
|
317711
318214
|
|
|
@@ -317954,31 +318457,155 @@ function renderToolResultEvent(parent, chunkLike) {
|
|
|
317954
318457
|
return resultEl;
|
|
317955
318458
|
}
|
|
317956
318459
|
|
|
317957
|
-
|
|
318460
|
+
// WO-CHAT-CHECKIN — render a teal-accent user check-in entry. The
|
|
318461
|
+
// content is the raw message the user typed during an active run,
|
|
318462
|
+
// before the triage sub-agent expanded it. Sits inline with the
|
|
318463
|
+
// active assistant turn's tool dropdowns.
|
|
318464
|
+
function renderCheckinEvent(parent, content) {
|
|
318465
|
+
const el = document.createElement('div');
|
|
318466
|
+
el.style.cssText = 'background:#0a2a2e;border-left:3px solid #2db4b4;color:#7fdada;padding:6px 10px 6px 14px;margin:3px 0;font-size:0.7rem;font-family:inherit';
|
|
318467
|
+
const label = document.createElement('div');
|
|
318468
|
+
label.style.cssText = 'color:#2db4b4;font-size:0.6rem;text-transform:uppercase;letter-spacing:0.08em;margin-bottom:2px';
|
|
318469
|
+
label.textContent = '\\u25B8 user check-in';
|
|
318470
|
+
el.appendChild(label);
|
|
318471
|
+
const body = document.createElement('div');
|
|
318472
|
+
body.style.cssText = 'color:#b0d4d4;white-space:pre-wrap;word-break:break-word';
|
|
318473
|
+
body.textContent = String(content || '');
|
|
318474
|
+
el.appendChild(body);
|
|
318475
|
+
parent.appendChild(el);
|
|
318476
|
+
return el;
|
|
318477
|
+
}
|
|
318478
|
+
|
|
318479
|
+
// WO-CHAT-CHECKIN — render the triage sub-agent's structured response
|
|
318480
|
+
// (acknowledgment + steering instruction). Uses the same teal accent
|
|
318481
|
+
// so the user can visually trace each side-channel exchange in the
|
|
318482
|
+
// stack of dropdowns.
|
|
318483
|
+
function renderTriageResponseEvent(parent, ack, steering) {
|
|
318484
|
+
const el = document.createElement('div');
|
|
318485
|
+
el.style.cssText = 'background:#0a2628;border-left:3px solid #2db4b4;color:#7fdada;padding:6px 10px 6px 14px;margin:3px 0;font-size:0.7rem';
|
|
318486
|
+
const label = document.createElement('div');
|
|
318487
|
+
label.style.cssText = 'color:#2db4b4;font-size:0.6rem;text-transform:uppercase;letter-spacing:0.08em;margin-bottom:2px';
|
|
318488
|
+
label.textContent = '\\u25B8 triage \\u2192 main agent';
|
|
318489
|
+
el.appendChild(label);
|
|
318490
|
+
if (ack) {
|
|
318491
|
+
const ackEl = document.createElement('div');
|
|
318492
|
+
ackEl.style.cssText = 'color:#9fe4e4;font-style:italic;margin-bottom:3px';
|
|
318493
|
+
ackEl.textContent = ack;
|
|
318494
|
+
el.appendChild(ackEl);
|
|
318495
|
+
}
|
|
318496
|
+
if (steering) {
|
|
318497
|
+
const steerWrap = document.createElement('div');
|
|
318498
|
+
steerWrap.style.cssText = 'color:#7fdada;font-size:0.65rem';
|
|
318499
|
+
appendExpandableContent(steerWrap, steering, { truncateAt: 200, baseStyle: 'color:#7fdada;' });
|
|
318500
|
+
el.appendChild(steerWrap);
|
|
318501
|
+
}
|
|
318502
|
+
parent.appendChild(el);
|
|
318503
|
+
return el;
|
|
318504
|
+
}
|
|
318505
|
+
|
|
318506
|
+
// WO-CHAT-CHECKIN — POST a side-channel check-in to /v1/chat/check-in
|
|
318507
|
+
// while a chat run is streaming. Renders the raw input + the triage
|
|
318508
|
+
// response as teal entries inline with the live assistant turn, and
|
|
318509
|
+
// the triage's steering string is forwarded into the agent's
|
|
318510
|
+
// pendingUserMessages queue via the daemon-side mailbox file.
|
|
318511
|
+
async function sendCheckin() {
|
|
317958
318512
|
const text = input.value.trim();
|
|
317959
|
-
if (!text || streaming) return;
|
|
318513
|
+
if (!text || !streaming || !chatSessionId) return;
|
|
317960
318514
|
input.value = '';
|
|
317961
318515
|
input.style.height = 'auto';
|
|
317962
|
-
|
|
317963
|
-
|
|
317964
|
-
|
|
317965
|
-
|
|
317966
|
-
|
|
317967
|
-
//
|
|
317968
|
-
|
|
317969
|
-
|
|
317970
|
-
|
|
317971
|
-
|
|
317972
|
-
|
|
317973
|
-
|
|
317974
|
-
|
|
317975
|
-
|
|
317976
|
-
|
|
317977
|
-
|
|
317978
|
-
|
|
317979
|
-
|
|
317980
|
-
|
|
318516
|
+
// Swap back to stop button after submission
|
|
318517
|
+
const checkinBtn = document.getElementById('checkin-btn');
|
|
318518
|
+
const stopBtn = document.getElementById('stop-btn');
|
|
318519
|
+
if (checkinBtn) checkinBtn.style.display = 'none';
|
|
318520
|
+
if (stopBtn) stopBtn.style.display = 'inline-block';
|
|
318521
|
+
// Find the active assistant bubble — the last assistant message
|
|
318522
|
+
// currently in the conversation. We attach the teal entries inline
|
|
318523
|
+
// with its tool dropdowns so the side-channel exchange interleaves
|
|
318524
|
+
// with the main agent's actions.
|
|
318525
|
+
const conv = document.getElementById('conversation');
|
|
318526
|
+
let toolsContainer = null;
|
|
318527
|
+
if (conv) {
|
|
318528
|
+
const lastAssistant = conv.querySelector('.msg.assistant:last-of-type');
|
|
318529
|
+
if (lastAssistant) {
|
|
318530
|
+
// Reuse the existing tools container if any, else create one
|
|
318531
|
+
toolsContainer = lastAssistant.querySelector('.tools-container');
|
|
318532
|
+
if (!toolsContainer) {
|
|
318533
|
+
toolsContainer = document.createElement('div');
|
|
318534
|
+
toolsContainer.className = 'tools-container';
|
|
318535
|
+
toolsContainer.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
318536
|
+
lastAssistant.appendChild(toolsContainer);
|
|
318537
|
+
}
|
|
318538
|
+
} else {
|
|
318539
|
+
// No active assistant bubble — create a fresh one
|
|
318540
|
+
const md = addMessage('assistant', '');
|
|
318541
|
+
toolsContainer = document.createElement('div');
|
|
318542
|
+
toolsContainer.className = 'tools-container';
|
|
318543
|
+
toolsContainer.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
318544
|
+
md.appendChild(toolsContainer);
|
|
318545
|
+
}
|
|
318546
|
+
}
|
|
318547
|
+
if (toolsContainer) renderCheckinEvent(toolsContainer, text);
|
|
318548
|
+
conv && maybeAutoScroll();
|
|
318549
|
+
// Fire the request — non-blocking from the user's perspective
|
|
318550
|
+
try {
|
|
318551
|
+
const r = await fetch('/v1/chat/check-in', {
|
|
318552
|
+
method: 'POST',
|
|
318553
|
+
headers: headers(),
|
|
318554
|
+
body: JSON.stringify({ session_id: chatSessionId, message: text }),
|
|
318555
|
+
});
|
|
318556
|
+
if (!r.ok) {
|
|
318557
|
+
if (toolsContainer) {
|
|
318558
|
+
const errEl = document.createElement('div');
|
|
318559
|
+
errEl.style.cssText = 'color:#b25f5f;font-size:0.6rem;padding:2px 14px';
|
|
318560
|
+
errEl.textContent = 'Check-in failed: HTTP ' + r.status;
|
|
318561
|
+
toolsContainer.appendChild(errEl);
|
|
318562
|
+
}
|
|
318563
|
+
return;
|
|
318564
|
+
}
|
|
318565
|
+
const data = await r.json();
|
|
318566
|
+
if (toolsContainer) {
|
|
318567
|
+
renderTriageResponseEvent(toolsContainer, data.acknowledgment || '', data.steering || '');
|
|
318568
|
+
conv && maybeAutoScroll();
|
|
318569
|
+
}
|
|
318570
|
+
} catch (err) {
|
|
318571
|
+
if (toolsContainer) {
|
|
318572
|
+
const errEl = document.createElement('div');
|
|
318573
|
+
errEl.style.cssText = 'color:#b25f5f;font-size:0.6rem;padding:2px 14px';
|
|
318574
|
+
errEl.textContent = 'Check-in network error: ' + (err && err.message || String(err));
|
|
318575
|
+
toolsContainer.appendChild(errEl);
|
|
318576
|
+
}
|
|
318577
|
+
}
|
|
318578
|
+
}
|
|
318579
|
+
window.sendCheckin = sendCheckin;
|
|
318580
|
+
|
|
318581
|
+
async function sendMessage() {
|
|
318582
|
+
const text = input.value.trim();
|
|
318583
|
+
if (!text || streaming) return;
|
|
318584
|
+
input.value = '';
|
|
318585
|
+
input.style.height = 'auto';
|
|
318586
|
+
|
|
318587
|
+
// Add user message
|
|
318588
|
+
messages.push({ role: 'user', content: text });
|
|
318589
|
+
addMessage('user', text);
|
|
318590
|
+
|
|
318591
|
+
// System prompt
|
|
318592
|
+
const sysPrompt = document.getElementById('system-prompt').value.trim();
|
|
318593
|
+
|
|
318594
|
+
streaming = true;
|
|
318595
|
+
chatAbortController = new AbortController();
|
|
318596
|
+
document.getElementById('send-btn').style.display = 'none';
|
|
318597
|
+
document.getElementById('stop-btn').style.display = 'inline-block';
|
|
318598
|
+
|
|
318599
|
+
const msgDiv = addMessage('assistant', '');
|
|
318600
|
+
let fullContent = '';
|
|
318601
|
+
let chatTools = []; // tool calls collected during streaming
|
|
318602
|
+
let metaInfo = null; // completion metadata
|
|
318603
|
+
|
|
318604
|
+
// Tool calls container — shown live during streaming. Class name lets
|
|
318605
|
+
// the WO-CHAT-CHECKIN sendCheckin() helper find it and attach teal
|
|
318606
|
+
// entries inline with the active assistant's tool dropdowns.
|
|
317981
318607
|
const toolsContainer = document.createElement('div');
|
|
318608
|
+
toolsContainer.className = 'tools-container';
|
|
317982
318609
|
toolsContainer.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
317983
318610
|
msgDiv.appendChild(toolsContainer);
|
|
317984
318611
|
|
|
@@ -318066,7 +318693,7 @@ async function sendMessage() {
|
|
|
318066
318693
|
if (fullContent && !fullContent.endsWith('\\n')) fullContent += '\\n\\n';
|
|
318067
318694
|
fullContent += summaryText;
|
|
318068
318695
|
contentDiv.innerHTML = renderMarkdown(fullContent);
|
|
318069
|
-
|
|
318696
|
+
maybeAutoScroll();
|
|
318070
318697
|
}
|
|
318071
318698
|
// fall through so the dropdown still renders for inspection
|
|
318072
318699
|
}
|
|
@@ -318178,7 +318805,7 @@ async function sendMessage() {
|
|
|
318178
318805
|
details.appendChild(argsDiv);
|
|
318179
318806
|
}
|
|
318180
318807
|
toolsContainer.appendChild(details);
|
|
318181
|
-
|
|
318808
|
+
maybeAutoScroll();
|
|
318182
318809
|
continue;
|
|
318183
318810
|
}
|
|
318184
318811
|
|
|
@@ -318204,7 +318831,7 @@ async function sendMessage() {
|
|
|
318204
318831
|
if (delta) {
|
|
318205
318832
|
fullContent += delta;
|
|
318206
318833
|
contentDiv.innerHTML = renderMarkdown(fullContent);
|
|
318207
|
-
|
|
318834
|
+
maybeAutoScroll();
|
|
318208
318835
|
}
|
|
318209
318836
|
} catch {}
|
|
318210
318837
|
}
|
|
@@ -318267,7 +318894,7 @@ async function sendMessage() {
|
|
|
318267
318894
|
chatAbortController = null;
|
|
318268
318895
|
document.getElementById('send-btn').style.display = 'inline-block';
|
|
318269
318896
|
document.getElementById('stop-btn').style.display = 'none';
|
|
318270
|
-
|
|
318897
|
+
maybeAutoScroll();
|
|
318271
318898
|
}
|
|
318272
318899
|
|
|
318273
318900
|
function toggleSystemPrompt() {
|
|
@@ -319962,37 +320589,36 @@ async function restoreChatSession() {
|
|
|
319962
320589
|
// assistant bubble's tools container so consecutive tool events
|
|
319963
320590
|
// attach to the same bubble (the live SSE handler does the same).
|
|
319964
320591
|
let currentAssistantTools = null;
|
|
320592
|
+
const ensureTools = () => {
|
|
320593
|
+
if (!currentAssistantTools) {
|
|
320594
|
+
const md = addMessage('assistant', '');
|
|
320595
|
+
currentAssistantTools = document.createElement('div');
|
|
320596
|
+
currentAssistantTools.className = 'tools-container';
|
|
320597
|
+
currentAssistantTools.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
320598
|
+
md.appendChild(currentAssistantTools);
|
|
320599
|
+
}
|
|
320600
|
+
return currentAssistantTools;
|
|
320601
|
+
};
|
|
319965
320602
|
for (const m of allMessages) {
|
|
319966
320603
|
if (m.role === 'user') {
|
|
319967
320604
|
addMessage('user', m.content);
|
|
319968
320605
|
currentAssistantTools = null;
|
|
319969
320606
|
} else if (m.role === 'assistant') {
|
|
319970
320607
|
const msgDiv = addMessage('assistant', m.content);
|
|
319971
|
-
// Stage a tools container for any tool events that come AFTER
|
|
319972
|
-
// this assistant turn (small models sometimes emit text first
|
|
319973
|
-
// then tool calls — we attach those tools to the most recent
|
|
319974
|
-
// assistant bubble for visual coherence).
|
|
319975
320608
|
currentAssistantTools = document.createElement('div');
|
|
320609
|
+
currentAssistantTools.className = 'tools-container';
|
|
319976
320610
|
currentAssistantTools.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
319977
320611
|
msgDiv.appendChild(currentAssistantTools);
|
|
319978
320612
|
} else if (m.role === 'tool_call') {
|
|
319979
|
-
|
|
319980
|
-
// create one with empty content so the dropdown has a parent.
|
|
319981
|
-
if (!currentAssistantTools) {
|
|
319982
|
-
const msgDiv = addMessage('assistant', '');
|
|
319983
|
-
currentAssistantTools = document.createElement('div');
|
|
319984
|
-
currentAssistantTools.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
319985
|
-
msgDiv.appendChild(currentAssistantTools);
|
|
319986
|
-
}
|
|
319987
|
-
renderToolCallEvent(currentAssistantTools, { tool: m.tool, args: m.args });
|
|
320613
|
+
renderToolCallEvent(ensureTools(), { tool: m.tool, args: m.args });
|
|
319988
320614
|
} else if (m.role === 'tool_result') {
|
|
319989
|
-
|
|
319990
|
-
|
|
319991
|
-
|
|
319992
|
-
|
|
319993
|
-
|
|
319994
|
-
|
|
319995
|
-
|
|
320615
|
+
renderToolResultEvent(ensureTools(), { output: m.output, success: m.success });
|
|
320616
|
+
} else if (m.role === 'user_checkin') {
|
|
320617
|
+
// WO-CHAT-CHECKIN — replay the teal user check-in entry
|
|
320618
|
+
renderCheckinEvent(ensureTools(), m.content);
|
|
320619
|
+
} else if (m.role === 'triage_response') {
|
|
320620
|
+
// WO-CHAT-CHECKIN — replay the triage sub-agent's structured response
|
|
320621
|
+
renderTriageResponseEvent(ensureTools(), m.acknowledgment || m.content || '', m.steering || '');
|
|
319996
320622
|
}
|
|
319997
320623
|
}
|
|
319998
320624
|
}
|
|
@@ -320489,485 +321115,139 @@ function getOpenApiSpec() {
|
|
|
320489
321115
|
responses: { 200: { description: "Search results" } }
|
|
320490
321116
|
}
|
|
320491
321117
|
},
|
|
320492
|
-
"/v1/memory/write": { post: { summary: "Write a memory entry (PT-04)", tags: ["Memory"], responses: { 201: { description: "Written" }, 403: { description: "Requires run/admin scope" } } } },
|
|
320493
|
-
"/v1/memory/episodes": { get: { summary: "List episodes", tags: ["Memory"], responses: { 200: { description: "Paginated episodes" } } } },
|
|
320494
|
-
"/v1/memory/failures": { get: { summary: "List failures", tags: ["Memory"], responses: { 200: { description: "Paginated failures" } } } },
|
|
320495
|
-
// ───── PT-06: Events ─────
|
|
320496
|
-
"/v1/events": {
|
|
320497
|
-
get: {
|
|
320498
|
-
summary: "Subscribe to the event bus (PT-06)",
|
|
320499
|
-
description: "Server-sent event stream. Event types: config.changed, run.started, run.completed, run.aborted, run.failed, tool.called, memory.written, memory.searched, skill.invoked, mcp.called, engine.state_changed, auth.failed, rate_limit.hit, incident.raised, incident.resolved, session.created, session.ended, aims.policy_changed, aims.decision_recorded. Filter with ?type=foo.bar or ?type=foo.*",
|
|
320500
|
-
tags: ["Events"],
|
|
320501
|
-
parameters: [{ name: "type", in: "query", required: false, schema: { type: "string" }, description: "Filter by event type (supports wildcard suffix .*)" }],
|
|
320502
|
-
responses: { 200: { description: "text/event-stream with event frames" } }
|
|
320503
|
-
}
|
|
320504
|
-
},
|
|
320505
|
-
// ───── P1: Sessions ─────
|
|
320506
|
-
"/v1/sessions": { get: { summary: "List OA task sessions", tags: ["Sessions"], responses: { 200: { description: "Paginated sessions" } } } },
|
|
320507
|
-
"/v1/sessions/{id}": { get: { summary: "Get session history", tags: ["Sessions"], parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }], responses: { 200: { description: "Session history" }, 404: { description: "Not found" } } } },
|
|
320508
|
-
// ───── P1: Context ─────
|
|
320509
|
-
"/v1/context": { get: { summary: "Show current session context", tags: ["Context"], responses: { 200: { description: "Context snapshot" } } } },
|
|
320510
|
-
"/v1/context/save": { post: { summary: "Save a session context entry", tags: ["Context"], responses: { 201: { description: "Saved" } } } },
|
|
320511
|
-
"/v1/context/restore": { get: { summary: "Build a restore prompt from saved context", tags: ["Context"], responses: { 200: { description: "Restore prompt" } } } },
|
|
320512
|
-
"/v1/context/compact": { post: { summary: "Request context compaction (event-driven)", tags: ["Context"], responses: { 202: { description: "Compaction requested" } } } },
|
|
320513
|
-
// ───── P1: Nexus / Sponsors ─────
|
|
320514
|
-
"/v1/nexus/status": { get: { summary: "Nexus peer state", tags: ["Nexus"], responses: { 200: { description: "Peer cache snapshot" } } } },
|
|
320515
|
-
"/v1/sponsors": { get: { summary: "Local sponsor directory cache", tags: ["Nexus"], responses: { 200: { description: "Paginated sponsors" } } } },
|
|
320516
|
-
// ───── P1: Cost / Evaluate / Index ─────
|
|
320517
|
-
"/v1/cost": { get: { summary: "Cost tracker (pricing model)", tags: ["Metering"], responses: { 200: { description: "Provider pricing" } } } },
|
|
320518
|
-
"/v1/evaluate": { post: { summary: "Evaluate a run by ID", tags: ["Agentic"], responses: { 200: { description: "Evaluation metrics" }, 404: { description: "Run not found" } } } },
|
|
320519
|
-
"/v1/index": { post: { summary: "Trigger repository indexing", tags: ["Agentic"], responses: { 202: { description: "Indexing started" } } } },
|
|
320520
|
-
// ───── P2: Tools / Hooks / Agents / Engines ─────
|
|
320521
|
-
"/v1/tools": { get: { summary: "List agentic tool registry", tags: ["Tools"], responses: { 200: { description: "Paginated tools" } } } },
|
|
320522
|
-
"/v1/hooks": { get: { summary: "List hook types and counts", tags: ["Tools"], responses: { 200: { description: "Hook registry" } } } },
|
|
320523
|
-
"/v1/agents": { get: { summary: "List agent types", tags: ["Tools"], responses: { 200: { description: "Agent type registry" } } } },
|
|
320524
|
-
"/v1/engines": { get: { summary: "List long-running engines", tags: ["Engines"], responses: { 200: { description: "Engine status + state files" } } } },
|
|
320525
|
-
// ───── P2: Voice / Vision (deferred) ─────
|
|
320526
|
-
"/v1/voice/tts": { post: { summary: "Text-to-speech (deferred to PT-07)", tags: ["Voice"], responses: { 501: { description: "Not yet daemon-resident" } } } },
|
|
320527
|
-
"/v1/voice/asr": { post: { summary: "Automatic speech recognition (deferred)", tags: ["Voice"], responses: { 501: { description: "Not yet daemon-resident" } } } },
|
|
320528
|
-
"/v1/vision/describe": { post: { summary: "Vision describe (deferred to PT-07)", tags: ["Vision"], responses: { 501: { description: "Not yet daemon-resident" } } } },
|
|
320529
|
-
// ───── ISO/IEC 42001:2023 AIMS ─────
|
|
320530
|
-
"/v1/aims": { get: { summary: "AIMS root — control map and endpoint index", tags: ["AIMS"], responses: { 200: { description: "AIMS overview" } } } },
|
|
320531
|
-
"/v1/aims/policies": {
|
|
320532
|
-
get: { summary: "AI policies (ISO 42001 A.2)", tags: ["AIMS"], responses: { 200: { description: "Policy register" } } },
|
|
320533
|
-
put: { summary: "Replace the policy register (ISO 42001 A.2)", tags: ["AIMS"], responses: { 200: { description: "Updated" }, 403: { description: "Admin scope required" } } }
|
|
320534
|
-
},
|
|
320535
|
-
"/v1/aims/roles": { get: { summary: "Roles and responsibilities (A.3)", tags: ["AIMS"], responses: { 200: { description: "Role register" } } } },
|
|
320536
|
-
"/v1/aims/resources": { get: { summary: "Resources inventory (A.4)", tags: ["AIMS"], responses: { 200: { description: "Compute + backend inventory" } } } },
|
|
320537
|
-
"/v1/aims/impact-assessments": {
|
|
320538
|
-
get: { summary: "Impact assessment register (A.5)", tags: ["AIMS"], responses: { 200: { description: "Paginated impact assessments" } } },
|
|
320539
|
-
post: { summary: "File an impact assessment (A.5)", tags: ["AIMS"], responses: { 201: { description: "Recorded" }, 403: { description: "Admin scope required" } } }
|
|
320540
|
-
},
|
|
320541
|
-
"/v1/aims/lifecycle": { get: { summary: "AI system lifecycle state (A.6)", tags: ["AIMS"], responses: { 200: { description: "Lifecycle snapshot" } } } },
|
|
320542
|
-
"/v1/aims/data-quality": { get: { summary: "Data quality controls (A.7.2)", tags: ["AIMS"], responses: { 200: { description: "Data quality register" } } } },
|
|
320543
|
-
"/v1/aims/transparency": { get: { summary: "Model cards and transparency info (A.8)", tags: ["AIMS"], responses: { 200: { description: "Model cards" } } } },
|
|
320544
|
-
"/v1/aims/usage": { get: { summary: "AI system usage (A.9 alias of /v1/usage)", tags: ["AIMS"], responses: { 200: { description: "Usage snapshot" } } } },
|
|
320545
|
-
"/v1/aims/suppliers": { get: { summary: "Third-party supplier inventory (A.10)", tags: ["AIMS"], responses: { 200: { description: "Suppliers" } } } },
|
|
320546
|
-
"/v1/aims/incidents": {
|
|
320547
|
-
get: { summary: "Incident register (A.6.2.8)", tags: ["AIMS"], responses: { 200: { description: "Paginated incidents" } } },
|
|
320548
|
-
post: { summary: "Raise an incident (A.6.2.8)", tags: ["AIMS"], responses: { 201: { description: "Recorded" } } }
|
|
320549
|
-
},
|
|
320550
|
-
"/v1/aims/oversight": { get: { summary: "Human oversight gates (A.6.2.7)", tags: ["AIMS"], responses: { 200: { description: "Oversight configuration" } } } },
|
|
320551
|
-
"/v1/aims/decisions": { get: { summary: "Consequential decision log (A.9)", tags: ["AIMS"], responses: { 200: { description: "Paginated decisions" } } } },
|
|
320552
|
-
"/v1/aims/config-history": { get: { summary: "Configuration change history (A.6.2.8)", tags: ["AIMS"], responses: { 200: { description: "Config change records from audit log" } } } }
|
|
320553
|
-
}
|
|
320554
|
-
};
|
|
320555
|
-
}
|
|
320556
|
-
function getSwaggerUI() {
|
|
320557
|
-
return `<!DOCTYPE html><html><head><title>OA API Docs</title>
|
|
320558
|
-
<meta charset="utf-8">
|
|
320559
|
-
<style>
|
|
320560
|
-
body{font-family:'SF Mono',ui-monospace,monospace;background:#1a1a1e;color:#b0b0b0;margin:0;padding:20px;font-size:13px}
|
|
320561
|
-
h1{color:#b2920a;font-size:1.3rem;margin-bottom:4px}
|
|
320562
|
-
h2{color:#b2920a;font-size:0.85rem;margin-top:20px;text-transform:uppercase;letter-spacing:1px}
|
|
320563
|
-
.subtitle{color:#888;font-size:0.85rem;margin-bottom:20px}
|
|
320564
|
-
.endpoint{background:#1e1e22;border:1px solid #2a2a30;border-radius:3px;padding:8px 12px;margin:4px 0;display:flex;align-items:center;gap:12px}
|
|
320565
|
-
.method{font-weight:bold;padding:2px 8px;border-radius:2px;font-size:0.7rem;min-width:50px;text-align:center}
|
|
320566
|
-
.get{color:#4ec94e;border:1px solid #4ec94e}.post{color:#b2920a;border:1px solid #b2920a}.put{color:#4e94c9;border:1px solid #4e94c9}
|
|
320567
|
-
.patch{color:#c9944e;border:1px solid #c9944e}.delete{color:#c94e4e;border:1px solid #c94e4e}
|
|
320568
|
-
.path{color:#b0b0b0;font-weight:600}.summary{color:#888;font-size:0.75rem;flex:1;text-align:right}
|
|
320569
|
-
.tag{margin-top:18px;border-bottom:1px solid #2a2a30;padding-bottom:4px}
|
|
320570
|
-
a{color:#b2920a;text-decoration:none}a:hover{text-decoration:underline}
|
|
320571
|
-
.iso-note{background:#222;border-left:3px solid #b2920a;padding:10px 14px;margin:12px 0;color:#ccc;font-size:0.8rem}
|
|
320572
|
-
</style></head><body>
|
|
320573
|
-
<h1>Open Agents REST API</h1>
|
|
320574
|
-
<p class="subtitle">Enterprise-grade REST surface. Errors follow RFC 7807. Responses carry X-API-Version. <a href="/openapi.json">Full OpenAPI 3.0 spec</a></p>
|
|
320575
|
-
<div class="iso-note">
|
|
320576
|
-
<strong>ISO/IEC 42001:2023 AIMS layer:</strong> The /v1/aims/* endpoints expose the
|
|
320577
|
-
AI Management System controls an auditor expects — policies, roles, resources, impact
|
|
320578
|
-
assessments, lifecycle, data quality, transparency, usage, suppliers, incidents, oversight,
|
|
320579
|
-
decisions, and config history. Events published to /v1/events are tagged with the matching
|
|
320580
|
-
Annex A control reference (aims:control field).
|
|
320581
|
-
</div>
|
|
320582
|
-
<div id="docs"></div>
|
|
320583
|
-
<script>
|
|
320584
|
-
fetch('/openapi.json').then(r=>r.json()).then(spec=>{
|
|
320585
|
-
const docs=document.getElementById('docs');
|
|
320586
|
-
const tagOrder=(spec.tags||[]).map(t=>t.name);
|
|
320587
|
-
const byTag={};
|
|
320588
|
-
for(const[path,methods]of Object.entries(spec.paths)){
|
|
320589
|
-
for(const[method,op]of Object.entries(methods)){
|
|
320590
|
-
const tag=(op.tags||['Other'])[0];
|
|
320591
|
-
if(!byTag[tag])byTag[tag]=[];
|
|
320592
|
-
byTag[tag].push({method,path,summary:op.summary||''});
|
|
320593
|
-
}
|
|
320594
|
-
}
|
|
320595
|
-
const orderedTags=[...tagOrder.filter(t=>byTag[t]),...Object.keys(byTag).filter(t=>!tagOrder.includes(t))];
|
|
320596
|
-
for(const tag of orderedTags){
|
|
320597
|
-
const endpoints=byTag[tag];
|
|
320598
|
-
const tagObj=(spec.tags||[]).find(t=>t.name===tag);
|
|
320599
|
-
let html='<div class="tag"><h2>'+tag+'</h2>';
|
|
320600
|
-
if(tagObj&&tagObj.description)html+='<div style="color:#777;font-size:0.75rem;margin-bottom:8px">'+tagObj.description+'</div>';
|
|
320601
|
-
html+='</div>';
|
|
320602
|
-
docs.innerHTML+=html;
|
|
320603
|
-
for(const ep of endpoints){
|
|
320604
|
-
docs.innerHTML+='<div class="endpoint"><span class="method '+ep.method+'">'+ep.method.toUpperCase()+'</span><span class="path">'+ep.path+'</span><span class="summary">'+ep.summary+'</span></div>';
|
|
320605
|
-
}
|
|
320606
|
-
}
|
|
320607
|
-
});
|
|
320608
|
-
</script></body></html>`;
|
|
320609
|
-
}
|
|
320610
|
-
var init_openapi = __esm({
|
|
320611
|
-
"packages/cli/src/api/openapi.ts"() {
|
|
320612
|
-
"use strict";
|
|
320613
|
-
init_http2();
|
|
320614
|
-
}
|
|
320615
|
-
});
|
|
320616
|
-
|
|
320617
|
-
// packages/cli/src/api/auth-oidc.ts
|
|
320618
|
-
var OIDC_ISSUER, OIDC_AUDIENCE, OIDC_SCOPE_CLAIM;
|
|
320619
|
-
var init_auth_oidc = __esm({
|
|
320620
|
-
"packages/cli/src/api/auth-oidc.ts"() {
|
|
320621
|
-
"use strict";
|
|
320622
|
-
OIDC_ISSUER = process.env["OA_OIDC_ISSUER"] || "";
|
|
320623
|
-
OIDC_AUDIENCE = process.env["OA_OIDC_AUDIENCE"] || "";
|
|
320624
|
-
OIDC_SCOPE_CLAIM = process.env["OA_OIDC_SCOPE_CLAIM"] || "scope";
|
|
320625
|
-
}
|
|
320626
|
-
});
|
|
320627
|
-
|
|
320628
|
-
// packages/cli/src/api/chat-session.ts
|
|
320629
|
-
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
320630
|
-
import {
|
|
320631
|
-
existsSync as existsSync78,
|
|
320632
|
-
readFileSync as readFileSync61,
|
|
320633
|
-
readdirSync as readdirSync26,
|
|
320634
|
-
writeFileSync as writeFileSync39,
|
|
320635
|
-
renameSync as renameSync4,
|
|
320636
|
-
mkdirSync as mkdirSync46,
|
|
320637
|
-
unlinkSync as unlinkSync20
|
|
320638
|
-
} from "node:fs";
|
|
320639
|
-
import { join as join94 } from "node:path";
|
|
320640
|
-
import { homedir as homedir33 } from "node:os";
|
|
320641
|
-
function sessionsDir() {
|
|
320642
|
-
return join94(homedir33(), ".open-agents", "chat-sessions");
|
|
320643
|
-
}
|
|
320644
|
-
function sessionPath(id) {
|
|
320645
|
-
const safe = id.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
320646
|
-
return join94(sessionsDir(), `${safe}.json`);
|
|
320647
|
-
}
|
|
320648
|
-
function inFlightPath(id) {
|
|
320649
|
-
const safe = id.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
320650
|
-
return join94(sessionsDir(), `${safe}.inflight.json`);
|
|
320651
|
-
}
|
|
320652
|
-
function persistSession(s2) {
|
|
320653
|
-
try {
|
|
320654
|
-
mkdirSync46(sessionsDir(), { recursive: true });
|
|
320655
|
-
const final2 = sessionPath(s2.id);
|
|
320656
|
-
const tmp = `${final2}.tmp.${process.pid}.${Date.now()}`;
|
|
320657
|
-
writeFileSync39(tmp, JSON.stringify(s2, null, 2), "utf-8");
|
|
320658
|
-
renameSync4(tmp, final2);
|
|
320659
|
-
} catch {
|
|
320660
|
-
}
|
|
320661
|
-
}
|
|
320662
|
-
function persistInFlight(j) {
|
|
320663
|
-
try {
|
|
320664
|
-
mkdirSync46(sessionsDir(), { recursive: true });
|
|
320665
|
-
const final2 = inFlightPath(j.sessionId);
|
|
320666
|
-
const tmp = `${final2}.tmp.${process.pid}.${Date.now()}`;
|
|
320667
|
-
writeFileSync39(tmp, JSON.stringify(j, null, 2), "utf-8");
|
|
320668
|
-
renameSync4(tmp, final2);
|
|
320669
|
-
} catch {
|
|
320670
|
-
}
|
|
320671
|
-
}
|
|
320672
|
-
function deleteInFlightFile(id) {
|
|
320673
|
-
try {
|
|
320674
|
-
const p2 = inFlightPath(id);
|
|
320675
|
-
if (existsSync78(p2)) unlinkSync20(p2);
|
|
320676
|
-
} catch {
|
|
320677
|
-
}
|
|
320678
|
-
}
|
|
320679
|
-
function loadPersistedSessions() {
|
|
320680
|
-
const report = { restored: 0, staleInFlight: 0 };
|
|
320681
|
-
try {
|
|
320682
|
-
const dir = sessionsDir();
|
|
320683
|
-
if (!existsSync78(dir)) return report;
|
|
320684
|
-
const cutoff = Date.now() - SESSION_TTL_MS;
|
|
320685
|
-
for (const f2 of readdirSync26(dir)) {
|
|
320686
|
-
if (!f2.endsWith(".json") || f2.includes(".tmp.")) continue;
|
|
320687
|
-
const fp = join94(dir, f2);
|
|
320688
|
-
try {
|
|
320689
|
-
const parsed = JSON.parse(readFileSync61(fp, "utf-8"));
|
|
320690
|
-
if (f2.endsWith(".inflight.json")) {
|
|
320691
|
-
if (parsed && parsed.pid && parsed.status === "running") {
|
|
320692
|
-
try {
|
|
320693
|
-
process.kill(parsed.pid, 0);
|
|
320694
|
-
} catch {
|
|
320695
|
-
parsed.status = "failed";
|
|
320696
|
-
parsed.error = "Daemon restart while subprocess was running";
|
|
320697
|
-
parsed.completedAt = Date.now();
|
|
320698
|
-
try {
|
|
320699
|
-
writeFileSync39(fp, JSON.stringify(parsed, null, 2), "utf-8");
|
|
320700
|
-
} catch {
|
|
320701
|
-
}
|
|
320702
|
-
report.staleInFlight++;
|
|
320703
|
-
}
|
|
320704
|
-
}
|
|
320705
|
-
continue;
|
|
320706
|
-
}
|
|
320707
|
-
if (parsed && typeof parsed === "object" && parsed.id) {
|
|
320708
|
-
if ((parsed.lastActivity ?? 0) >= cutoff) {
|
|
320709
|
-
sessions.set(parsed.id, parsed);
|
|
320710
|
-
report.restored++;
|
|
320711
|
-
}
|
|
320712
|
-
}
|
|
320713
|
-
} catch {
|
|
320714
|
-
}
|
|
320715
|
-
}
|
|
320716
|
-
} catch {
|
|
320717
|
-
}
|
|
320718
|
-
return report;
|
|
320719
|
-
}
|
|
320720
|
-
function buildSystemPrompt(cwd4) {
|
|
320721
|
-
const parts = [];
|
|
320722
|
-
parts.push(
|
|
320723
|
-
"You are Open Agent (OA), an AI coding assistant running locally via Ollama. You have access to the user's workspace and can discuss code, files, and projects. Be helpful, concise, and technically precise. When asked about files or code, describe what you know from the conversation context."
|
|
320724
|
-
);
|
|
320725
|
-
parts.push(`\\nEnvironment: ${process.platform}, Node ${process.version}, CWD: ${cwd4}`);
|
|
320726
|
-
const diaryPath = join94(cwd4, ".oa", "context", "session-diary.md");
|
|
320727
|
-
if (existsSync78(diaryPath)) {
|
|
320728
|
-
try {
|
|
320729
|
-
const diary = readFileSync61(diaryPath, "utf-8").slice(0, 1e3);
|
|
320730
|
-
parts.push(`\\nPrevious session history:\\n${diary}`);
|
|
320731
|
-
} catch {
|
|
320732
|
-
}
|
|
320733
|
-
}
|
|
320734
|
-
const memDir = join94(cwd4, ".oa", "memory");
|
|
320735
|
-
if (existsSync78(memDir)) {
|
|
320736
|
-
try {
|
|
320737
|
-
const files = readdirSync26(memDir).filter((f2) => f2.endsWith(".json")).slice(0, 5);
|
|
320738
|
-
if (files.length > 0) {
|
|
320739
|
-
parts.push("\\nPersistent memory topics: " + files.map((f2) => f2.replace(".json", "")).join(", "));
|
|
320740
|
-
for (const f2 of files.slice(0, 3)) {
|
|
320741
|
-
try {
|
|
320742
|
-
const data = JSON.parse(readFileSync61(join94(memDir, f2), "utf-8"));
|
|
320743
|
-
const entries = Object.entries(data).slice(0, 3);
|
|
320744
|
-
if (entries.length > 0) {
|
|
320745
|
-
parts.push(`\\nMemory [${f2.replace(".json", "")}]: ` + entries.map(([k, v]) => `${k}: ${String(v.value ?? v).slice(0, 100)}`).join("; "));
|
|
320746
|
-
}
|
|
320747
|
-
} catch {
|
|
320748
|
-
}
|
|
320749
|
-
}
|
|
320750
|
-
}
|
|
320751
|
-
} catch {
|
|
320752
|
-
}
|
|
320753
|
-
}
|
|
320754
|
-
for (const name10 of ["AGENTS.md", "OA.md", ".open-agents.md"]) {
|
|
320755
|
-
const p2 = join94(cwd4, name10);
|
|
320756
|
-
if (existsSync78(p2)) {
|
|
320757
|
-
try {
|
|
320758
|
-
const content = readFileSync61(p2, "utf-8").slice(0, 500);
|
|
320759
|
-
parts.push(`\\nProject instructions (${name10}):\\n${content}`);
|
|
320760
|
-
} catch {
|
|
320761
|
-
}
|
|
320762
|
-
}
|
|
320763
|
-
}
|
|
320764
|
-
return parts.join("");
|
|
320765
|
-
}
|
|
320766
|
-
function getSession(sessionId, model, cwd4) {
|
|
320767
|
-
if (sessionId && sessions.has(sessionId)) {
|
|
320768
|
-
const s2 = sessions.get(sessionId);
|
|
320769
|
-
s2.lastActivity = Date.now();
|
|
320770
|
-
return s2;
|
|
320771
|
-
}
|
|
320772
|
-
if (sessionId) {
|
|
320773
|
-
try {
|
|
320774
|
-
const fp = sessionPath(sessionId);
|
|
320775
|
-
if (existsSync78(fp)) {
|
|
320776
|
-
const parsed = JSON.parse(readFileSync61(fp, "utf-8"));
|
|
320777
|
-
if (parsed && parsed.id === sessionId) {
|
|
320778
|
-
parsed.lastActivity = Date.now();
|
|
320779
|
-
sessions.set(sessionId, parsed);
|
|
320780
|
-
return parsed;
|
|
320781
|
-
}
|
|
320782
|
-
}
|
|
320783
|
-
} catch {
|
|
320784
|
-
}
|
|
320785
|
-
}
|
|
320786
|
-
const id = sessionId || randomUUID10();
|
|
320787
|
-
const systemPrompt = buildSystemPrompt(cwd4);
|
|
320788
|
-
const session = {
|
|
320789
|
-
id,
|
|
320790
|
-
messages: [{ role: "system", content: systemPrompt }],
|
|
320791
|
-
model,
|
|
320792
|
-
createdAt: Date.now(),
|
|
320793
|
-
lastActivity: Date.now(),
|
|
320794
|
-
tokensIn: 0,
|
|
320795
|
-
tokensOut: 0
|
|
320796
|
-
};
|
|
320797
|
-
sessions.set(id, session);
|
|
320798
|
-
persistSession(session);
|
|
320799
|
-
return session;
|
|
320800
|
-
}
|
|
320801
|
-
function addUserMessage(session, content) {
|
|
320802
|
-
session.messages.push({ role: "user", content });
|
|
320803
|
-
session.lastActivity = Date.now();
|
|
320804
|
-
persistSession(session);
|
|
320805
|
-
return session.messages.filter((m2) => m2.role === "system" || m2.role === "user" || m2.role === "assistant");
|
|
320806
|
-
}
|
|
320807
|
-
function addAssistantMessage(session, content) {
|
|
320808
|
-
session.messages.push({ role: "assistant", content });
|
|
320809
|
-
session.lastActivity = Date.now();
|
|
320810
|
-
persistSession(session);
|
|
320811
|
-
}
|
|
320812
|
-
function addToolCallMessage(session, tool, args) {
|
|
320813
|
-
let cappedArgs = args;
|
|
320814
|
-
try {
|
|
320815
|
-
const json = JSON.stringify(args);
|
|
320816
|
-
if (json && json.length > 4e3) {
|
|
320817
|
-
if (args && typeof args === "object" && !Array.isArray(args)) {
|
|
320818
|
-
const pruned = {};
|
|
320819
|
-
for (const [k, v] of Object.entries(args)) {
|
|
320820
|
-
if (typeof v === "string" && v.length > 1e3) {
|
|
320821
|
-
pruned[k] = v.slice(0, 800) + `… [+${v.length - 800} chars truncated for storage]`;
|
|
320822
|
-
} else {
|
|
320823
|
-
pruned[k] = v;
|
|
320824
|
-
}
|
|
320825
|
-
}
|
|
320826
|
-
cappedArgs = pruned;
|
|
320827
|
-
} else {
|
|
320828
|
-
cappedArgs = { _truncated: true, _bytes: json.length };
|
|
320829
|
-
}
|
|
320830
|
-
}
|
|
320831
|
-
} catch {
|
|
320832
|
-
}
|
|
320833
|
-
session.messages.push({
|
|
320834
|
-
role: "tool_call",
|
|
320835
|
-
content: "",
|
|
320836
|
-
tool,
|
|
320837
|
-
args: cappedArgs,
|
|
320838
|
-
ts: Date.now()
|
|
320839
|
-
});
|
|
320840
|
-
session.lastActivity = Date.now();
|
|
320841
|
-
persistSession(session);
|
|
320842
|
-
}
|
|
320843
|
-
function addToolResultMessage(session, tool, output, success) {
|
|
320844
|
-
const capped = output && output.length > 2048 ? output.slice(0, 2e3) + `… [+${output.length - 2e3} chars truncated for storage]` : output;
|
|
320845
|
-
session.messages.push({
|
|
320846
|
-
role: "tool_result",
|
|
320847
|
-
content: "",
|
|
320848
|
-
tool,
|
|
320849
|
-
output: capped,
|
|
320850
|
-
success,
|
|
320851
|
-
ts: Date.now()
|
|
320852
|
-
});
|
|
320853
|
-
session.lastActivity = Date.now();
|
|
320854
|
-
persistSession(session);
|
|
320855
|
-
}
|
|
320856
|
-
function listSessions2() {
|
|
320857
|
-
return Array.from(sessions.values()).map((s2) => ({
|
|
320858
|
-
id: s2.id,
|
|
320859
|
-
model: s2.model,
|
|
320860
|
-
messages: s2.messages.length - 1,
|
|
320861
|
-
// exclude system prompt
|
|
320862
|
-
tokensIn: s2.tokensIn,
|
|
320863
|
-
tokensOut: s2.tokensOut,
|
|
320864
|
-
lastActivity: new Date(s2.lastActivity).toISOString()
|
|
320865
|
-
}));
|
|
320866
|
-
}
|
|
320867
|
-
function deleteSession2(id) {
|
|
320868
|
-
try {
|
|
320869
|
-
const p2 = sessionPath(id);
|
|
320870
|
-
if (existsSync78(p2)) unlinkSync20(p2);
|
|
320871
|
-
} catch {
|
|
320872
|
-
}
|
|
320873
|
-
deleteInFlightFile(id);
|
|
320874
|
-
inFlight.delete(id);
|
|
320875
|
-
return sessions.delete(id);
|
|
320876
|
-
}
|
|
320877
|
-
function lookupSession(id) {
|
|
320878
|
-
const cached = sessions.get(id);
|
|
320879
|
-
if (cached) return cached;
|
|
320880
|
-
try {
|
|
320881
|
-
const fp = sessionPath(id);
|
|
320882
|
-
if (existsSync78(fp)) {
|
|
320883
|
-
const parsed = JSON.parse(readFileSync61(fp, "utf-8"));
|
|
320884
|
-
if (parsed && parsed.id === id) {
|
|
320885
|
-
sessions.set(id, parsed);
|
|
320886
|
-
return parsed;
|
|
320887
|
-
}
|
|
321118
|
+
"/v1/memory/write": { post: { summary: "Write a memory entry (PT-04)", tags: ["Memory"], responses: { 201: { description: "Written" }, 403: { description: "Requires run/admin scope" } } } },
|
|
321119
|
+
"/v1/memory/episodes": { get: { summary: "List episodes", tags: ["Memory"], responses: { 200: { description: "Paginated episodes" } } } },
|
|
321120
|
+
"/v1/memory/failures": { get: { summary: "List failures", tags: ["Memory"], responses: { 200: { description: "Paginated failures" } } } },
|
|
321121
|
+
// ───── PT-06: Events ─────
|
|
321122
|
+
"/v1/events": {
|
|
321123
|
+
get: {
|
|
321124
|
+
summary: "Subscribe to the event bus (PT-06)",
|
|
321125
|
+
description: "Server-sent event stream. Event types: config.changed, run.started, run.completed, run.aborted, run.failed, tool.called, memory.written, memory.searched, skill.invoked, mcp.called, engine.state_changed, auth.failed, rate_limit.hit, incident.raised, incident.resolved, session.created, session.ended, aims.policy_changed, aims.decision_recorded. Filter with ?type=foo.bar or ?type=foo.*",
|
|
321126
|
+
tags: ["Events"],
|
|
321127
|
+
parameters: [{ name: "type", in: "query", required: false, schema: { type: "string" }, description: "Filter by event type (supports wildcard suffix .*)" }],
|
|
321128
|
+
responses: { 200: { description: "text/event-stream with event frames" } }
|
|
321129
|
+
}
|
|
321130
|
+
},
|
|
321131
|
+
// ───── P1: Sessions ─────
|
|
321132
|
+
"/v1/sessions": { get: { summary: "List OA task sessions", tags: ["Sessions"], responses: { 200: { description: "Paginated sessions" } } } },
|
|
321133
|
+
"/v1/sessions/{id}": { get: { summary: "Get session history", tags: ["Sessions"], parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }], responses: { 200: { description: "Session history" }, 404: { description: "Not found" } } } },
|
|
321134
|
+
// ───── P1: Context ─────
|
|
321135
|
+
"/v1/context": { get: { summary: "Show current session context", tags: ["Context"], responses: { 200: { description: "Context snapshot" } } } },
|
|
321136
|
+
"/v1/context/save": { post: { summary: "Save a session context entry", tags: ["Context"], responses: { 201: { description: "Saved" } } } },
|
|
321137
|
+
"/v1/context/restore": { get: { summary: "Build a restore prompt from saved context", tags: ["Context"], responses: { 200: { description: "Restore prompt" } } } },
|
|
321138
|
+
"/v1/context/compact": { post: { summary: "Request context compaction (event-driven)", tags: ["Context"], responses: { 202: { description: "Compaction requested" } } } },
|
|
321139
|
+
// ───── P1: Nexus / Sponsors ─────
|
|
321140
|
+
"/v1/nexus/status": { get: { summary: "Nexus peer state", tags: ["Nexus"], responses: { 200: { description: "Peer cache snapshot" } } } },
|
|
321141
|
+
"/v1/sponsors": { get: { summary: "Local sponsor directory cache", tags: ["Nexus"], responses: { 200: { description: "Paginated sponsors" } } } },
|
|
321142
|
+
// ───── P1: Cost / Evaluate / Index ─────
|
|
321143
|
+
"/v1/cost": { get: { summary: "Cost tracker (pricing model)", tags: ["Metering"], responses: { 200: { description: "Provider pricing" } } } },
|
|
321144
|
+
"/v1/evaluate": { post: { summary: "Evaluate a run by ID", tags: ["Agentic"], responses: { 200: { description: "Evaluation metrics" }, 404: { description: "Run not found" } } } },
|
|
321145
|
+
"/v1/index": { post: { summary: "Trigger repository indexing", tags: ["Agentic"], responses: { 202: { description: "Indexing started" } } } },
|
|
321146
|
+
// ───── P2: Tools / Hooks / Agents / Engines ─────
|
|
321147
|
+
"/v1/tools": { get: { summary: "List agentic tool registry", tags: ["Tools"], responses: { 200: { description: "Paginated tools" } } } },
|
|
321148
|
+
"/v1/hooks": { get: { summary: "List hook types and counts", tags: ["Tools"], responses: { 200: { description: "Hook registry" } } } },
|
|
321149
|
+
"/v1/agents": { get: { summary: "List agent types", tags: ["Tools"], responses: { 200: { description: "Agent type registry" } } } },
|
|
321150
|
+
"/v1/engines": { get: { summary: "List long-running engines", tags: ["Engines"], responses: { 200: { description: "Engine status + state files" } } } },
|
|
321151
|
+
// ───── P2: Voice / Vision (deferred) ─────
|
|
321152
|
+
"/v1/voice/tts": { post: { summary: "Text-to-speech (deferred to PT-07)", tags: ["Voice"], responses: { 501: { description: "Not yet daemon-resident" } } } },
|
|
321153
|
+
"/v1/voice/asr": { post: { summary: "Automatic speech recognition (deferred)", tags: ["Voice"], responses: { 501: { description: "Not yet daemon-resident" } } } },
|
|
321154
|
+
"/v1/vision/describe": { post: { summary: "Vision describe (deferred to PT-07)", tags: ["Vision"], responses: { 501: { description: "Not yet daemon-resident" } } } },
|
|
321155
|
+
// ───── ISO/IEC 42001:2023 AIMS ─────
|
|
321156
|
+
"/v1/aims": { get: { summary: "AIMS root — control map and endpoint index", tags: ["AIMS"], responses: { 200: { description: "AIMS overview" } } } },
|
|
321157
|
+
"/v1/aims/policies": {
|
|
321158
|
+
get: { summary: "AI policies (ISO 42001 A.2)", tags: ["AIMS"], responses: { 200: { description: "Policy register" } } },
|
|
321159
|
+
put: { summary: "Replace the policy register (ISO 42001 A.2)", tags: ["AIMS"], responses: { 200: { description: "Updated" }, 403: { description: "Admin scope required" } } }
|
|
321160
|
+
},
|
|
321161
|
+
"/v1/aims/roles": { get: { summary: "Roles and responsibilities (A.3)", tags: ["AIMS"], responses: { 200: { description: "Role register" } } } },
|
|
321162
|
+
"/v1/aims/resources": { get: { summary: "Resources inventory (A.4)", tags: ["AIMS"], responses: { 200: { description: "Compute + backend inventory" } } } },
|
|
321163
|
+
"/v1/aims/impact-assessments": {
|
|
321164
|
+
get: { summary: "Impact assessment register (A.5)", tags: ["AIMS"], responses: { 200: { description: "Paginated impact assessments" } } },
|
|
321165
|
+
post: { summary: "File an impact assessment (A.5)", tags: ["AIMS"], responses: { 201: { description: "Recorded" }, 403: { description: "Admin scope required" } } }
|
|
321166
|
+
},
|
|
321167
|
+
"/v1/aims/lifecycle": { get: { summary: "AI system lifecycle state (A.6)", tags: ["AIMS"], responses: { 200: { description: "Lifecycle snapshot" } } } },
|
|
321168
|
+
"/v1/aims/data-quality": { get: { summary: "Data quality controls (A.7.2)", tags: ["AIMS"], responses: { 200: { description: "Data quality register" } } } },
|
|
321169
|
+
"/v1/aims/transparency": { get: { summary: "Model cards and transparency info (A.8)", tags: ["AIMS"], responses: { 200: { description: "Model cards" } } } },
|
|
321170
|
+
"/v1/aims/usage": { get: { summary: "AI system usage (A.9 alias of /v1/usage)", tags: ["AIMS"], responses: { 200: { description: "Usage snapshot" } } } },
|
|
321171
|
+
"/v1/aims/suppliers": { get: { summary: "Third-party supplier inventory (A.10)", tags: ["AIMS"], responses: { 200: { description: "Suppliers" } } } },
|
|
321172
|
+
"/v1/aims/incidents": {
|
|
321173
|
+
get: { summary: "Incident register (A.6.2.8)", tags: ["AIMS"], responses: { 200: { description: "Paginated incidents" } } },
|
|
321174
|
+
post: { summary: "Raise an incident (A.6.2.8)", tags: ["AIMS"], responses: { 201: { description: "Recorded" } } }
|
|
321175
|
+
},
|
|
321176
|
+
"/v1/aims/oversight": { get: { summary: "Human oversight gates (A.6.2.7)", tags: ["AIMS"], responses: { 200: { description: "Oversight configuration" } } } },
|
|
321177
|
+
"/v1/aims/decisions": { get: { summary: "Consequential decision log (A.9)", tags: ["AIMS"], responses: { 200: { description: "Paginated decisions" } } } },
|
|
321178
|
+
"/v1/aims/config-history": { get: { summary: "Configuration change history (A.6.2.8)", tags: ["AIMS"], responses: { 200: { description: "Config change records from audit log" } } } }
|
|
320888
321179
|
}
|
|
320889
|
-
} catch {
|
|
320890
|
-
}
|
|
320891
|
-
return null;
|
|
320892
|
-
}
|
|
320893
|
-
function startInFlightChat(opts) {
|
|
320894
|
-
const job = {
|
|
320895
|
-
sessionId: opts.sessionId,
|
|
320896
|
-
runId: opts.runId,
|
|
320897
|
-
pid: opts.pid,
|
|
320898
|
-
startedAt: Date.now(),
|
|
320899
|
-
partialOutput: "",
|
|
320900
|
-
status: "running",
|
|
320901
|
-
tailBytes: 0
|
|
320902
321180
|
};
|
|
320903
|
-
inFlight.set(opts.sessionId, job);
|
|
320904
|
-
persistInFlight(job);
|
|
320905
|
-
return job;
|
|
320906
321181
|
}
|
|
320907
|
-
function
|
|
320908
|
-
|
|
320909
|
-
|
|
320910
|
-
|
|
320911
|
-
|
|
320912
|
-
|
|
320913
|
-
|
|
321182
|
+
function getSwaggerUI() {
|
|
321183
|
+
return `<!DOCTYPE html><html><head><title>OA API Docs</title>
|
|
321184
|
+
<meta charset="utf-8">
|
|
321185
|
+
<style>
|
|
321186
|
+
body{font-family:'SF Mono',ui-monospace,monospace;background:#1a1a1e;color:#b0b0b0;margin:0;padding:20px;font-size:13px}
|
|
321187
|
+
h1{color:#b2920a;font-size:1.3rem;margin-bottom:4px}
|
|
321188
|
+
h2{color:#b2920a;font-size:0.85rem;margin-top:20px;text-transform:uppercase;letter-spacing:1px}
|
|
321189
|
+
.subtitle{color:#888;font-size:0.85rem;margin-bottom:20px}
|
|
321190
|
+
.endpoint{background:#1e1e22;border:1px solid #2a2a30;border-radius:3px;padding:8px 12px;margin:4px 0;display:flex;align-items:center;gap:12px}
|
|
321191
|
+
.method{font-weight:bold;padding:2px 8px;border-radius:2px;font-size:0.7rem;min-width:50px;text-align:center}
|
|
321192
|
+
.get{color:#4ec94e;border:1px solid #4ec94e}.post{color:#b2920a;border:1px solid #b2920a}.put{color:#4e94c9;border:1px solid #4e94c9}
|
|
321193
|
+
.patch{color:#c9944e;border:1px solid #c9944e}.delete{color:#c94e4e;border:1px solid #c94e4e}
|
|
321194
|
+
.path{color:#b0b0b0;font-weight:600}.summary{color:#888;font-size:0.75rem;flex:1;text-align:right}
|
|
321195
|
+
.tag{margin-top:18px;border-bottom:1px solid #2a2a30;padding-bottom:4px}
|
|
321196
|
+
a{color:#b2920a;text-decoration:none}a:hover{text-decoration:underline}
|
|
321197
|
+
.iso-note{background:#222;border-left:3px solid #b2920a;padding:10px 14px;margin:12px 0;color:#ccc;font-size:0.8rem}
|
|
321198
|
+
</style></head><body>
|
|
321199
|
+
<h1>Open Agents REST API</h1>
|
|
321200
|
+
<p class="subtitle">Enterprise-grade REST surface. Errors follow RFC 7807. Responses carry X-API-Version. <a href="/openapi.json">Full OpenAPI 3.0 spec</a></p>
|
|
321201
|
+
<div class="iso-note">
|
|
321202
|
+
<strong>ISO/IEC 42001:2023 AIMS layer:</strong> The /v1/aims/* endpoints expose the
|
|
321203
|
+
AI Management System controls an auditor expects — policies, roles, resources, impact
|
|
321204
|
+
assessments, lifecycle, data quality, transparency, usage, suppliers, incidents, oversight,
|
|
321205
|
+
decisions, and config history. Events published to /v1/events are tagged with the matching
|
|
321206
|
+
Annex A control reference (aims:control field).
|
|
321207
|
+
</div>
|
|
321208
|
+
<div id="docs"></div>
|
|
321209
|
+
<script>
|
|
321210
|
+
fetch('/openapi.json').then(r=>r.json()).then(spec=>{
|
|
321211
|
+
const docs=document.getElementById('docs');
|
|
321212
|
+
const tagOrder=(spec.tags||[]).map(t=>t.name);
|
|
321213
|
+
const byTag={};
|
|
321214
|
+
for(const[path,methods]of Object.entries(spec.paths)){
|
|
321215
|
+
for(const[method,op]of Object.entries(methods)){
|
|
321216
|
+
const tag=(op.tags||['Other'])[0];
|
|
321217
|
+
if(!byTag[tag])byTag[tag]=[];
|
|
321218
|
+
byTag[tag].push({method,path,summary:op.summary||''});
|
|
321219
|
+
}
|
|
320914
321220
|
}
|
|
320915
|
-
|
|
320916
|
-
|
|
320917
|
-
|
|
320918
|
-
|
|
320919
|
-
|
|
320920
|
-
|
|
320921
|
-
|
|
320922
|
-
|
|
320923
|
-
|
|
320924
|
-
|
|
320925
|
-
setTimeout(() => {
|
|
320926
|
-
inFlight.delete(sessionId);
|
|
320927
|
-
deleteInFlightFile(sessionId);
|
|
320928
|
-
}, 3e4).unref?.();
|
|
320929
|
-
}
|
|
320930
|
-
function getInFlightChat(sessionId) {
|
|
320931
|
-
const cached = inFlight.get(sessionId);
|
|
320932
|
-
if (cached) return cached;
|
|
320933
|
-
try {
|
|
320934
|
-
const p2 = inFlightPath(sessionId);
|
|
320935
|
-
if (existsSync78(p2)) {
|
|
320936
|
-
const parsed = JSON.parse(readFileSync61(p2, "utf-8"));
|
|
320937
|
-
if (parsed && parsed.sessionId === sessionId) {
|
|
320938
|
-
return parsed;
|
|
320939
|
-
}
|
|
321221
|
+
const orderedTags=[...tagOrder.filter(t=>byTag[t]),...Object.keys(byTag).filter(t=>!tagOrder.includes(t))];
|
|
321222
|
+
for(const tag of orderedTags){
|
|
321223
|
+
const endpoints=byTag[tag];
|
|
321224
|
+
const tagObj=(spec.tags||[]).find(t=>t.name===tag);
|
|
321225
|
+
let html='<div class="tag"><h2>'+tag+'</h2>';
|
|
321226
|
+
if(tagObj&&tagObj.description)html+='<div style="color:#777;font-size:0.75rem;margin-bottom:8px">'+tagObj.description+'</div>';
|
|
321227
|
+
html+='</div>';
|
|
321228
|
+
docs.innerHTML+=html;
|
|
321229
|
+
for(const ep of endpoints){
|
|
321230
|
+
docs.innerHTML+='<div class="endpoint"><span class="method '+ep.method+'">'+ep.method.toUpperCase()+'</span><span class="path">'+ep.path+'</span><span class="summary">'+ep.summary+'</span></div>';
|
|
320940
321231
|
}
|
|
320941
|
-
} catch {
|
|
320942
321232
|
}
|
|
320943
|
-
|
|
320944
|
-
|
|
320945
|
-
function compactSession(session, maxMessages = 40) {
|
|
320946
|
-
if (session.messages.length <= maxMessages) return;
|
|
320947
|
-
const system = session.messages[0];
|
|
320948
|
-
const recent = session.messages.slice(-20);
|
|
320949
|
-
const middle = session.messages.slice(1, -20);
|
|
320950
|
-
const summary = `[Earlier conversation: ${middle.length} messages discussed ` + middle.filter((m2) => m2.role === "user").map((m2) => m2.content.slice(0, 50)).slice(0, 5).join(", ") + "...]";
|
|
320951
|
-
session.messages = [
|
|
320952
|
-
system,
|
|
320953
|
-
{ role: "system", content: summary },
|
|
320954
|
-
...recent
|
|
320955
|
-
];
|
|
321233
|
+
});
|
|
321234
|
+
</script></body></html>`;
|
|
320956
321235
|
}
|
|
320957
|
-
var
|
|
320958
|
-
|
|
320959
|
-
"packages/cli/src/api/chat-session.ts"() {
|
|
321236
|
+
var init_openapi = __esm({
|
|
321237
|
+
"packages/cli/src/api/openapi.ts"() {
|
|
320960
321238
|
"use strict";
|
|
320961
|
-
|
|
320962
|
-
|
|
320963
|
-
|
|
320964
|
-
|
|
320965
|
-
|
|
320966
|
-
|
|
320967
|
-
|
|
320968
|
-
|
|
320969
|
-
|
|
320970
|
-
|
|
321239
|
+
init_http2();
|
|
321240
|
+
}
|
|
321241
|
+
});
|
|
321242
|
+
|
|
321243
|
+
// packages/cli/src/api/auth-oidc.ts
|
|
321244
|
+
var OIDC_ISSUER, OIDC_AUDIENCE, OIDC_SCOPE_CLAIM;
|
|
321245
|
+
var init_auth_oidc = __esm({
|
|
321246
|
+
"packages/cli/src/api/auth-oidc.ts"() {
|
|
321247
|
+
"use strict";
|
|
321248
|
+
OIDC_ISSUER = process.env["OA_OIDC_ISSUER"] || "";
|
|
321249
|
+
OIDC_AUDIENCE = process.env["OA_OIDC_AUDIENCE"] || "";
|
|
321250
|
+
OIDC_SCOPE_CLAIM = process.env["OA_OIDC_SCOPE_CLAIM"] || "scope";
|
|
320971
321251
|
}
|
|
320972
321252
|
});
|
|
320973
321253
|
|
|
@@ -324407,6 +324687,105 @@ ${historyLines}
|
|
|
324407
324687
|
jsonResponse(res, 200, { sessions: listSessions2() });
|
|
324408
324688
|
return;
|
|
324409
324689
|
}
|
|
324690
|
+
if (pathname === "/v1/chat/check-in" && method === "POST") {
|
|
324691
|
+
if (!checkAuth(req2, res, "run")) {
|
|
324692
|
+
status = 401;
|
|
324693
|
+
return;
|
|
324694
|
+
}
|
|
324695
|
+
const body = await parseJsonBody(req2);
|
|
324696
|
+
if (!body || typeof body.session_id !== "string" || typeof body.message !== "string") {
|
|
324697
|
+
jsonResponse(res, 400, { error: "Required: { session_id: string, message: string }" });
|
|
324698
|
+
return;
|
|
324699
|
+
}
|
|
324700
|
+
const checkinSession = lookupSession(body.session_id);
|
|
324701
|
+
if (!checkinSession) {
|
|
324702
|
+
jsonResponse(res, 404, { error: "Session not found", session_id: body.session_id });
|
|
324703
|
+
return;
|
|
324704
|
+
}
|
|
324705
|
+
try {
|
|
324706
|
+
addCheckinMessage(checkinSession, body.message);
|
|
324707
|
+
} catch {
|
|
324708
|
+
}
|
|
324709
|
+
let acknowledgment = "Got it, adapting course.";
|
|
324710
|
+
let steering = body.message;
|
|
324711
|
+
try {
|
|
324712
|
+
const cfg = loadConfig();
|
|
324713
|
+
const { OllamaAgenticBackend: OllamaAgenticBackend2, AgenticRunner: AgenticRunner2 } = await Promise.resolve().then(() => (init_dist8(), dist_exports4));
|
|
324714
|
+
const triageBackend = new OllamaAgenticBackend2(cfg.backendUrl, cfg.model, cfg.apiKey);
|
|
324715
|
+
const triage = new AgenticRunner2(triageBackend, {
|
|
324716
|
+
maxTurns: 3,
|
|
324717
|
+
maxTokens: 512,
|
|
324718
|
+
temperature: 0.3,
|
|
324719
|
+
requestTimeoutMs: 15e3,
|
|
324720
|
+
taskTimeoutMs: 3e4,
|
|
324721
|
+
streamEnabled: false
|
|
324722
|
+
});
|
|
324723
|
+
triage.registerTool({
|
|
324724
|
+
name: "task_complete",
|
|
324725
|
+
description: "Return the structured triage result.",
|
|
324726
|
+
parameters: {
|
|
324727
|
+
type: "object",
|
|
324728
|
+
properties: {
|
|
324729
|
+
acknowledgment: { type: "string", description: "Short spoken-style ack (1 sentence) referencing what the user asked." },
|
|
324730
|
+
summary: { type: "string", description: "Expanded steering instruction for the main agent." }
|
|
324731
|
+
},
|
|
324732
|
+
required: ["summary", "acknowledgment"]
|
|
324733
|
+
},
|
|
324734
|
+
async execute(args) {
|
|
324735
|
+
return { success: true, output: JSON.stringify(args) };
|
|
324736
|
+
}
|
|
324737
|
+
});
|
|
324738
|
+
const recentMessages = checkinSession.messages.filter((m2) => m2.role === "user" || m2.role === "assistant").slice(-6).map((m2) => `${m2.role}: ${m2.content.slice(0, 200)}`).join("\n");
|
|
324739
|
+
const triagePrompt = [
|
|
324740
|
+
"The user has typed a mid-run check-in message while the main agent is working.",
|
|
324741
|
+
"Your job: expand their raw input into a clear steering instruction the main agent can act on,",
|
|
324742
|
+
"and produce a brief spoken acknowledgment that REFERENCES what they actually said.",
|
|
324743
|
+
"",
|
|
324744
|
+
`Recent conversation:
|
|
324745
|
+
${recentMessages || "(none)"}`,
|
|
324746
|
+
"",
|
|
324747
|
+
`User check-in: "${body.message}"`,
|
|
324748
|
+
"",
|
|
324749
|
+
"Call task_complete with:",
|
|
324750
|
+
"- acknowledgment: 1 sentence specific to their request (no generic 'Got it')",
|
|
324751
|
+
"- summary: an expanded instruction for the main agent (~2-4 sentences)"
|
|
324752
|
+
].join("\n");
|
|
324753
|
+
const result = await triage.run(triagePrompt, "Triage check-in handler");
|
|
324754
|
+
try {
|
|
324755
|
+
const parsed = JSON.parse(result.summary || "{}");
|
|
324756
|
+
if (parsed.acknowledgment) acknowledgment = String(parsed.acknowledgment);
|
|
324757
|
+
if (parsed.summary) steering = String(parsed.summary);
|
|
324758
|
+
} catch {
|
|
324759
|
+
if (result.summary && result.summary.length > 10) steering = result.summary;
|
|
324760
|
+
}
|
|
324761
|
+
} catch (err) {
|
|
324762
|
+
}
|
|
324763
|
+
try {
|
|
324764
|
+
addTriageResponseMessage(checkinSession, acknowledgment, steering);
|
|
324765
|
+
} catch {
|
|
324766
|
+
}
|
|
324767
|
+
const markedSteering = `[USER CHECK-IN — incorporate during current run]
|
|
324768
|
+
${steering}`;
|
|
324769
|
+
try {
|
|
324770
|
+
appendCheckin(body.session_id, markedSteering);
|
|
324771
|
+
} catch {
|
|
324772
|
+
}
|
|
324773
|
+
try {
|
|
324774
|
+
publishEvent("memory.written", {
|
|
324775
|
+
kind: "chat_checkin",
|
|
324776
|
+
session_id: body.session_id,
|
|
324777
|
+
ack: acknowledgment.slice(0, 120)
|
|
324778
|
+
}, { subject: body.session_id });
|
|
324779
|
+
} catch {
|
|
324780
|
+
}
|
|
324781
|
+
jsonResponse(res, 200, {
|
|
324782
|
+
session_id: body.session_id,
|
|
324783
|
+
acknowledgment,
|
|
324784
|
+
steering,
|
|
324785
|
+
delivered: true
|
|
324786
|
+
});
|
|
324787
|
+
return;
|
|
324788
|
+
}
|
|
324410
324789
|
const chatSessionMatch = pathname.match(/^\/v1\/chat\/sessions\/([^/]+)$/);
|
|
324411
324790
|
if (chatSessionMatch) {
|
|
324412
324791
|
const sid = decodeURIComponent(chatSessionMatch[1]);
|
|
@@ -326049,6 +326428,23 @@ RULES:
|
|
|
326049
326428
|
});
|
|
326050
326429
|
runner.setWorkingDirectory(repoRoot);
|
|
326051
326430
|
_activeRunnerRef = runner;
|
|
326431
|
+
let _checkinPoller = null;
|
|
326432
|
+
try {
|
|
326433
|
+
const oaSessionId = process.env["OA_SESSION_ID"];
|
|
326434
|
+
if (oaSessionId) {
|
|
326435
|
+
const { drainCheckins: drainCheckins2 } = (init_chat_session(), __toCommonJS(chat_session_exports));
|
|
326436
|
+
_checkinPoller = setInterval(() => {
|
|
326437
|
+
try {
|
|
326438
|
+
const pending = drainCheckins2(oaSessionId);
|
|
326439
|
+
for (const steering of pending) {
|
|
326440
|
+
runner.injectUserMessage(steering);
|
|
326441
|
+
}
|
|
326442
|
+
} catch {
|
|
326443
|
+
}
|
|
326444
|
+
}, 1500);
|
|
326445
|
+
}
|
|
326446
|
+
} catch {
|
|
326447
|
+
}
|
|
326052
326448
|
const tools = buildTools(repoRoot, config, contextWindowSize, modelTier);
|
|
326053
326449
|
if (contextWindowSize && contextWindowSize > 0) {
|
|
326054
326450
|
for (const tool of tools) {
|
|
@@ -326925,6 +327321,13 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
|
|
|
326925
327321
|
if (backend && typeof backend.stop === "function") {
|
|
326926
327322
|
backend.stop();
|
|
326927
327323
|
}
|
|
327324
|
+
if (_checkinPoller) {
|
|
327325
|
+
try {
|
|
327326
|
+
clearInterval(_checkinPoller);
|
|
327327
|
+
} catch {
|
|
327328
|
+
}
|
|
327329
|
+
_checkinPoller = null;
|
|
327330
|
+
}
|
|
326928
327331
|
});
|
|
326929
327332
|
return { runner, promise, filesTouched, get toolCallCount() {
|
|
326930
327333
|
return toolSequence.length;
|