@inetafrica/open-claudia 3.5.19 → 3.5.20
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/CHANGELOG.md +10 -0
- package/bin/cli.js +6 -0
- package/bin/teach.js +78 -0
- package/bot.js +7 -0
- package/channels/kazee/adapter.js +30 -0
- package/core/bot-apps.js +133 -0
- package/core/browser-control.js +540 -0
- package/core/browser-lock.js +51 -0
- package/core/browser-teach.js +1012 -0
- package/core/browser-view.js +5 -2
- package/core/cards.js +4 -2
- package/core/system-prompt.js +11 -1
- package/core/tool-guard.js +343 -0
- package/package.json +2 -2
- package/test-provider-language.js +5 -1
- package/web.js +68 -0
package/core/browser-view.js
CHANGED
|
@@ -98,13 +98,15 @@ function listSessions({ dirs = socketDirs(), alive = processAlive } = {}) {
|
|
|
98
98
|
} catch (_) { /* try the next file */ }
|
|
99
99
|
}
|
|
100
100
|
seen.add(name);
|
|
101
|
-
|
|
101
|
+
// dir: the socket folder, so the bot's own agent-browser calls (core/browser-teach.js) reach
|
|
102
|
+
// this session and never start a new one. Internal like the port.
|
|
103
|
+
sessions.push({ name, port, page, updatedAt, dir });
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
106
|
return sessions.sort((a, b) => String(b.updatedAt || "").localeCompare(String(a.updatedAt || "")));
|
|
105
107
|
}
|
|
106
108
|
|
|
107
|
-
// Public view of a session: the stream port
|
|
109
|
+
// Public view of a session: the stream port and socket folder stay inside the bot.
|
|
108
110
|
function describeSessions(options) {
|
|
109
111
|
return listSessions(options).map(({ name, page, updatedAt }) => ({ name, page, updatedAt }));
|
|
110
112
|
}
|
|
@@ -189,6 +191,7 @@ function serveStream(res, sessionName, { fps, sessions, connect, keepaliveMs = K
|
|
|
189
191
|
module.exports = {
|
|
190
192
|
DEFAULT_FPS,
|
|
191
193
|
MAX_FPS,
|
|
194
|
+
SESSION_NAME,
|
|
192
195
|
clampFps,
|
|
193
196
|
describeSessions,
|
|
194
197
|
listSessions,
|
package/core/cards.js
CHANGED
|
@@ -255,7 +255,9 @@ function describeResponse(rec, payload, now = Date.now()) {
|
|
|
255
255
|
// ---------------------------------------------------------------------------
|
|
256
256
|
// Waking the conversation a card (or decision) came from
|
|
257
257
|
|
|
258
|
-
|
|
258
|
+
// `source` tags the job for job lists and audits: "card" for card answers,
|
|
259
|
+
// "browser" for the Browser app's hand-backs and Teach-me recordings.
|
|
260
|
+
function wakeOrigin(origin, prompt, label, { scheduler, delayMs = 1500, source = "card" } = {}) {
|
|
259
261
|
if (!origin || !origin.channelId || !origin.provider || !origin.project || !origin.canonicalUserId) {
|
|
260
262
|
throw new Error("the card's origin conversation is unknown");
|
|
261
263
|
}
|
|
@@ -282,7 +284,7 @@ function wakeOrigin(origin, prompt, label, { scheduler, delayMs = 1500 } = {}) {
|
|
|
282
284
|
},
|
|
283
285
|
prompt,
|
|
284
286
|
label: String(label).slice(0, 60),
|
|
285
|
-
source
|
|
287
|
+
source,
|
|
286
288
|
fireAt: Date.now() + delayMs,
|
|
287
289
|
});
|
|
288
290
|
}
|
package/core/system-prompt.js
CHANGED
|
@@ -239,6 +239,16 @@ Raw shell one-liners and heredocs are for probing and diagnosis only — never t
|
|
|
239
239
|
${listing}`;
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
// Kazee Chat's Browser app (core/bot-apps.js): only on a bot that offers it, so a bot without
|
|
243
|
+
// Agent Space is never told to send an Open Browser button that cannot open.
|
|
244
|
+
function buildBrowserAppBlock() {
|
|
245
|
+
let apps = null;
|
|
246
|
+
try { apps = require("./bot-apps").appsToRegister(process.env); } catch (_) { apps = null; }
|
|
247
|
+
if (!apps || !apps.some((app) => app.key === "browser")) return "";
|
|
248
|
+
return `
|
|
249
|
+
Kazee Chat Browser app: people in a direct chat with you can watch this browser live, take it over, and teach you a task (Teach me). When you need a person in the browser (a login, a captcha, a one-time code, a judgement call), send a card in their direct chat with an Open Browser button, say what you need, then wait: you are woken in that chat when they hand it back. For example \`open-claudia card send '{"v":1,"kind":"report","title":"I need you in the browser","body":"<what you need and why>","open":{"app":"browser","label":"Open Browser"}}'\`. While someone drives, your agent-browser commands other than get, snapshot and screenshot are refused. Never ask for passwords in chat.`;
|
|
250
|
+
}
|
|
251
|
+
|
|
242
252
|
function buildCoreInstructionsUncached() {
|
|
243
253
|
const soul = loadSoul();
|
|
244
254
|
// Gate on actual TTS capability, not local Whisper: hosted pods hear and
|
|
@@ -368,7 +378,7 @@ Core workflow — open, snapshot to get element refs, then act on those refs:
|
|
|
368
378
|
- \`agent-browser eval "<js>"\` — run JS in the page when no command fits.
|
|
369
379
|
- \`agent-browser close\` — end the session when done.
|
|
370
380
|
|
|
371
|
-
Notes: a background daemon persists between commands, so navigation state carries across calls in a turn. Prefer \`snapshot -i\` refs over guessing selectors. Keep output lean by acting on refs rather than dumping full HTML. This replaced the old Playwright MCP — do not look for a playwright tool
|
|
381
|
+
Notes: a background daemon persists between commands, so navigation state carries across calls in a turn. Prefer \`snapshot -i\` refs over guessing selectors. Keep output lean by acting on refs rather than dumping full HTML. This replaced the old Playwright MCP — do not look for a playwright tool.${buildBrowserAppBlock()}
|
|
372
382
|
|
|
373
383
|
If you tell the user "I'll check back in N minutes" or "I'll run this every morning", you MUST schedule it with one of the commands above in the same turn. Otherwise nothing happens.
|
|
374
384
|
|
package/core/tool-guard.js
CHANGED
|
@@ -659,6 +659,340 @@ function hookFailure(toolName, reason) {
|
|
|
659
659
|
};
|
|
660
660
|
}
|
|
661
661
|
|
|
662
|
+
// ── Browser app: the bot pauses while a person drives (bot apps v1, C7) ────
|
|
663
|
+
// While someone drives the bot's browser from Kazee Chat, the driver lock file
|
|
664
|
+
// (core/browser-lock.js) is live, and the bot's own agent-browser commands
|
|
665
|
+
// would fight their clicks. So an agent-browser call at a command position is
|
|
666
|
+
// refused, except the read-only ones: get, snapshot, screenshot, session list.
|
|
667
|
+
// A command position is the start of a command: the start of the line, or
|
|
668
|
+
// after ; & | && || ( $( a backtick or a newline, behind env assignments
|
|
669
|
+
// (FOO=1), shell words (if, then, do, !, {) and wrappers (npx, sudo, env,
|
|
670
|
+
// timeout, nice, xargs...). What a shell runs from a string is read the same
|
|
671
|
+
// way: bash/sh -c '...', eval, a heredoc or here-string fed to a shell, and
|
|
672
|
+
// $(...) or backticks, inside double quotes too. Any other string is data, so
|
|
673
|
+
// `echo "agent-browser click"`, `grep agent-browser`, a commit message or a
|
|
674
|
+
// heredoc written to a file never match. An expired lock never blocks, and any
|
|
675
|
+
// error here allows: this is a pause for a person's sake, not a security gate.
|
|
676
|
+
const AGENT_BROWSER_READ_ONLY = new Set(["get", "snapshot", "screenshot"]);
|
|
677
|
+
const AGENT_BROWSER_VALUE_FLAGS = new Set([
|
|
678
|
+
"--session", "--restore-save", "--restore-check-url", "--restore-check-text", "--restore-check-fn",
|
|
679
|
+
"--namespace", "--session-name", "--profile", "--state", "--headers", "--executable-path", "--extension",
|
|
680
|
+
"--init-script", "--enable", "--args", "--user-agent", "--proxy", "--proxy-bypass", "--ca-cert",
|
|
681
|
+
"--hide-scrollbars", "-p", "--provider", "--device", "--threshold", "--screenshot-dir", "--screenshot-quality",
|
|
682
|
+
"--screenshot-format", "--cdp", "--color-scheme", "--download-path", "--max-output", "--allowed-domains",
|
|
683
|
+
"--action-policy", "--confirm-actions", "--engine", "--input-mode", "--idle-timeout", "--model", "--config",
|
|
684
|
+
"--port", "--allowed-origins",
|
|
685
|
+
]);
|
|
686
|
+
// agent-browser, agent-browser.js, agent-browser@latest, agent-browser-linux-x64...
|
|
687
|
+
const AGENT_BROWSER_BIN = /^agent-browser(?:$|[.@-])/;
|
|
688
|
+
const SHELL_WORDS = new Set(["!", "{", "if", "then", "else", "elif", "do", "while", "until"]);
|
|
689
|
+
const SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh", "ash", "mksh", "fish"]);
|
|
690
|
+
// Wrappers that run the command after them, with their options that take a value.
|
|
691
|
+
const COMMAND_WRAPPERS = new Map([
|
|
692
|
+
["sudo", ["-u", "-g", "-C", "-h", "-p", "-r", "-t", "-U", "-D", "-R", "-T", "--user", "--group", "--host", "--prompt", "--role", "--type", "--other-user", "--chdir", "--chroot", "--command-timeout", "--close-from"]],
|
|
693
|
+
["doas", ["-u", "-C"]],
|
|
694
|
+
["env", ["-u", "-C", "-S", "--unset", "--chdir", "--split-string"]],
|
|
695
|
+
["command", []], ["builtin", []], ["exec", ["-a"]], ["nohup", []], ["setsid", []],
|
|
696
|
+
["time", ["-f", "-o", "--format", "--output"]],
|
|
697
|
+
["nice", ["-n", "--adjustment"]],
|
|
698
|
+
["timeout", ["-s", "-k", "--signal", "--kill-after"]],
|
|
699
|
+
["stdbuf", ["-i", "-o", "-e", "--input", "--output", "--error"]],
|
|
700
|
+
["xargs", ["-I", "-n", "-L", "-P", "-d", "-E", "-s", "-a", "--max-args", "--max-lines", "--max-procs", "--delimiter", "--eof", "--max-chars", "--arg-file"]],
|
|
701
|
+
["npx", ["-p", "--package", "-c", "--call"]],
|
|
702
|
+
["pnpx", ["-p", "--package"]],
|
|
703
|
+
["bunx", ["-p", "--package"]],
|
|
704
|
+
]);
|
|
705
|
+
// Wrapper options whose value is itself a command line: npx -c, env -S.
|
|
706
|
+
const WRAPPER_SCRIPT_OPTIONS = new Set(["-c", "--call", "-S", "--split-string"]);
|
|
707
|
+
const ENV_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
708
|
+
|
|
709
|
+
const baseName = (word) => String(word).split("/").pop();
|
|
710
|
+
|
|
711
|
+
// Where $( ... ) that opens at `start` (just after "$(") closes: the index of
|
|
712
|
+
// its ")", or the end of the text. Nested parentheses and quotes are skipped.
|
|
713
|
+
function substitutionEnd(s, start) {
|
|
714
|
+
let depth = 1;
|
|
715
|
+
let i = start;
|
|
716
|
+
while (i < s.length) {
|
|
717
|
+
const c = s[i];
|
|
718
|
+
if (c === "\\") { i += 2; continue; }
|
|
719
|
+
if (c === "'") { const end = s.indexOf("'", i + 1); i = end < 0 ? s.length : end + 1; continue; }
|
|
720
|
+
if (c === '"') {
|
|
721
|
+
let j = i + 1;
|
|
722
|
+
while (j < s.length && s[j] !== '"') j += s[j] === "\\" ? 2 : 1;
|
|
723
|
+
i = j + 1;
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
if (c === "(") depth += 1;
|
|
727
|
+
else if (c === ")") { depth -= 1; if (depth === 0) return i; }
|
|
728
|
+
i += 1;
|
|
729
|
+
}
|
|
730
|
+
return s.length;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// The commands of a shell command line, the way a shell splits them: each is
|
|
734
|
+
// { words, input } with quotes removed, redirection targets and comments left
|
|
735
|
+
// out, and `input` the heredoc bodies and here-strings it reads. `nested` holds
|
|
736
|
+
// the text of every $(...) and backtick, which the shell also runs.
|
|
737
|
+
function shellCommands(command) {
|
|
738
|
+
const s = String(command || "");
|
|
739
|
+
const commands = [];
|
|
740
|
+
const nested = [];
|
|
741
|
+
let words = [];
|
|
742
|
+
let input = [];
|
|
743
|
+
let word = null;
|
|
744
|
+
let target = false; // the next word is a redirection's file or descriptor
|
|
745
|
+
let hereString = false; // the next word is a here-string (<<<)
|
|
746
|
+
let heredocs = []; // heredocs whose bodies start after the next newline
|
|
747
|
+
const endWord = () => {
|
|
748
|
+
if (word === null) return;
|
|
749
|
+
if (target) target = false;
|
|
750
|
+
else if (hereString) { hereString = false; input.push(word); }
|
|
751
|
+
else words.push(word);
|
|
752
|
+
word = null;
|
|
753
|
+
};
|
|
754
|
+
const endCommand = () => {
|
|
755
|
+
endWord();
|
|
756
|
+
if (words.length) commands.push({ words, input });
|
|
757
|
+
words = [];
|
|
758
|
+
input = [];
|
|
759
|
+
target = false;
|
|
760
|
+
hereString = false;
|
|
761
|
+
};
|
|
762
|
+
const backtick = (i) => {
|
|
763
|
+
let j = i + 1;
|
|
764
|
+
while (j < s.length && s[j] !== "`") j += s[j] === "\\" ? 2 : 1;
|
|
765
|
+
nested.push(s.slice(i + 1, Math.min(j, s.length)));
|
|
766
|
+
return j + 1;
|
|
767
|
+
};
|
|
768
|
+
let i = 0;
|
|
769
|
+
while (i < s.length) {
|
|
770
|
+
const c = s[i];
|
|
771
|
+
if (c === "\\") {
|
|
772
|
+
if (s[i + 1] === "\n") { i += 2; continue; } // a line continuation
|
|
773
|
+
if (i + 1 < s.length) word = (word || "") + s[i + 1];
|
|
774
|
+
i += 2;
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
if (c === "#" && word === null) { // a comment, to the end of the line
|
|
778
|
+
while (i < s.length && s[i] !== "\n") i += 1;
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
if (c === "'") {
|
|
782
|
+
const end = s.indexOf("'", i + 1) < 0 ? s.length : s.indexOf("'", i + 1);
|
|
783
|
+
word = (word || "") + s.slice(i + 1, end);
|
|
784
|
+
i = end + 1;
|
|
785
|
+
continue;
|
|
786
|
+
}
|
|
787
|
+
if (c === '"') {
|
|
788
|
+
let j = i + 1;
|
|
789
|
+
let text = "";
|
|
790
|
+
while (j < s.length && s[j] !== '"') {
|
|
791
|
+
if (s[j] === "\\" && j + 1 < s.length) {
|
|
792
|
+
if (s[j + 1] !== "\n") text += /[$`"\\]/.test(s[j + 1]) ? s[j + 1] : s[j] + s[j + 1];
|
|
793
|
+
j += 2;
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
if (s[j] === "$" && s[j + 1] === "(") {
|
|
797
|
+
const end = substitutionEnd(s, j + 2);
|
|
798
|
+
nested.push(s.slice(j + 2, end));
|
|
799
|
+
text += s.slice(j, end + 1);
|
|
800
|
+
j = end + 1;
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
if (s[j] === "`") {
|
|
804
|
+
const after = backtick(j);
|
|
805
|
+
text += s.slice(j, after);
|
|
806
|
+
j = after;
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
text += s[j];
|
|
810
|
+
j += 1;
|
|
811
|
+
}
|
|
812
|
+
word = (word || "") + text;
|
|
813
|
+
i = j + 1;
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
if (c === "`") {
|
|
817
|
+
const after = backtick(i);
|
|
818
|
+
word = (word || "") + s.slice(i, after);
|
|
819
|
+
i = after;
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
if (c === "$" && s[i + 1] === "(") {
|
|
823
|
+
const end = substitutionEnd(s, i + 2);
|
|
824
|
+
nested.push(s.slice(i + 2, end));
|
|
825
|
+
word = (word || "") + s.slice(i, end + 1);
|
|
826
|
+
i = end + 1;
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
if (c === "<" && s.startsWith("<<<", i)) { endWord(); hereString = true; i += 3; continue; }
|
|
830
|
+
if (c === "<" && s[i + 1] === "<") { // a heredoc: its body follows the next newline
|
|
831
|
+
endWord();
|
|
832
|
+
i += 2;
|
|
833
|
+
const strip = s[i] === "-";
|
|
834
|
+
if (strip) i += 1;
|
|
835
|
+
while (s[i] === " " || s[i] === "\t") i += 1;
|
|
836
|
+
let delim = "";
|
|
837
|
+
while (i < s.length && !/[\s;&|<>()]/.test(s[i])) {
|
|
838
|
+
if (s[i] === "'" || s[i] === '"') {
|
|
839
|
+
const q = s[i];
|
|
840
|
+
const end = s.indexOf(q, i + 1) < 0 ? s.length : s.indexOf(q, i + 1);
|
|
841
|
+
delim += s.slice(i + 1, end);
|
|
842
|
+
i = end + 1;
|
|
843
|
+
} else {
|
|
844
|
+
if (s[i] !== "\\") delim += s[i];
|
|
845
|
+
i += 1;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (delim) heredocs.push({ delim, strip, input });
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
if (c === "<" || c === ">") { // a redirection: the word after it is a file, not an argument
|
|
852
|
+
if (word !== null && /^\d+$/.test(word)) word = null; // the descriptor of 2>, 1>&...
|
|
853
|
+
endWord();
|
|
854
|
+
i += /[>&|]/.test(s[i + 1] || "") ? 2 : 1;
|
|
855
|
+
target = true;
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
if (c === "\n" || c === "\r") {
|
|
859
|
+
endCommand();
|
|
860
|
+
i += 1;
|
|
861
|
+
if (c === "\n" && heredocs.length) {
|
|
862
|
+
for (const doc of heredocs) {
|
|
863
|
+
const body = [];
|
|
864
|
+
while (i < s.length) {
|
|
865
|
+
const eol = s.indexOf("\n", i) < 0 ? s.length : s.indexOf("\n", i);
|
|
866
|
+
let line = s.slice(i, eol);
|
|
867
|
+
if (doc.strip) line = line.replace(/^\t+/, "");
|
|
868
|
+
i = eol + 1;
|
|
869
|
+
if (line === doc.delim) break;
|
|
870
|
+
body.push(line);
|
|
871
|
+
}
|
|
872
|
+
doc.input.push(body.join("\n"));
|
|
873
|
+
}
|
|
874
|
+
heredocs = [];
|
|
875
|
+
}
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
if (/[;&|()]/.test(c)) { endCommand(); i += 1; continue; }
|
|
879
|
+
if (/\s/.test(c)) { endWord(); i += 1; continue; }
|
|
880
|
+
word = (word || "") + c;
|
|
881
|
+
i += 1;
|
|
882
|
+
}
|
|
883
|
+
endCommand();
|
|
884
|
+
return { commands, nested };
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// The index of the command's own name, behind env assignments, shell words and
|
|
888
|
+
// wrappers with their options (-1 when there is none). A wrapper option whose
|
|
889
|
+
// value is a command line (npx -c, env -S) is added to `scripts`.
|
|
890
|
+
function commandWordIndex(words, scripts) {
|
|
891
|
+
let k = 0;
|
|
892
|
+
for (let guard = 0; guard < 16 && k < words.length; guard += 1) {
|
|
893
|
+
const w = words[k];
|
|
894
|
+
if (ENV_ASSIGNMENT.test(w) || SHELL_WORDS.has(w)) { k += 1; continue; }
|
|
895
|
+
const name = baseName(w);
|
|
896
|
+
const valueOptions = COMMAND_WRAPPERS.get(name);
|
|
897
|
+
if (!valueOptions) return k;
|
|
898
|
+
k += 1;
|
|
899
|
+
let positional = name === "timeout" ? 1 : 0; // timeout DURATION command
|
|
900
|
+
while (k < words.length) {
|
|
901
|
+
const arg = words[k];
|
|
902
|
+
if (arg === "--") { k += 1; break; }
|
|
903
|
+
if (name === "env" && ENV_ASSIGNMENT.test(arg)) { k += 1; continue; }
|
|
904
|
+
if (arg.length > 1 && arg.startsWith("-")) {
|
|
905
|
+
if (valueOptions.includes(arg)) {
|
|
906
|
+
if (WRAPPER_SCRIPT_OPTIONS.has(arg) && words[k + 1] !== undefined) scripts.push(words[k + 1]);
|
|
907
|
+
k += 2;
|
|
908
|
+
} else {
|
|
909
|
+
k += 1;
|
|
910
|
+
}
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
913
|
+
if (positional > 0) { positional -= 1; k += 1; continue; }
|
|
914
|
+
break;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return k < words.length ? k : -1;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// agent-browser's subcommand and the word after it (sub is null when there is
|
|
921
|
+
// none, e.g. --help or --version).
|
|
922
|
+
function agentBrowserCall(words, at) {
|
|
923
|
+
let j = at + 1;
|
|
924
|
+
while (j < words.length) {
|
|
925
|
+
const arg = words[j];
|
|
926
|
+
if (arg === "--") { j += 1; continue; }
|
|
927
|
+
if (arg.startsWith("-")) {
|
|
928
|
+
if (!arg.includes("=") && AGENT_BROWSER_VALUE_FLAGS.has(arg)) j += 2;
|
|
929
|
+
else j += /^(true|false)$/.test(words[j + 1] || "") ? 2 : 1; // boolean flags take an optional true/false
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
return { sub: arg, sub2: words[j + 1] === undefined ? null : words[j + 1] };
|
|
933
|
+
}
|
|
934
|
+
return { sub: null, sub2: null };
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Every agent-browser invocation in a command: [{ sub, sub2 }].
|
|
938
|
+
function agentBrowserInvocations(command, depth = 0) {
|
|
939
|
+
const calls = [];
|
|
940
|
+
if (depth > 4) return calls;
|
|
941
|
+
const { commands, nested } = shellCommands(command);
|
|
942
|
+
const scripts = [...nested];
|
|
943
|
+
for (const { words, input } of commands) {
|
|
944
|
+
const at = commandWordIndex(words, scripts);
|
|
945
|
+
if (at < 0) continue;
|
|
946
|
+
const name = baseName(words[at]);
|
|
947
|
+
if (AGENT_BROWSER_BIN.test(name)) {
|
|
948
|
+
calls.push(agentBrowserCall(words, at));
|
|
949
|
+
} else if (name === "eval") {
|
|
950
|
+
scripts.push(words.slice(at + 1).join(" "));
|
|
951
|
+
} else if (SHELLS.has(name)) {
|
|
952
|
+
// sh -c '<script>' [name args], else the script a shell reads from its heredoc/here-string.
|
|
953
|
+
let script = null;
|
|
954
|
+
for (let j = at + 1; j < words.length; j += 1) {
|
|
955
|
+
const w = words[j];
|
|
956
|
+
if (/^-[A-Za-z]*c[A-Za-z]*$/.test(w)) {
|
|
957
|
+
script = words.slice(j + 1).find((x) => !x.startsWith("-"));
|
|
958
|
+
break;
|
|
959
|
+
}
|
|
960
|
+
if (w === "-o" || w === "+o" || w === "-O" || w === "+O") { j += 1; continue; }
|
|
961
|
+
if (!w.startsWith("-") && !w.startsWith("+")) break; // a script file
|
|
962
|
+
}
|
|
963
|
+
if (script != null) scripts.push(script);
|
|
964
|
+
else scripts.push(...input);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
for (const script of scripts) calls.push(...agentBrowserInvocations(script, depth + 1));
|
|
968
|
+
return calls;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
function readOnlyBrowserCall(call) {
|
|
972
|
+
if (!call.sub) return true; // no subcommand: usage or version
|
|
973
|
+
if (AGENT_BROWSER_READ_ONLY.has(call.sub)) return true;
|
|
974
|
+
return call.sub === "session" && call.sub2 === "list";
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// null, or { subcommand, message } when a live driver lock refuses this command.
|
|
978
|
+
function browserDriverDenial(command, now = Date.now()) {
|
|
979
|
+
try {
|
|
980
|
+
const cmd = String(command || "");
|
|
981
|
+
if (!cmd.includes("agent-browser")) return null;
|
|
982
|
+
const lock = require("./browser-lock").liveLock(now);
|
|
983
|
+
if (!lock) return null;
|
|
984
|
+
const blocked = agentBrowserInvocations(cmd).find((call) => !readOnlyBrowserCall(call));
|
|
985
|
+
if (!blocked) return null;
|
|
986
|
+
const name = String(lock.name || "").replace(/[\u0000-\u001f\u007f]+/g, " ").replace(/\s+/g, " ").trim().slice(0, 60) || "Someone";
|
|
987
|
+
return {
|
|
988
|
+
subcommand: String(blocked.sub || "").slice(0, 40),
|
|
989
|
+
message: `${name} is driving the browser right now. Wait for them to hand it back; you will be woken in their chat when they do.`,
|
|
990
|
+
};
|
|
991
|
+
} catch (e) {
|
|
992
|
+
return null;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
662
996
|
// Claude sends the shell command as a string; accept an argv array too.
|
|
663
997
|
function shellCommandOf(input) {
|
|
664
998
|
if (!input || typeof input !== "object") return null;
|
|
@@ -727,6 +1061,14 @@ function runHook(payload) {
|
|
|
727
1061
|
logGuardEvent({ kind: "deny", rule: "external-secret-read", pattern: secretDenial.rule, command: String(command).slice(0, 300) });
|
|
728
1062
|
return { exit: 2, stderr: secretDenial.message };
|
|
729
1063
|
}
|
|
1064
|
+
// A person is driving the browser (Browser app): ahead of the tooling-mode
|
|
1065
|
+
// switch, so relaxed mode cannot drive over them either. Only the
|
|
1066
|
+
// subcommand is logged: the bot's own fill/type commands can carry text.
|
|
1067
|
+
const driving = browserDriverDenial(command);
|
|
1068
|
+
if (driving) {
|
|
1069
|
+
logGuardEvent({ kind: "deny", rule: "browser-driver", subcommand: driving.subcommand });
|
|
1070
|
+
return { exit: 2, stderr: driving.message };
|
|
1071
|
+
}
|
|
730
1072
|
// Relaxed tooling mode: the gate observes but never blocks. The event is
|
|
731
1073
|
// still logged (as relaxed-pass, not deny) so the dream KPI can report
|
|
732
1074
|
// what WOULD have been denied — visibility survives the mode switch.
|
|
@@ -761,4 +1103,5 @@ module.exports = {
|
|
|
761
1103
|
externalFileMutationDenial, externalControlPlaneDenial, guardedSpeakerFromEnv,
|
|
762
1104
|
protectedReadRoots, isProtectedReadPath, isProtectedSearchRoot, secretReadRule,
|
|
763
1105
|
matchRawOp, noteRawOp, readGuardEvents,
|
|
1106
|
+
agentBrowserInvocations, browserDriverDenial,
|
|
764
1107
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inetafrica/open-claudia",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.20",
|
|
4
4
|
"description": "An always-on, provider-agnostic coding-agent harness for Claude Code and OpenAI Codex via chat",
|
|
5
5
|
"main": "bot.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"setup": "node setup.js",
|
|
12
12
|
"start": "node bot.js",
|
|
13
13
|
"pretest": "node test-provider-fixture.js && node test-provider-core-prompt.js && node test-turn-observer.js && node test-provider-prompt-parity.js && node test-provider-env-isolation.js && node test-provider-resume-parity.js && node test-provider-bootstrap.js && node test-provider-registry.js && node test-provider-stream-decoder.js && node test-provider-events.js && node test-provider-claude.js && node test-provider-codex.js && node test-codex-output-schema.js && node test-codex-device-login.js && node test-provider-runner.js && node test-provider-refusal-recovery.js && node test-provider-run-lifecycle.js && node test-provider-gateway.js && node test-provider-side-chat.js && node test-single-runtime.js && node test-provider-migration-backup.js && node test-provider-state-migration.js && node test-provider-session-history.js && node test-provider-session-commands.js && node test-provider-compaction.js && node test-provider-scheduler.js && node test-credential-capture.js && node test-cursor-removal.js && node test-utility-provider-policy.js && node test-provider-subagent.js && node test-provider-recall.js && node test-provider-pack-review.js && node test-memory-mutation-queue.js && node test-provider-dream.js && node test-provider-enforcer.js && node test-provider-tool-hooks.js && node test-provider-boot-matrix.js && node test-provider-capabilities.js && node test-provider-language.js && node test-upgrade-path.js && node test-provider-coupling-audit.js && node test-provider-parity-e2e.js && node test-kazee-channel-health.js && node test-kazee-media-caption.js && node test-kazee-context-media.js && node test-kazee-typing-join.js && node test-kazee-inbound-dedup.js && node test-typing-phase-push.js && node test-socket-reconnect.js && node test-speech-loopback.js && node test-prompt-budget.js && node test-agency-ledger.js && node test-agency-deliberate.js && node test-guardrail.js && node test-group-status-notes.js && node test-spaces-comment-content.js && node test-boot-health.js && node test-lexical-floor.js && node test-safe-mode.js && node test-logout.js && node test-kazee-call-audio.js && node test-kazee-call-janus.js && node test-kazee-call-webrtc.js && node test-kazee-call-speech.js && node test-kazee-call-manager.js && node test-call-speech.js && node test-kazee-call-session.js",
|
|
14
|
-
"test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && node test-codex-rollout.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-query.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-walker-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-score-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cokept-warming.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-transcript-room-scope.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-search.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-extractive-injection.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-mandate-hygiene.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-external-rule-scopes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-cards.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-people-owner-seed.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-thread-lanes.js && node test-thread-titles.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-thread-stop.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-typing-heartbeat.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-web-sessions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-poll-recovery.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-409-grace.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-conflict-heal-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-single-instance.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-streaming-split.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-stream-preview-purpose.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-spaces-autoenable.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-say-loopback.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-loopback-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-transcript-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-union.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-subst-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-section-budgets.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-dedup-adjudicator.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-prospective.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-regressions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cost-report.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-mining.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-certification.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-apprenticeship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-attest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-attest.js && node test-sandbox-probe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-bot-endpoint.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cli-owner-gates.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-verify-contact.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-loopback-owner-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-watch-check-env.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-intro-adoption.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-grants.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-room-provider-settings.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-store-durability.js && node test-kazee-cards.js && node test-decision-upkeep.js && node test-passive-replies.js && node test-kazee-bot-loops.js && node test-kazee-bot-handoffs.js && node test-placeholder-reply.js && node test-media-speech.js && node test-passive-replies-router.js && node test-loopback-cards.js && node test-task-injection.js && node test-speaker-extras.js && node test-token-diet.js && node test-dream-tasks.js && node test-task-archive.js && node test-cron-tolerance.js && node test-dream-schedule.js && node test-job-failures.js && node test-scheduled-placeholder.js && node test-lessons-since.js && node test-pack-reinject.js && node test-turn-journal.js && node test-usage-limits.js && node test-limit-pickup.js && node test-browser-view.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-browser-view.js && node test-safe-filename.js && node test-media-filename-paths.js && node test-router-document-guard.js && OPEN_CLAUDIA_TEST=1 node test-web-dashboard-escape.js && OPEN_CLAUDIA_TEST=1 node test-web-bad-requests.js && OPEN_CLAUDIA_TEST=1 node test-kazee-download-limit.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-auth-routes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-otp-link-preview.js"
|
|
14
|
+
"test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && node test-codex-rollout.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-query.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-walker-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-score-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cokept-warming.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-transcript-room-scope.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-search.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-extractive-injection.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-mandate-hygiene.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-external-rule-scopes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-cards.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-people-owner-seed.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-thread-lanes.js && node test-thread-titles.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-thread-stop.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-typing-heartbeat.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-web-sessions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-poll-recovery.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-409-grace.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-conflict-heal-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-single-instance.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-streaming-split.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-stream-preview-purpose.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-spaces-autoenable.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-say-loopback.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-loopback-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-transcript-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-union.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-subst-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-section-budgets.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-dedup-adjudicator.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-prospective.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-regressions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cost-report.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-mining.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-certification.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-apprenticeship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-attest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-attest.js && node test-sandbox-probe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-bot-endpoint.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cli-owner-gates.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-verify-contact.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-loopback-owner-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-watch-check-env.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-intro-adoption.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-grants.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-room-provider-settings.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-store-durability.js && node test-kazee-cards.js && node test-decision-upkeep.js && node test-passive-replies.js && node test-kazee-bot-loops.js && node test-kazee-bot-handoffs.js && node test-placeholder-reply.js && node test-media-speech.js && node test-passive-replies-router.js && node test-loopback-cards.js && node test-task-injection.js && node test-speaker-extras.js && node test-token-diet.js && node test-dream-tasks.js && node test-task-archive.js && node test-cron-tolerance.js && node test-dream-schedule.js && node test-job-failures.js && node test-scheduled-placeholder.js && node test-lessons-since.js && node test-pack-reinject.js && node test-turn-journal.js && node test-usage-limits.js && node test-limit-pickup.js && node test-browser-view.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-browser-view.js && node test-browser-control.js && node test-browser-teach.js && node test-kazee-bot-apps.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-browser-app.js && node test-safe-filename.js && node test-media-filename-paths.js && node test-router-document-guard.js && OPEN_CLAUDIA_TEST=1 node test-web-dashboard-escape.js && OPEN_CLAUDIA_TEST=1 node test-web-bad-requests.js && OPEN_CLAUDIA_TEST=1 node test-kazee-download-limit.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-auth-routes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test node test-web-otp-link-preview.js"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"bot.js",
|
|
@@ -591,7 +591,11 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
|
|
|
591
591
|
// dedup judge and the classic recall filter reply in an object (the API
|
|
592
592
|
// refused their bare-array formats on every Claude call), and the utility
|
|
593
593
|
// agent refuses any reply format without an object root. This Mac bot first.
|
|
594
|
-
|
|
594
|
+
// 3.5.20 approved by Sumeet 2026-10-09 12:02 EAT ("fine go ahead and push but
|
|
595
|
+
// also ensure you clear the images after they all done"): the Browser app in
|
|
596
|
+
// Kazee Chat (bot apps v1): Take over, Teach me with its pictures deleted when
|
|
597
|
+
// the work is done, and the bot paused while a person drives. A canary bot first.
|
|
598
|
+
assert.strictEqual(pkg.version, "3.5.20", "release version must remain unchanged without explicit approval");
|
|
595
599
|
for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
|
|
596
600
|
assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
|
|
597
601
|
}
|
package/web.js
CHANGED
|
@@ -375,6 +375,63 @@ async function liveChannels() {
|
|
|
375
375
|
|
|
376
376
|
const USER_ACCESS_ROUTES = new Set(["/api/auth/approve", "/api/auth/deny", "/api/auth/remove"]);
|
|
377
377
|
|
|
378
|
+
// Browser app routes (core/bot-apps.js, core/browser-control.js, core/browser-teach.js):
|
|
379
|
+
// POST /api/apps/check may this person open the app (C1)
|
|
380
|
+
// POST /api/browser/control GET /api/browser/control take / renew / release, status (C2)
|
|
381
|
+
// POST /api/browser/input the driver's input events (C3)
|
|
382
|
+
// POST /api/browser/teach Teach me: start / stop / cancel (C4)
|
|
383
|
+
// GET /api/browser/teach[/<id>[/pictures/<n>]]?userId= that person's recordings only
|
|
384
|
+
const BROWSER_APP_ROUTE = /^\/api\/(?:apps\/check|browser\/(?:control|input|teach))(?:[/?]|$)/;
|
|
385
|
+
const BROWSER_APP_MAX_BODY = 64 * 1024;
|
|
386
|
+
const BROWSER_APP_USER_ID = /^[A-Za-z0-9._:-]{1,100}$/;
|
|
387
|
+
|
|
388
|
+
async function handleBrowserApp(req, res, body) {
|
|
389
|
+
const send = (status, obj) => {
|
|
390
|
+
res.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store" });
|
|
391
|
+
return res.end(JSON.stringify(obj));
|
|
392
|
+
};
|
|
393
|
+
const answer = (result) => send(result.status, result.body);
|
|
394
|
+
const parsedUrl = new URL(req.url, "http://localhost");
|
|
395
|
+
const route = parsedUrl.pathname;
|
|
396
|
+
let parsed = {};
|
|
397
|
+
if (req.method === "POST") {
|
|
398
|
+
if (Buffer.byteLength(body || "") > BROWSER_APP_MAX_BODY) return send(413, { error: "too-large" });
|
|
399
|
+
try { parsed = JSON.parse(body || "{}"); } catch (e) { return send(400, { error: "bad-json" }); }
|
|
400
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return send(400, { error: "bad-json" });
|
|
401
|
+
}
|
|
402
|
+
const control = require("./core/browser-control");
|
|
403
|
+
if (route === "/api/apps/check" && req.method === "POST") return send(200, require("./core/bot-apps").checkAccess(parsed));
|
|
404
|
+
if (route === "/api/browser/control" && req.method === "POST") return answer(await control.control(parsed));
|
|
405
|
+
if (route === "/api/browser/control" && req.method === "GET") return answer(control.status());
|
|
406
|
+
if (route === "/api/browser/input" && req.method === "POST") return answer(await control.input(parsed));
|
|
407
|
+
if (route === "/api/browser/teach" && req.method === "POST") return answer(await control.teachAction(parsed));
|
|
408
|
+
if (req.method === "GET" && (route === "/api/browser/teach" || route.startsWith("/api/browser/teach/"))) {
|
|
409
|
+
const teach = require("./core/browser-teach");
|
|
410
|
+
const userId = parsedUrl.searchParams.get("userId") || "";
|
|
411
|
+
if (!BROWSER_APP_USER_ID.test(userId)) return send(400, { error: "userId required" });
|
|
412
|
+
if (route === "/api/browser/teach") return send(200, teach.listRecordings({ userId }));
|
|
413
|
+
const match = route.match(/^\/api\/browser\/teach\/([a-z0-9-]{1,64})(?:\/pictures\/(\d{1,4}))?$/);
|
|
414
|
+
if (!match) return send(404, { error: "not-found" });
|
|
415
|
+
if (!match[2]) {
|
|
416
|
+
const rec = teach.getRecording(match[1], { userId });
|
|
417
|
+
return rec ? send(200, rec) : send(404, { error: "not-found" });
|
|
418
|
+
}
|
|
419
|
+
const file = teach.picturePath(match[1], Number(match[2]), { userId });
|
|
420
|
+
if (!file) return send(404, { error: "not-found" });
|
|
421
|
+
let data;
|
|
422
|
+
// Deleted a moment ago (teach finish, cancel, the sweeper): gone, like any deleted picture.
|
|
423
|
+
try { data = fs.readFileSync(file); } catch (_) { return send(404, { error: "not-found" }); }
|
|
424
|
+
res.writeHead(200, {
|
|
425
|
+
"Content-Type": "image/jpeg",
|
|
426
|
+
"Content-Length": data.length,
|
|
427
|
+
"Cache-Control": "no-store",
|
|
428
|
+
"X-Content-Type-Options": "nosniff",
|
|
429
|
+
});
|
|
430
|
+
return res.end(data);
|
|
431
|
+
}
|
|
432
|
+
return send(404, { error: "Not found" });
|
|
433
|
+
}
|
|
434
|
+
|
|
378
435
|
async function handleAPI(req, res, body, dependencies = {}) {
|
|
379
436
|
const url = req.url;
|
|
380
437
|
|
|
@@ -643,6 +700,17 @@ async function handleAPI(req, res, body, dependencies = {}) {
|
|
|
643
700
|
return res.end(JSON.stringify({ content: loadLogs() }));
|
|
644
701
|
}
|
|
645
702
|
|
|
703
|
+
// Browser app in Kazee Chat (bot apps v1): who may open it, Take over, input and Teach me.
|
|
704
|
+
// Machine-to-machine for Agent Space, so the control token only: a dashboard session is
|
|
705
|
+
// refused, like /api/otp/mint.
|
|
706
|
+
if (BROWSER_APP_ROUTE.test(url)) {
|
|
707
|
+
if (!checkBearerAuth(req)) {
|
|
708
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
709
|
+
return res.end(JSON.stringify({ error: "Control token required" }));
|
|
710
|
+
}
|
|
711
|
+
return handleBrowserApp(req, res, body);
|
|
712
|
+
}
|
|
713
|
+
|
|
646
714
|
// Live view of the bot's own browser, watch-only (core/browser-view.js): the running
|
|
647
715
|
// agent-browser sessions, and one session's screen as server-sent events.
|
|
648
716
|
if (url.split("?")[0] === "/api/browser/sessions" && req.method === "GET") {
|