open-agents-ai 0.187.532 → 0.187.534
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 +537 -9
- package/npm-shrinkwrap.json +14 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -526818,6 +526818,85 @@ Pick the SMALLEST concrete deliverable from the spec — typically the project e
|
|
|
526818
526818
|
}
|
|
526819
526819
|
}
|
|
526820
526820
|
}
|
|
526821
|
+
/**
|
|
526822
|
+
* REG-66 Debug-Loop Detection (root-cause from batch535-midi, 2026-05-04).
|
|
526823
|
+
*
|
|
526824
|
+
* Empirical: midi run had 11x `npm run build 2>&1` + same 5 files re-read
|
|
526825
|
+
* 5-6 times each + 22 BFC-61.G coercion BLOCKS — and ZERO of those blocks
|
|
526826
|
+
* resulted in a creative edit. The agent was rationally stuck: it
|
|
526827
|
+
* believed it needed to read more to debug, the build command kept
|
|
526828
|
+
* giving the same error, and the standard "issue an edit" directive
|
|
526829
|
+
* gave no traction because the agent had no concrete edit hypothesis.
|
|
526830
|
+
*
|
|
526831
|
+
* This method analyzes toolCallLog for the debug-loop signature:
|
|
526832
|
+
* - Same shell command stem repeated ≥5 times in the trailing window, OR
|
|
526833
|
+
* - Same file_read path re-read ≥4 times in the trailing window.
|
|
526834
|
+
* Both indicate the agent is reading/running the same things hoping for
|
|
526835
|
+
* different output. Without this signal we'd just keep telling the
|
|
526836
|
+
* agent to "make an edit" — which is exactly what it can't think of.
|
|
526837
|
+
*
|
|
526838
|
+
* When detected, the BFC-61.G block message swaps to a PERTURB-strategy
|
|
526839
|
+
* directive: stop reading, change ONE thing in the most-likely-culprit
|
|
526840
|
+
* file even if you're uncertain, and let the new error signal guide
|
|
526841
|
+
* the next iteration. This is real human debugging strategy ("perturb
|
|
526842
|
+
* to disambiguate"), NOT reward-hacking — the agent still has to
|
|
526843
|
+
* produce a real edit and the success criteria (todos done + build
|
|
526844
|
+
* passing) are unchanged.
|
|
526845
|
+
*
|
|
526846
|
+
* @returns Detection result. `detected=false` → use standard message.
|
|
526847
|
+
* `detected=true` → use REG-66 perturb-strategy message;
|
|
526848
|
+
* `repeatedSample` carries the offending command/path for the
|
|
526849
|
+
* message body so the agent sees the specific pattern called out.
|
|
526850
|
+
*/
|
|
526851
|
+
_detectDebugLoop(toolCallLog) {
|
|
526852
|
+
if (process.env["OA_DISABLE_REG66"] === "1")
|
|
526853
|
+
return { detected: false };
|
|
526854
|
+
const WINDOW = 20;
|
|
526855
|
+
const SHELL_REPEAT_THRESHOLD = 5;
|
|
526856
|
+
const READ_REPEAT_THRESHOLD = 4;
|
|
526857
|
+
const window2 = toolCallLog.slice(-WINDOW);
|
|
526858
|
+
if (window2.length < SHELL_REPEAT_THRESHOLD)
|
|
526859
|
+
return { detected: false };
|
|
526860
|
+
const _editClasses = /* @__PURE__ */ new Set(["file_write", "file_edit", "batch_edit", "file_patch"]);
|
|
526861
|
+
for (const c9 of window2) {
|
|
526862
|
+
if (_editClasses.has(c9.name) && c9.success !== false)
|
|
526863
|
+
return { detected: false };
|
|
526864
|
+
}
|
|
526865
|
+
const shellCounts = /* @__PURE__ */ new Map();
|
|
526866
|
+
const readCounts = /* @__PURE__ */ new Map();
|
|
526867
|
+
for (const c9 of window2) {
|
|
526868
|
+
if (c9.name === "shell") {
|
|
526869
|
+
const m2 = c9.argsKey.match(/(?:^|,)command=([^,]+)/);
|
|
526870
|
+
if (m2 && m2[1]) {
|
|
526871
|
+
const stem = m2[1].trim();
|
|
526872
|
+
shellCounts.set(stem, (shellCounts.get(stem) ?? 0) + 1);
|
|
526873
|
+
}
|
|
526874
|
+
} else if (c9.name === "file_read" || c9.name === "file_explore") {
|
|
526875
|
+
const m2 = c9.argsKey.match(/(?:^|,)path=([^,]+)/);
|
|
526876
|
+
if (m2 && m2[1]) {
|
|
526877
|
+
const stem = m2[1].trim();
|
|
526878
|
+
readCounts.set(stem, (readCounts.get(stem) ?? 0) + 1);
|
|
526879
|
+
}
|
|
526880
|
+
}
|
|
526881
|
+
}
|
|
526882
|
+
let bestShell = null;
|
|
526883
|
+
for (const [k, n2] of shellCounts) {
|
|
526884
|
+
if (n2 >= SHELL_REPEAT_THRESHOLD && (!bestShell || n2 > bestShell[1]))
|
|
526885
|
+
bestShell = [k, n2];
|
|
526886
|
+
}
|
|
526887
|
+
let bestRead = null;
|
|
526888
|
+
for (const [k, n2] of readCounts) {
|
|
526889
|
+
if (n2 >= READ_REPEAT_THRESHOLD && (!bestRead || n2 > bestRead[1]))
|
|
526890
|
+
bestRead = [k, n2];
|
|
526891
|
+
}
|
|
526892
|
+
if (bestShell) {
|
|
526893
|
+
return { detected: true, repeatedSample: bestShell[0], count: bestShell[1], kind: "shell" };
|
|
526894
|
+
}
|
|
526895
|
+
if (bestRead) {
|
|
526896
|
+
return { detected: true, repeatedSample: bestRead[0], count: bestRead[1], kind: "read" };
|
|
526897
|
+
}
|
|
526898
|
+
return { detected: false };
|
|
526899
|
+
}
|
|
526821
526900
|
readSessionTodos() {
|
|
526822
526901
|
try {
|
|
526823
526902
|
const sid = process.env["OA_SESSION_ID"] || this._sessionId || "default";
|
|
@@ -528818,6 +528897,8 @@ TASK: ${task}` : task;
|
|
|
528818
528897
|
try {
|
|
528819
528898
|
if (this.options.subAgent)
|
|
528820
528899
|
throw "skip-handoff-subagent";
|
|
528900
|
+
if (process.env["OA_FRESH_SESSION"] === "1")
|
|
528901
|
+
throw "skip-handoff-fresh";
|
|
528821
528902
|
const oaDir = this._workingDirectory ? _pathJoin(this._workingDirectory, ".oa") : _pathJoin(process.cwd(), ".oa");
|
|
528822
528903
|
const chainPairs = loadMessagePairsFromLog(oaDir, { currentTask: cleanedTask });
|
|
528823
528904
|
if (chainPairs.length > 0) {
|
|
@@ -530749,7 +530830,32 @@ ${memoryLines.join("\n")}`
|
|
|
530749
530830
|
turn,
|
|
530750
530831
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
530751
530832
|
});
|
|
530752
|
-
const
|
|
530833
|
+
const _dbgLoop = this._detectDebugLoop(toolCallLog);
|
|
530834
|
+
const _debugLoopSampleSafe = (_dbgLoop.repeatedSample ?? "").slice(0, 120);
|
|
530835
|
+
const reg61BlockMsg = _dbgLoop.detected ? [
|
|
530836
|
+
`[BLOCKED — REG-61 directive in effect — REG-66 DEBUG-LOOP detected]`,
|
|
530837
|
+
``,
|
|
530838
|
+
`Pattern: ${_dbgLoop.kind === "shell" ? "shell command" : "file"} "${_debugLoopSampleSafe}" was used ${_dbgLoop.count}× in the trailing window with ZERO creative edits landing. You are stuck in a debug loop where re-running / re-reading is producing no new information.`,
|
|
530839
|
+
``,
|
|
530840
|
+
`STOP DEBUGGING. PERTURB.`,
|
|
530841
|
+
``,
|
|
530842
|
+
`Strategy when stuck like this (real human debuggers do this):`,
|
|
530843
|
+
` 1. Pick the source file most likely implicated by the recurring failure (probably in src/, the one most-imported by failing tests).`,
|
|
530844
|
+
` 2. Pick ONE plausible cause — most-recently-modified line, most-complex function, most-likely-misnamed import, most-likely off-by-one.`,
|
|
530845
|
+
` 3. Make a SPECULATIVE edit that changes that thing — even if you are NOT certain it'll fix the bug. The point is to get a NEW error signal that disambiguates.`,
|
|
530846
|
+
` 4. Re-run the failing command. If the error CHANGED, you've learned something. If it's identical, you've ruled out one hypothesis.`,
|
|
530847
|
+
``,
|
|
530848
|
+
`This is NOT random guessing — it's targeted hypothesis falsification. Reading the same files 5+ times has already proven uninformative; only a state change will move the system.`,
|
|
530849
|
+
``,
|
|
530850
|
+
`Issue EXACTLY ONE of: file_write / file_edit / batch_edit / file_patch on a single concrete change. The exact CHOICE of edit matters less than NOT continuing to re-read.`,
|
|
530851
|
+
``,
|
|
530852
|
+
`Allowed bypasses (will not be blocked but will not clear the directive either):`,
|
|
530853
|
+
` • web_search — search the EXACT recurring error string`,
|
|
530854
|
+
` • task_complete — exit if you genuinely cannot identify any plausible perturbation`,
|
|
530855
|
+
` • ask_user — escalate to human (if available)`,
|
|
530856
|
+
``,
|
|
530857
|
+
`Once you make a real edit, the directive clears and you'll see the new test result.`
|
|
530858
|
+
].join("\n") : [
|
|
530753
530859
|
`[BLOCKED — REG-61 directive in effect]`,
|
|
530754
530860
|
``,
|
|
530755
530861
|
`A REG-61 FIRST-EDIT NUDGE was issued earlier and has not yet been satisfied. The directive: your next tool call MUST be a creative edit. You issued '${tc.name}' instead, which is a read/explore/shell call. This call has been BLOCKED.`,
|
|
@@ -530777,12 +530883,12 @@ ${memoryLines.join("\n")}`
|
|
|
530777
530883
|
});
|
|
530778
530884
|
this.emit({
|
|
530779
530885
|
type: "status",
|
|
530780
|
-
content: `REG-61 COERCION BLOCK — rejected '${tc.name}' at turn ${turn}; gate stays active until creative edit dispatches`,
|
|
530886
|
+
content: `REG-61 COERCION BLOCK — rejected '${tc.name}' at turn ${turn}; gate stays active until creative edit dispatches${_dbgLoop.detected ? `; REG-66 debug-loop variant (${_dbgLoop.kind} "${_debugLoopSampleSafe.slice(0, 60)}" ${_dbgLoop.count}×)` : ""}`,
|
|
530781
530887
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
530782
530888
|
});
|
|
530783
530889
|
this._tagSyntheticFailure({
|
|
530784
530890
|
mode: "step_repetition",
|
|
530785
|
-
rationale: `REG-61 perpetual coercion block on '${tc.name}' — agent ignored FIRST-EDIT NUDGE`
|
|
530891
|
+
rationale: `REG-61 perpetual coercion block on '${tc.name}' — agent ignored FIRST-EDIT NUDGE${_dbgLoop.detected ? " (debug-loop variant)" : ""}`
|
|
530786
530892
|
});
|
|
530787
530893
|
return { tc, output: reg61BlockMsg };
|
|
530788
530894
|
}
|
|
@@ -585450,6 +585556,7 @@ async function tryRouteV1(ctx3) {
|
|
|
585450
585556
|
const m2 = /^\/v1\/keys\/([^/]+)$/.exec(pathname);
|
|
585451
585557
|
if (m2 && method === "DELETE") return handleRevokeKey(ctx3, decodeURIComponent(m2[1]));
|
|
585452
585558
|
}
|
|
585559
|
+
if (pathname === "/v1/share/generate" && method === "POST") return handleGenerateShare(ctx3);
|
|
585453
585560
|
if (pathname === "/v1/tools" && method === "GET") {
|
|
585454
585561
|
return handleListTools(ctx3);
|
|
585455
585562
|
}
|
|
@@ -586591,6 +586698,75 @@ async function handleMintKey(ctx3) {
|
|
|
586591
586698
|
}
|
|
586592
586699
|
return true;
|
|
586593
586700
|
}
|
|
586701
|
+
async function handleGenerateShare(ctx3) {
|
|
586702
|
+
const { req: req2, res, requestId } = ctx3;
|
|
586703
|
+
const reqAuth = req2;
|
|
586704
|
+
if (reqAuth._authScope !== "admin") {
|
|
586705
|
+
sendProblem(res, problemDetails({
|
|
586706
|
+
type: P.forbidden,
|
|
586707
|
+
status: 403,
|
|
586708
|
+
title: "Admin scope required",
|
|
586709
|
+
detail: "Generating a share URL mints a runtime key, which requires 'admin' scope.",
|
|
586710
|
+
instance: requestId
|
|
586711
|
+
}));
|
|
586712
|
+
return true;
|
|
586713
|
+
}
|
|
586714
|
+
try {
|
|
586715
|
+
const body = await parseJsonBodyStrict(req2).catch(() => null) ?? {};
|
|
586716
|
+
const label = typeof body["label"] === "string" && body["label"] ? String(body["label"]).slice(0, 100) : `share-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
586717
|
+
const scope = typeof body["scope"] === "string" && ["read", "run", "admin"].includes(body["scope"]) ? body["scope"] : "run";
|
|
586718
|
+
const owner = `share:${label}`;
|
|
586719
|
+
const { mintKey: mintKey2 } = await Promise.resolve().then(() => (init_runtime_keys(), runtime_keys_exports));
|
|
586720
|
+
const rec = mintKey2({
|
|
586721
|
+
scope,
|
|
586722
|
+
owner,
|
|
586723
|
+
profile: null
|
|
586724
|
+
});
|
|
586725
|
+
const hostHeader = String(req2.headers["host"] || "").trim();
|
|
586726
|
+
const fallbackHost = process.env["OA_HOST"] || "127.0.0.1:11435";
|
|
586727
|
+
let hostPort = hostHeader || fallbackHost;
|
|
586728
|
+
hostPort = hostPort.replace(/^https?:\/\//i, "");
|
|
586729
|
+
const colonIdx = hostPort.lastIndexOf(":");
|
|
586730
|
+
const host = colonIdx > 0 ? hostPort.slice(0, colonIdx) : hostPort;
|
|
586731
|
+
const portStr = colonIdx > 0 ? hostPort.slice(colonIdx + 1) : "11435";
|
|
586732
|
+
const port = parseInt(portStr, 10) || 11435;
|
|
586733
|
+
const fullKey = rec.key || "";
|
|
586734
|
+
const keyPrefix2 = fullKey.slice(0, 12);
|
|
586735
|
+
if (!fullKey) {
|
|
586736
|
+
sendProblem(res, problemDetails({
|
|
586737
|
+
type: P.internalError,
|
|
586738
|
+
status: 500,
|
|
586739
|
+
title: "Key generation succeeded but no secret returned",
|
|
586740
|
+
detail: "mintKey() did not include the full secret in its response.",
|
|
586741
|
+
instance: requestId
|
|
586742
|
+
}));
|
|
586743
|
+
return true;
|
|
586744
|
+
}
|
|
586745
|
+
const scheme = String(req2.headers["x-forwarded-proto"] || (req2.socket?.encrypted ? "https" : "http"));
|
|
586746
|
+
const shareUrl = `oa-share://${hostPort}#${fullKey}`;
|
|
586747
|
+
const plainUrl = `${scheme}://${hostPort}/?oa-key=${encodeURIComponent(fullKey)}&oa-share-label=${encodeURIComponent(label)}`;
|
|
586748
|
+
sendJson(res, 201, {
|
|
586749
|
+
shareUrl,
|
|
586750
|
+
plainUrl,
|
|
586751
|
+
host,
|
|
586752
|
+
port,
|
|
586753
|
+
key: fullKey,
|
|
586754
|
+
keyPrefix: keyPrefix2,
|
|
586755
|
+
label,
|
|
586756
|
+
issuedAt: rec.created || (/* @__PURE__ */ new Date()).toISOString(),
|
|
586757
|
+
_note: "This is the ONLY response that contains the full key. Hand off the URL now."
|
|
586758
|
+
});
|
|
586759
|
+
} catch (err) {
|
|
586760
|
+
sendProblem(res, problemDetails({
|
|
586761
|
+
type: P.internalError,
|
|
586762
|
+
status: 500,
|
|
586763
|
+
title: "Share URL generation failed",
|
|
586764
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
586765
|
+
instance: requestId
|
|
586766
|
+
}));
|
|
586767
|
+
}
|
|
586768
|
+
return true;
|
|
586769
|
+
}
|
|
586594
586770
|
async function handleRevokeKey(ctx3, prefix) {
|
|
586595
586771
|
const { req: req2, res, requestId } = ctx3;
|
|
586596
586772
|
const reqAuth = req2;
|
|
@@ -588907,7 +589083,7 @@ body { display:flex; flex-direction:column; height:100vh; margin:0; overflow:hid
|
|
|
588907
589083
|
<span id="sidebar-status-dot" style="width:8px;height:8px;border-radius:50%;background:var(--color-fg-faint);flex-shrink:0" title="Backend connection"></span>
|
|
588908
589084
|
<span id="sidebar-status-text" style="flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">connecting...</span>
|
|
588909
589085
|
<button id="sidebar-update-btn" onclick="doUpdate()" style="display:none;background:var(--color-warning);border:none;color:#000;padding:2px 6px;border-radius:var(--radius-sm);font-size:0.6rem;cursor:pointer;font-weight:600">update</button>
|
|
588910
|
-
<button id="sidebar-key-btn" onclick="
|
|
589086
|
+
<button id="sidebar-key-btn" onclick="openKeyModal()" title="Set API key / share access" style="background:transparent;border:1px solid var(--color-border);color:var(--color-fg-muted);padding:2px 6px;border-radius:var(--radius-sm);font-size:0.6rem;cursor:pointer">key</button>
|
|
588911
589087
|
</div>
|
|
588912
589088
|
|
|
588913
589089
|
<!-- Resize handle -->
|
|
@@ -589239,14 +589415,21 @@ body { display:flex; flex-direction:column; height:100vh; margin:0; overflow:hid
|
|
|
589239
589415
|
</div>
|
|
589240
589416
|
|
|
589241
589417
|
<div id="key-modal">
|
|
589242
|
-
<form class="modal" onsubmit="event.preventDefault(); saveKey();" autocomplete="off">
|
|
589243
|
-
<h3>API Key</h3>
|
|
589244
|
-
<
|
|
589245
|
-
|
|
589418
|
+
<form class="modal" onsubmit="event.preventDefault(); saveKey();" autocomplete="off" style="min-width:480px">
|
|
589419
|
+
<h3>API Key & Sharing</h3>
|
|
589420
|
+
<div style="position:relative">
|
|
589421
|
+
<input id="key-input" type="password" placeholder="Bearer token (leave empty if auth disabled, or paste an oa-share:// URL to connect remote)" autocomplete="new-password" oninput="onKeyInputChange(this.value)" onfocus="showRecentKeysDropdown()" onblur="setTimeout(hideRecentKeysDropdown, 150)">
|
|
589422
|
+
<div id="key-recent-dropdown" style="display:none;position:absolute;top:100%;left:0;right:0;background:var(--color-bg-elevated);border:1px solid var(--color-border);border-radius:var(--radius-sm);max-height:180px;overflow-y:auto;z-index:10;font-size:0.78rem;margin-top:2px"></div>
|
|
589423
|
+
</div>
|
|
589424
|
+
<p id="key-input-hint" style="font-size:0.7rem;color:var(--color-fg-muted);margin:4px 0 8px">Tip: paste an <code>oa-share://host:port#key</code> URL or <code>http://host:port/?oa-key=…</code> to connect to a remote OA instance.</p>
|
|
589425
|
+
<div style="display:flex;gap:6px;flex-wrap:wrap">
|
|
589246
589426
|
<button type="submit">save</button>
|
|
589247
589427
|
<button type="button" onclick="clearKey()">clear</button>
|
|
589428
|
+
<button type="button" onclick="generateShareUrl()" title="Generate a share URL that lets a remote OA instance connect to this one">share access</button>
|
|
589248
589429
|
<button type="button" onclick="closeKeyModal()">cancel</button>
|
|
589249
589430
|
</div>
|
|
589431
|
+
<div id="share-result" style="display:none;margin-top:14px;padding:10px;background:var(--color-bg-elevated);border:1px solid var(--color-border);border-radius:var(--radius-sm);font-size:0.78rem"></div>
|
|
589432
|
+
<div id="remote-state" style="display:none;margin-top:14px;padding:10px;background:var(--color-bg-elevated);border:1px solid var(--color-accent);border-radius:var(--radius-sm);font-size:0.78rem"></div>
|
|
589250
589433
|
</form>
|
|
589251
589434
|
</div>
|
|
589252
589435
|
|
|
@@ -590368,6 +590551,15 @@ async function sendMessage() {
|
|
|
590368
590551
|
messageWithContext = filesBlock + text;
|
|
590369
590552
|
}
|
|
590370
590553
|
|
|
590554
|
+
// FRESH-SESSION: pull the one-shot fresh flag the newChatSession()
|
|
590555
|
+
// handler set. Consumed (cleared) here so subsequent sends within
|
|
590556
|
+
// this same session don't keep claiming "fresh" and miss legitimate
|
|
590557
|
+
// mid-session handoff.
|
|
590558
|
+
let _freshPending = false;
|
|
590559
|
+
try {
|
|
590560
|
+
_freshPending = localStorage.getItem('oa.freshSessionPending') === '1';
|
|
590561
|
+
if (_freshPending) localStorage.removeItem('oa.freshSessionPending');
|
|
590562
|
+
} catch {}
|
|
590371
590563
|
const body = {
|
|
590372
590564
|
session_id: chatSessionId,
|
|
590373
590565
|
model: modelSelect.value,
|
|
@@ -590377,6 +590569,10 @@ async function sendMessage() {
|
|
|
590377
590569
|
// Pass the user-selected workspace as working_directory so the
|
|
590378
590570
|
// agent subprocess operates in the right cwd.
|
|
590379
590571
|
...(chatWorkingDir ? { working_directory: chatWorkingDir } : {}),
|
|
590572
|
+
// FRESH-SESSION: tell the daemon to skip cross-task handoff
|
|
590573
|
+
// injection on this turn. Daemon plumbs it to OA_FRESH_SESSION=1
|
|
590574
|
+
// for the agent subprocess; runner's handoff block respects it.
|
|
590575
|
+
...(_freshPending ? { fresh: true } : {}),
|
|
590380
590576
|
};
|
|
590381
590577
|
|
|
590382
590578
|
const response = await fetch('/v1/chat', {
|
|
@@ -590684,8 +590880,33 @@ document.getElementById('key-btn').onclick = () => {
|
|
|
590684
590880
|
document.getElementById('key-input').value = apiKey;
|
|
590685
590881
|
};
|
|
590686
590882
|
function saveKey() {
|
|
590687
|
-
|
|
590883
|
+
const raw = document.getElementById('key-input').value || '';
|
|
590884
|
+
// SHARE: detect oa-share://host:port#key OR http(s)://host:port/?oa-key=...
|
|
590885
|
+
const parsed = parseShareInput(raw);
|
|
590886
|
+
if (parsed) {
|
|
590887
|
+
// Pasting a share URL → switch into REMOTE mode and reload page
|
|
590888
|
+
// against the remote origin with the key in localStorage.
|
|
590889
|
+
saveRecentKey({ key: parsed.key, host: parsed.host, label: parsed.label || ('remote ' + parsed.host) });
|
|
590890
|
+
try {
|
|
590891
|
+
localStorage.setItem('oa.remoteHost', parsed.host);
|
|
590892
|
+
localStorage.setItem('oa.remoteScheme', parsed.scheme || 'http');
|
|
590893
|
+
localStorage.setItem('oa-api-key', parsed.key);
|
|
590894
|
+
} catch {}
|
|
590895
|
+
// Redirect to the remote host's UI with the key carried in localStorage
|
|
590896
|
+
// (the remote origin uses its own localStorage so we set on first
|
|
590897
|
+
// load there). We open the remote UI in a new tab to preserve the
|
|
590898
|
+
// local session.
|
|
590899
|
+
const remoteUrl = (parsed.scheme || 'http') + '://' + parsed.host + '/?oa-key=' + encodeURIComponent(parsed.key) + '&oa-share-label=' + encodeURIComponent(parsed.label || '');
|
|
590900
|
+
window.open(remoteUrl, '_blank');
|
|
590901
|
+
closeKeyModal();
|
|
590902
|
+
return;
|
|
590903
|
+
}
|
|
590904
|
+
apiKey = raw;
|
|
590688
590905
|
localStorage.setItem('oa-api-key', apiKey);
|
|
590906
|
+
// Track this key in the recent-keys list so future paste autocompletes it.
|
|
590907
|
+
if (apiKey) {
|
|
590908
|
+
saveRecentKey({ key: apiKey, host: location.host, label: 'local ' + location.host });
|
|
590909
|
+
}
|
|
590689
590910
|
closeKeyModal();
|
|
590690
590911
|
loadModels();
|
|
590691
590912
|
}
|
|
@@ -590698,8 +590919,296 @@ function clearKey() {
|
|
|
590698
590919
|
}
|
|
590699
590920
|
function closeKeyModal() {
|
|
590700
590921
|
document.getElementById('key-modal').classList.remove('visible');
|
|
590922
|
+
const sr = document.getElementById('share-result'); if (sr) sr.style.display = 'none';
|
|
590923
|
+
}
|
|
590924
|
+
function openKeyModal() {
|
|
590925
|
+
document.getElementById('key-modal').classList.add('visible');
|
|
590926
|
+
document.getElementById('key-input').value = apiKey;
|
|
590927
|
+
// Refresh the remote-state hint based on current localStorage.
|
|
590928
|
+
refreshKeyModalRemoteState();
|
|
590929
|
+
}
|
|
590930
|
+
window.openKeyModal = openKeyModal;
|
|
590931
|
+
|
|
590932
|
+
// ─── Share URL parsing ─────────────────────────────────────────────────
|
|
590933
|
+
// Accepts BOTH:
|
|
590934
|
+
// oa-share://[peerId@]host:port#key
|
|
590935
|
+
// http(s)://host:port/?oa-key=KEY[&oa-share-label=LABEL]
|
|
590936
|
+
// Returns { host, key, scheme?, label? } or null when the input is a plain key.
|
|
590937
|
+
function parseShareInput(raw) {
|
|
590938
|
+
const v = String(raw || '').trim();
|
|
590939
|
+
if (!v) return null;
|
|
590940
|
+
// oa-share scheme — use a custom parser since URL() doesn't always honor it.
|
|
590941
|
+
if (v.toLowerCase().startsWith('oa-share://')) {
|
|
590942
|
+
const after = v.slice('oa-share://'.length);
|
|
590943
|
+
const hashIdx = after.indexOf('#');
|
|
590944
|
+
if (hashIdx < 0) return null;
|
|
590945
|
+
const hostPart = after.slice(0, hashIdx);
|
|
590946
|
+
const key = after.slice(hashIdx + 1);
|
|
590947
|
+
if (!hostPart || !key) return null;
|
|
590948
|
+
// Strip optional peerId@ prefix (forward-compat for libp2p tunneling).
|
|
590949
|
+
const atIdx = hostPart.indexOf('@');
|
|
590950
|
+
const host = atIdx >= 0 ? hostPart.slice(atIdx + 1) : hostPart;
|
|
590951
|
+
return { host, key, scheme: 'http' };
|
|
590952
|
+
}
|
|
590953
|
+
// http(s) URL with ?oa-key=...
|
|
590954
|
+
if (/^https?:\\/\\//i.test(v)) {
|
|
590955
|
+
try {
|
|
590956
|
+
const u = new URL(v);
|
|
590957
|
+
const k = u.searchParams.get('oa-key');
|
|
590958
|
+
if (!k) return null;
|
|
590959
|
+
const label = u.searchParams.get('oa-share-label') || '';
|
|
590960
|
+
return { host: u.host, key: k, scheme: u.protocol.replace(':', ''), label };
|
|
590961
|
+
} catch { return null; }
|
|
590962
|
+
}
|
|
590963
|
+
return null;
|
|
590964
|
+
}
|
|
590965
|
+
|
|
590966
|
+
function onKeyInputChange(v) {
|
|
590967
|
+
const hint = document.getElementById('key-input-hint');
|
|
590968
|
+
if (!hint) return;
|
|
590969
|
+
const parsed = parseShareInput(v);
|
|
590970
|
+
if (parsed) {
|
|
590971
|
+
hint.innerHTML = '✓ detected share URL — host <code>' + escapeHtml(parsed.host) + '</code>; clicking save will open the remote UI with this key';
|
|
590972
|
+
hint.style.color = 'var(--color-success)';
|
|
590973
|
+
} else {
|
|
590974
|
+
hint.innerHTML = 'Tip: paste an <code>oa-share://host:port#key</code> URL or <code>http://host:port/?oa-key=…</code> to connect to a remote OA instance.';
|
|
590975
|
+
hint.style.color = 'var(--color-fg-muted)';
|
|
590976
|
+
}
|
|
590977
|
+
showRecentKeysDropdown();
|
|
590978
|
+
}
|
|
590979
|
+
|
|
590980
|
+
// Tiny HTML escape — same as the one used in connections settings.
|
|
590981
|
+
function escapeHtml(s) {
|
|
590982
|
+
return String(s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
590701
590983
|
}
|
|
590702
590984
|
|
|
590985
|
+
// ─── Recent keys (localStorage history with autocomplete dropdown) ──
|
|
590986
|
+
const _OA_RECENT_KEYS_LS = 'oa.recentKeys';
|
|
590987
|
+
const _OA_RECENT_KEYS_MAX = 10;
|
|
590988
|
+
function loadRecentKeys() {
|
|
590989
|
+
try {
|
|
590990
|
+
const raw = localStorage.getItem(_OA_RECENT_KEYS_LS);
|
|
590991
|
+
if (!raw) return [];
|
|
590992
|
+
const parsed = JSON.parse(raw);
|
|
590993
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
590994
|
+
} catch { return []; }
|
|
590995
|
+
}
|
|
590996
|
+
function saveRecentKey(rec) {
|
|
590997
|
+
if (!rec || !rec.key) return;
|
|
590998
|
+
let list = loadRecentKeys();
|
|
590999
|
+
// Move existing entry with same key to top; otherwise prepend.
|
|
591000
|
+
list = list.filter(r => r.key !== rec.key);
|
|
591001
|
+
list.unshift({ ...rec, lastUsed: new Date().toISOString() });
|
|
591002
|
+
if (list.length > _OA_RECENT_KEYS_MAX) list = list.slice(0, _OA_RECENT_KEYS_MAX);
|
|
591003
|
+
try { localStorage.setItem(_OA_RECENT_KEYS_LS, JSON.stringify(list)); } catch {}
|
|
591004
|
+
}
|
|
591005
|
+
function deleteRecentKey(key) {
|
|
591006
|
+
let list = loadRecentKeys().filter(r => r.key !== key);
|
|
591007
|
+
try { localStorage.setItem(_OA_RECENT_KEYS_LS, JSON.stringify(list)); } catch {}
|
|
591008
|
+
showRecentKeysDropdown();
|
|
591009
|
+
}
|
|
591010
|
+
window.deleteRecentKey = deleteRecentKey;
|
|
591011
|
+
|
|
591012
|
+
function showRecentKeysDropdown() {
|
|
591013
|
+
const dd = document.getElementById('key-recent-dropdown');
|
|
591014
|
+
if (!dd) return;
|
|
591015
|
+
const recents = loadRecentKeys();
|
|
591016
|
+
const inp = document.getElementById('key-input');
|
|
591017
|
+
const filter = (inp && inp.value || '').toLowerCase();
|
|
591018
|
+
const matches = recents.filter(r => {
|
|
591019
|
+
if (!filter) return true;
|
|
591020
|
+
return (r.key || '').toLowerCase().includes(filter)
|
|
591021
|
+
|| (r.host || '').toLowerCase().includes(filter)
|
|
591022
|
+
|| (r.label || '').toLowerCase().includes(filter);
|
|
591023
|
+
});
|
|
591024
|
+
if (matches.length === 0) { dd.style.display = 'none'; return; }
|
|
591025
|
+
dd.innerHTML = matches.map(r => {
|
|
591026
|
+
const k = String(r.key || '');
|
|
591027
|
+
const masked = k.length > 12 ? k.slice(0, 4) + '…' + k.slice(-4) : k;
|
|
591028
|
+
const host = escapeHtml(r.host || '');
|
|
591029
|
+
const label = escapeHtml(r.label || '');
|
|
591030
|
+
const keySafe = k.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, "\\\\'");
|
|
591031
|
+
return '<div class="oa-rec-key" style="display:flex;align-items:center;gap:8px;padding:6px 10px;cursor:pointer;border-bottom:1px solid var(--color-border)" '
|
|
591032
|
+
+ 'onclick="useRecentKey(\\'' + keySafe + '\\')" '
|
|
591033
|
+
+ 'onmouseover="this.style.background=\\'var(--color-bg-hover)\\'" '
|
|
591034
|
+
+ 'onmouseout="this.style.background=\\'transparent\\'">'
|
|
591035
|
+
+ '<span style="flex:1;font-family:var(--font-mono);font-size:0.78rem"><span style="color:var(--color-fg-muted)">' + (host || 'local') + '</span> · <code>' + masked + '</code></span>'
|
|
591036
|
+
+ '<span style="font-size:0.7rem;color:var(--color-fg-muted)">' + label + '</span>'
|
|
591037
|
+
+ '<button type="button" onclick="event.stopPropagation();deleteRecentKey(\\'' + keySafe + '\\')" '
|
|
591038
|
+
+ 'title="Forget this key" '
|
|
591039
|
+
+ 'style="background:transparent;border:none;color:var(--color-fg-muted);cursor:pointer;font-size:1rem;padding:0 4px">×</button>'
|
|
591040
|
+
+ '</div>';
|
|
591041
|
+
}).join('');
|
|
591042
|
+
dd.style.display = 'block';
|
|
591043
|
+
}
|
|
591044
|
+
function hideRecentKeysDropdown() {
|
|
591045
|
+
const dd = document.getElementById('key-recent-dropdown');
|
|
591046
|
+
if (dd) dd.style.display = 'none';
|
|
591047
|
+
}
|
|
591048
|
+
function useRecentKey(k) {
|
|
591049
|
+
const inp = document.getElementById('key-input');
|
|
591050
|
+
if (inp) {
|
|
591051
|
+
inp.value = k;
|
|
591052
|
+
inp.focus();
|
|
591053
|
+
onKeyInputChange(k);
|
|
591054
|
+
}
|
|
591055
|
+
hideRecentKeysDropdown();
|
|
591056
|
+
}
|
|
591057
|
+
window.useRecentKey = useRecentKey;
|
|
591058
|
+
|
|
591059
|
+
// ─── Generate share URL ────────────────────────────────────────────────
|
|
591060
|
+
async function generateShareUrl() {
|
|
591061
|
+
const out = document.getElementById('share-result');
|
|
591062
|
+
if (!out) return;
|
|
591063
|
+
out.style.display = 'block';
|
|
591064
|
+
out.innerHTML = '<div style="color:var(--color-fg-muted)">generating…</div>';
|
|
591065
|
+
try {
|
|
591066
|
+
const r = await fetch('/v1/share/generate', {
|
|
591067
|
+
method: 'POST',
|
|
591068
|
+
headers: { ...headers(), 'Content-Type': 'application/json' },
|
|
591069
|
+
body: JSON.stringify({ scope: 'run' }),
|
|
591070
|
+
});
|
|
591071
|
+
if (r.status === 403) {
|
|
591072
|
+
out.innerHTML = '<div style="color:var(--color-error)">✗ admin scope required — your current key does not have permission to mint share URLs. Set an admin-scope key first.</div>';
|
|
591073
|
+
return;
|
|
591074
|
+
}
|
|
591075
|
+
if (r.status >= 400) {
|
|
591076
|
+
const err = await r.json().catch(() => ({}));
|
|
591077
|
+
out.innerHTML = '<div style="color:var(--color-error)">✗ HTTP ' + r.status + (err.detail ? ' — ' + escapeHtml(err.detail) : '') + '</div>';
|
|
591078
|
+
return;
|
|
591079
|
+
}
|
|
591080
|
+
const j = await r.json();
|
|
591081
|
+
const safeShare = escapeHtml(j.shareUrl || '');
|
|
591082
|
+
const safePlain = escapeHtml(j.plainUrl || '');
|
|
591083
|
+
out.innerHTML =
|
|
591084
|
+
'<div style="font-weight:500;margin-bottom:6px;color:var(--color-success)">✓ Share URL generated — hand off to remote</div>' +
|
|
591085
|
+
'<div style="margin:6px 0">' +
|
|
591086
|
+
'<label style="display:block;font-size:0.7rem;color:var(--color-fg-muted);margin-bottom:2px">oa-share URL (compact, recommended for OA-to-OA)</label>' +
|
|
591087
|
+
'<div style="display:flex;gap:6px;align-items:center">' +
|
|
591088
|
+
'<input readonly value="' + safeShare + '" style="flex:1;background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:5px 8px;border-radius:var(--radius-sm);font-family:var(--font-mono);font-size:0.74rem">' +
|
|
591089
|
+
'<button type="button" onclick="copyShareUrl(\\'oa-share\\')" style="font-size:0.74rem">copy</button>' +
|
|
591090
|
+
'</div>' +
|
|
591091
|
+
'</div>' +
|
|
591092
|
+
'<div style="margin:6px 0">' +
|
|
591093
|
+
'<label style="display:block;font-size:0.7rem;color:var(--color-fg-muted);margin-bottom:2px">plain URL (works in any browser, no scheme handler needed)</label>' +
|
|
591094
|
+
'<div style="display:flex;gap:6px;align-items:center">' +
|
|
591095
|
+
'<input readonly value="' + safePlain + '" style="flex:1;background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:5px 8px;border-radius:var(--radius-sm);font-family:var(--font-mono);font-size:0.74rem">' +
|
|
591096
|
+
'<button type="button" onclick="copyShareUrl(\\'plain\\')" style="font-size:0.74rem">copy</button>' +
|
|
591097
|
+
'</div>' +
|
|
591098
|
+
'</div>' +
|
|
591099
|
+
'<p style="font-size:0.68rem;color:var(--color-fg-muted);margin:6px 0 0">Note: this URL contains the full secret. Anyone with it can use this OA. To revoke: open Advanced settings → keys → revoke the prefix <code>' + escapeHtml(j.keyPrefix || '') + '</code>.</p>';
|
|
591100
|
+
// Stash for the copy buttons + add to recent.
|
|
591101
|
+
window.__oaLastShareUrl = j.shareUrl;
|
|
591102
|
+
window.__oaLastPlainUrl = j.plainUrl;
|
|
591103
|
+
saveRecentKey({ key: j.key, host: j.host + ':' + j.port, label: j.label || ('shared ' + j.host) });
|
|
591104
|
+
} catch (e) {
|
|
591105
|
+
out.innerHTML = '<div style="color:var(--color-error)">✗ ' + escapeHtml(e && e.message ? e.message : String(e)) + '</div>';
|
|
591106
|
+
}
|
|
591107
|
+
}
|
|
591108
|
+
function copyShareUrl(which) {
|
|
591109
|
+
const v = which === 'plain' ? window.__oaLastPlainUrl : window.__oaLastShareUrl;
|
|
591110
|
+
if (!v) return;
|
|
591111
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
591112
|
+
navigator.clipboard.writeText(v).then(
|
|
591113
|
+
() => { /* ok */ },
|
|
591114
|
+
() => { fallbackCopy(v); }
|
|
591115
|
+
);
|
|
591116
|
+
} else {
|
|
591117
|
+
fallbackCopy(v);
|
|
591118
|
+
}
|
|
591119
|
+
}
|
|
591120
|
+
function fallbackCopy(v) {
|
|
591121
|
+
const ta = document.createElement('textarea');
|
|
591122
|
+
ta.value = v; document.body.appendChild(ta); ta.select();
|
|
591123
|
+
try { document.execCommand('copy'); } catch {}
|
|
591124
|
+
document.body.removeChild(ta);
|
|
591125
|
+
}
|
|
591126
|
+
window.generateShareUrl = generateShareUrl;
|
|
591127
|
+
window.copyShareUrl = copyShareUrl;
|
|
591128
|
+
|
|
591129
|
+
// ─── Remote-state helpers (visual indicator on the key button) ───────
|
|
591130
|
+
// When localStorage carries oa.remoteHost we are in REMOTE mode: the
|
|
591131
|
+
// key was loaded from an oa-share URL or via on-load ?oa-key=. The key
|
|
591132
|
+
// button shows a "remote" badge and clicking expands to show host+key
|
|
591133
|
+
// inline with a "close connection" button that severs and shuffles the
|
|
591134
|
+
// key into recents.
|
|
591135
|
+
function refreshKeyModalRemoteState() {
|
|
591136
|
+
const remote = (function() {
|
|
591137
|
+
try { return localStorage.getItem('oa.remoteHost') || ''; } catch { return ''; }
|
|
591138
|
+
})();
|
|
591139
|
+
const stateBox = document.getElementById('remote-state');
|
|
591140
|
+
const btn = document.getElementById('sidebar-key-btn');
|
|
591141
|
+
if (remote && btn) {
|
|
591142
|
+
btn.textContent = 'remote';
|
|
591143
|
+
btn.style.background = 'var(--color-accent)';
|
|
591144
|
+
btn.style.color = '#fff';
|
|
591145
|
+
btn.style.borderColor = 'var(--color-accent)';
|
|
591146
|
+
btn.title = 'connected to remote ' + remote + ' — click to view / disconnect';
|
|
591147
|
+
} else if (btn) {
|
|
591148
|
+
btn.textContent = 'key';
|
|
591149
|
+
btn.style.background = 'transparent';
|
|
591150
|
+
btn.style.color = 'var(--color-fg-muted)';
|
|
591151
|
+
btn.style.borderColor = 'var(--color-border)';
|
|
591152
|
+
btn.title = 'set API key / share access';
|
|
591153
|
+
}
|
|
591154
|
+
if (!stateBox) return;
|
|
591155
|
+
if (remote) {
|
|
591156
|
+
const safeRemote = escapeHtml(remote);
|
|
591157
|
+
stateBox.style.display = 'block';
|
|
591158
|
+
stateBox.innerHTML =
|
|
591159
|
+
'<div style="font-weight:500;color:var(--color-accent)">REMOTE connection active</div>' +
|
|
591160
|
+
'<div style="margin-top:4px;font-size:0.74rem">host <code>' + safeRemote + '</code></div>' +
|
|
591161
|
+
'<div style="margin-top:8px"><button type="button" onclick="closeRemoteConnection()" style="background:var(--color-error);color:#fff;border:none;padding:4px 10px;border-radius:var(--radius-sm);cursor:pointer;font-size:0.74rem">close connection</button></div>';
|
|
591162
|
+
} else {
|
|
591163
|
+
stateBox.style.display = 'none';
|
|
591164
|
+
}
|
|
591165
|
+
}
|
|
591166
|
+
function closeRemoteConnection() {
|
|
591167
|
+
// Move current remote key into recents BEFORE clearing.
|
|
591168
|
+
let savedKey = '';
|
|
591169
|
+
let savedHost = '';
|
|
591170
|
+
try {
|
|
591171
|
+
savedKey = localStorage.getItem('oa-api-key') || '';
|
|
591172
|
+
savedHost = localStorage.getItem('oa.remoteHost') || '';
|
|
591173
|
+
} catch {}
|
|
591174
|
+
if (savedKey) {
|
|
591175
|
+
saveRecentKey({ key: savedKey, host: savedHost, label: 'recent remote ' + savedHost });
|
|
591176
|
+
}
|
|
591177
|
+
try {
|
|
591178
|
+
localStorage.removeItem('oa.remoteHost');
|
|
591179
|
+
localStorage.removeItem('oa.remoteScheme');
|
|
591180
|
+
localStorage.removeItem('oa-api-key');
|
|
591181
|
+
} catch {}
|
|
591182
|
+
apiKey = '';
|
|
591183
|
+
refreshKeyModalRemoteState();
|
|
591184
|
+
// Reload to drop any cached state from the remote target.
|
|
591185
|
+
location.reload();
|
|
591186
|
+
}
|
|
591187
|
+
window.closeRemoteConnection = closeRemoteConnection;
|
|
591188
|
+
|
|
591189
|
+
// ─── On-load: ?oa-key=... pickup ───────────────────────────────────────
|
|
591190
|
+
// When the page loads with an oa-key query param (i.e. someone clicked
|
|
591191
|
+
// a share URL), capture the key into localStorage, mark this as a
|
|
591192
|
+
// remote session (host = this origin), and clean the URL.
|
|
591193
|
+
(function pickupShareKeyFromUrl() {
|
|
591194
|
+
try {
|
|
591195
|
+
const u = new URL(location.href);
|
|
591196
|
+
const k = u.searchParams.get('oa-key');
|
|
591197
|
+
if (!k) return;
|
|
591198
|
+
const label = u.searchParams.get('oa-share-label') || '';
|
|
591199
|
+
localStorage.setItem('oa-api-key', k);
|
|
591200
|
+
// Origin-based remote: when the page loaded from a different host
|
|
591201
|
+
// than the user's "home" OA, that's a remote session by definition.
|
|
591202
|
+
localStorage.setItem('oa.remoteHost', location.host);
|
|
591203
|
+
localStorage.setItem('oa.remoteScheme', location.protocol.replace(':', ''));
|
|
591204
|
+
saveRecentKey({ key: k, host: location.host, label: label || ('remote ' + location.host) });
|
|
591205
|
+
// Strip the key from the URL so it isn't visible / browser-history'd.
|
|
591206
|
+
u.searchParams.delete('oa-key');
|
|
591207
|
+
u.searchParams.delete('oa-share-label');
|
|
591208
|
+
history.replaceState({}, '', u.pathname + (u.search || '') + (u.hash || ''));
|
|
591209
|
+
} catch {}
|
|
591210
|
+
})();
|
|
591211
|
+
|
|
590703
591212
|
// Tab switching
|
|
590704
591213
|
const allPanels = ['chat-container','agent-panel','jobs-panel','config-panel','activity-panel','projects-panel','voice-panel'];
|
|
590705
591214
|
function switchTab(tab) {
|
|
@@ -591432,6 +591941,11 @@ function newChatSession() {
|
|
|
591432
591941
|
switchSession('');
|
|
591433
591942
|
const sel = document.getElementById('chat-session-select');
|
|
591434
591943
|
if (sel) sel.value = '';
|
|
591944
|
+
// FRESH-SESSION: mark the next chat send as fresh so the daemon skips
|
|
591945
|
+
// cross-task handoff injection (which previously bled prior session
|
|
591946
|
+
// context into the new chat regardless of the browser's reset).
|
|
591947
|
+
// Cleared inside the chat send handler after one use.
|
|
591948
|
+
try { localStorage.setItem('oa.freshSessionPending', '1'); } catch {}
|
|
591435
591949
|
}
|
|
591436
591950
|
function deleteChatSession() {
|
|
591437
591951
|
if (!chatSessionId) return;
|
|
@@ -592295,6 +592809,7 @@ async function doUpdate() {
|
|
|
592295
592809
|
try { pollMetrics(); } catch {}
|
|
592296
592810
|
try { loadScheduled(); } catch {}
|
|
592297
592811
|
try { loadServices(); } catch {}
|
|
592812
|
+
try { refreshKeyModalRemoteState(); } catch {}
|
|
592298
592813
|
|
|
592299
592814
|
btn.textContent = 'updated v' + newVersion;
|
|
592300
592815
|
btn.style.background = '#1a3a1a';
|
|
@@ -593279,6 +593794,7 @@ $currentProject.subscribe((proj) => {
|
|
|
593279
593794
|
try { if (typeof updateSessionSelect === 'function') updateSessionSelect(); } catch {}
|
|
593280
593795
|
try { if (typeof updateAgentRunSelect === 'function') updateAgentRunSelect(); } catch {}
|
|
593281
593796
|
try { if (typeof restoreChatSession === 'function') restoreChatSession(); } catch {}
|
|
593797
|
+
try { if (typeof refreshKeyModalRemoteState === 'function') refreshKeyModalRemoteState(); } catch {}
|
|
593282
593798
|
});
|
|
593283
593799
|
|
|
593284
593800
|
// $selectedModel changes update the visible <select> if it's not
|
|
@@ -599758,6 +600274,15 @@ async function handleRequest(req2, res, ollamaUrl, verbose) {
|
|
|
599758
600274
|
});
|
|
599759
600275
|
return;
|
|
599760
600276
|
}
|
|
600277
|
+
if (pathname === "/favicon.ico" && method === "GET") {
|
|
600278
|
+
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect width="16" height="16" rx="3" fill="#b2920a"/><text x="50%" y="55%" font-size="11" font-family="monospace" font-weight="700" text-anchor="middle" dominant-baseline="middle" fill="#0b0b0b">oa</text></svg>';
|
|
600279
|
+
res.writeHead(200, {
|
|
600280
|
+
"Content-Type": "image/svg+xml",
|
|
600281
|
+
"Cache-Control": "public, max-age=86400"
|
|
600282
|
+
});
|
|
600283
|
+
res.end(svg);
|
|
600284
|
+
return;
|
|
600285
|
+
}
|
|
599761
600286
|
if (pathname === "/" && method === "GET" && req2.headers.accept?.includes("text/html")) {
|
|
599762
600287
|
res.writeHead(200, {
|
|
599763
600288
|
"Content-Type": "text/html; charset=utf-8",
|
|
@@ -600881,6 +601406,9 @@ ${historyLines}
|
|
|
600881
601406
|
runEnv["OA_RUN_USER"] = req2._authUser || "anonymous";
|
|
600882
601407
|
runEnv["OA_RUN_SCOPE"] = req2._authScope || "admin";
|
|
600883
601408
|
runEnv["OA_SESSION_ID"] = session.id;
|
|
601409
|
+
if (chatBody["fresh"] === true) {
|
|
601410
|
+
runEnv["OA_FRESH_SESSION"] = "1";
|
|
601411
|
+
}
|
|
600884
601412
|
runEnv["OLLAMA_HOST"] = currentCfg.backendUrl || process.env["OLLAMA_HOST"] || "http://127.0.0.1:11434";
|
|
600885
601413
|
if (currentCfg.apiKey) runEnv["OA_API_KEY_INHERIT"] = currentCfg.apiKey;
|
|
600886
601414
|
const child = spawn25(process.execPath, [oaBin, ...args], {
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-agents-ai",
|
|
3
|
-
"version": "0.187.
|
|
3
|
+
"version": "0.187.534",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "open-agents-ai",
|
|
9
|
-
"version": "0.187.
|
|
9
|
+
"version": "0.187.534",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "CC-BY-NC-4.0",
|
|
12
12
|
"dependencies": {
|
|
@@ -2192,9 +2192,9 @@
|
|
|
2192
2192
|
}
|
|
2193
2193
|
},
|
|
2194
2194
|
"node_modules/bare-url": {
|
|
2195
|
-
"version": "2.4.
|
|
2196
|
-
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.
|
|
2197
|
-
"integrity": "sha512
|
|
2195
|
+
"version": "2.4.3",
|
|
2196
|
+
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.3.tgz",
|
|
2197
|
+
"integrity": "sha512-Kccpc7ACfXaxfeInfqKcZtW4pT5YBn1mesc4sCsun6sRwtbJ4h+sNOaksUpYEJUKfN65YWC6Bw2OJEFiKxq8nQ==",
|
|
2198
2198
|
"license": "Apache-2.0",
|
|
2199
2199
|
"optional": true,
|
|
2200
2200
|
"dependencies": {
|
|
@@ -3132,9 +3132,9 @@
|
|
|
3132
3132
|
}
|
|
3133
3133
|
},
|
|
3134
3134
|
"node_modules/express-rate-limit": {
|
|
3135
|
-
"version": "8.
|
|
3136
|
-
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.
|
|
3137
|
-
"integrity": "sha512-
|
|
3135
|
+
"version": "8.5.0",
|
|
3136
|
+
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.0.tgz",
|
|
3137
|
+
"integrity": "sha512-XKhFohWaSBdVJNTi5TaHziqnPkv04I9UQV6q1Wy7Ui6GGQZVW12ojDFwqer14EvCXxjvPG0CyWXx7cAXpALB4Q==",
|
|
3138
3138
|
"license": "MIT",
|
|
3139
3139
|
"dependencies": {
|
|
3140
3140
|
"ip-address": "10.1.0"
|
|
@@ -3179,9 +3179,9 @@
|
|
|
3179
3179
|
}
|
|
3180
3180
|
},
|
|
3181
3181
|
"node_modules/fast-uri": {
|
|
3182
|
-
"version": "3.1.
|
|
3183
|
-
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.
|
|
3184
|
-
"integrity": "sha512-
|
|
3182
|
+
"version": "3.1.2",
|
|
3183
|
+
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
|
|
3184
|
+
"integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
|
|
3185
3185
|
"funding": [
|
|
3186
3186
|
{
|
|
3187
3187
|
"type": "github",
|
|
@@ -3638,9 +3638,9 @@
|
|
|
3638
3638
|
}
|
|
3639
3639
|
},
|
|
3640
3640
|
"node_modules/hono": {
|
|
3641
|
-
"version": "4.12.
|
|
3642
|
-
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.
|
|
3643
|
-
"integrity": "sha512-
|
|
3641
|
+
"version": "4.12.17",
|
|
3642
|
+
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.17.tgz",
|
|
3643
|
+
"integrity": "sha512-FbJJNb/XgX7YW0hX/V8w5oYLztKEsRLykCMZWt1WdLtsfjzMvmoqWBA4H4t5norinq8/rh20oiZYr+WSl4UzAQ==",
|
|
3644
3644
|
"license": "MIT",
|
|
3645
3645
|
"engines": {
|
|
3646
3646
|
"node": ">=16.9.0"
|
package/package.json
CHANGED