@inetafrica/open-claudia 3.3.1 → 3.4.0
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 +21 -0
- package/channels/kazee/adapter.js +6 -0
- package/channels/spaces/adapter.js +11 -2
- package/channels/telegram/adapter.js +28 -9
- package/core/access.js +125 -80
- package/core/actions.js +58 -4
- package/core/approvals.js +6 -0
- package/core/config.js +2 -1
- package/core/context.js +26 -2
- package/core/enforcer.js +113 -3
- package/core/fsutil.js +42 -1
- package/core/grants.js +221 -0
- package/core/handlers.js +49 -0
- package/core/intro-flow.js +83 -17
- package/core/mandates.js +10 -1
- package/core/people.js +157 -134
- package/core/relationship.js +45 -4
- package/core/router.js +18 -7
- package/core/scheduler.js +23 -2
- package/core/state.js +77 -3
- package/package.json +2 -2
- package/setup.js +2 -5
- package/test-enforcer.js +31 -0
- package/test-mandate-hygiene.js +23 -5
- package/test-provider-gateway.js +1 -0
- package/test-provider-language.js +26 -1
- package/web.js +5 -2
package/core/state.js
CHANGED
|
@@ -20,7 +20,7 @@ const {
|
|
|
20
20
|
channelKey,
|
|
21
21
|
identities,
|
|
22
22
|
} = require("./identity");
|
|
23
|
-
const { currentChannelId, currentCanonicalUserId, currentConversationKey } = require("./context");
|
|
23
|
+
const { currentChannelId, currentCanonicalUserId, currentConversationKey, currentRoomKey } = require("./context");
|
|
24
24
|
const { atomicWriteFileSync, readJsonWithFallback } = require("./fsutil");
|
|
25
25
|
const {
|
|
26
26
|
defaultMigrationSources,
|
|
@@ -336,7 +336,12 @@ function migrateStateDocument(raw, options = {}) {
|
|
|
336
336
|
const migrated = migrateSavedUser(value);
|
|
337
337
|
users[userId] = users[userId] ? mergeSavedState(users[userId], migrated) : migrated;
|
|
338
338
|
}
|
|
339
|
-
|
|
339
|
+
// Runtime settings belong to the room, not to whoever last spoke in it — the
|
|
340
|
+
// assistant in a room is one assistant regardless of who types. Absent on
|
|
341
|
+
// documents written before rooms existed, which is the no-override case and
|
|
342
|
+
// leaves resolution byte-identical to the per-person behaviour.
|
|
343
|
+
const rooms = isRecord(source.rooms) ? source.rooms : {};
|
|
344
|
+
return { schemaVersion: STATE_SCHEMA_VERSION, users, rooms };
|
|
340
345
|
}
|
|
341
346
|
|
|
342
347
|
function loadStateFile() {
|
|
@@ -380,7 +385,49 @@ function freshSettings(defaultProvider = null) {
|
|
|
380
385
|
};
|
|
381
386
|
}
|
|
382
387
|
|
|
388
|
+
// ── Room-scoped runtime settings ─────────────────────────────────────
|
|
389
|
+
// A room owns its provider/model/effort so every speaker in it — and every job
|
|
390
|
+
// created from it — runs under the same assistant. The person's own settings
|
|
391
|
+
// remain the seed for rooms that have never been set, so a pod that has never
|
|
392
|
+
// pinned a room behaves exactly as it did before rooms existed.
|
|
393
|
+
|
|
394
|
+
function roomSettingsFor(roomKey) {
|
|
395
|
+
const key = String(roomKey || "");
|
|
396
|
+
if (!key || !isRecord(savedState.rooms)) return null;
|
|
397
|
+
const entry = savedState.rooms[key];
|
|
398
|
+
return isRecord(entry) && isRecord(entry.settings) ? entry.settings : null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// The room whose settings govern the state's current turn. Stamped in
|
|
402
|
+
// getUserState from the live room key; null outside a chat turn (CLI, boot,
|
|
403
|
+
// tests), which is what keeps those paths on the person's settings.
|
|
404
|
+
function roomOverlayFor(state) {
|
|
405
|
+
return roomSettingsFor(state && state.roomKey);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function setRoomSetting(roomKey, key, value) {
|
|
409
|
+
const room = String(roomKey || "");
|
|
410
|
+
if (!room) return false;
|
|
411
|
+
if (!isRecord(savedState.rooms)) savedState.rooms = {};
|
|
412
|
+
if (!isRecord(savedState.rooms[room])) savedState.rooms[room] = {};
|
|
413
|
+
if (!isRecord(savedState.rooms[room].settings)) savedState.rooms[room].settings = {};
|
|
414
|
+
if (value === null || value === undefined) delete savedState.rooms[room].settings[key];
|
|
415
|
+
else savedState.rooms[room].settings[key] = value;
|
|
416
|
+
saveState();
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function clearRoomSettings(roomKey) {
|
|
421
|
+
const room = String(roomKey || "");
|
|
422
|
+
if (!room || !isRecord(savedState.rooms) || !savedState.rooms[room]) return false;
|
|
423
|
+
delete savedState.rooms[room];
|
|
424
|
+
saveState();
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
|
|
383
428
|
function getActiveProvider(state) {
|
|
429
|
+
const room = roomOverlayFor(state);
|
|
430
|
+
if (room && room.backend) return normalizeProviderId(room.backend, null);
|
|
384
431
|
return normalizeProviderId(state?.settings?.backend, null);
|
|
385
432
|
}
|
|
386
433
|
|
|
@@ -483,6 +530,23 @@ function getProviderSettings(state, providerId) {
|
|
|
483
530
|
if (!isRecord(state.providerSettings)) state.providerSettings = {};
|
|
484
531
|
if (!isRecord(state.providerSettings[provider])) state.providerSettings[provider] = providerDefaults();
|
|
485
532
|
else state.providerSettings[provider] = { ...providerDefaults(), ...state.providerSettings[provider] };
|
|
533
|
+
// Inside a room, model/effort/budget belong to the room, so every speaker
|
|
534
|
+
// gets the same assistant. The room's bucket is seeded from the speaker who
|
|
535
|
+
// first pins it, then diverges — mutations by callers land on the room object
|
|
536
|
+
// and persist with savedState.rooms.
|
|
537
|
+
const roomKey = state && state.roomKey ? String(state.roomKey) : "";
|
|
538
|
+
if (roomKey) {
|
|
539
|
+
if (!isRecord(savedState.rooms)) savedState.rooms = {};
|
|
540
|
+
if (!isRecord(savedState.rooms[roomKey])) savedState.rooms[roomKey] = {};
|
|
541
|
+
const room = savedState.rooms[roomKey];
|
|
542
|
+
if (!isRecord(room.providerSettings)) room.providerSettings = {};
|
|
543
|
+
if (!isRecord(room.providerSettings[provider])) {
|
|
544
|
+
room.providerSettings[provider] = { ...providerDefaults(), ...state.providerSettings[provider] };
|
|
545
|
+
} else {
|
|
546
|
+
room.providerSettings[provider] = { ...providerDefaults(), ...room.providerSettings[provider] };
|
|
547
|
+
}
|
|
548
|
+
return room.providerSettings[provider];
|
|
549
|
+
}
|
|
486
550
|
return state.providerSettings[provider];
|
|
487
551
|
}
|
|
488
552
|
|
|
@@ -667,6 +731,7 @@ function getUserState(userId) {
|
|
|
667
731
|
}
|
|
668
732
|
}
|
|
669
733
|
state.channelId = currentChannelId() || state.channelId;
|
|
734
|
+
state.roomKey = currentRoomKey() || null;
|
|
670
735
|
try { seedConversationContinuity(state); } catch (e) {}
|
|
671
736
|
return state;
|
|
672
737
|
}
|
|
@@ -779,7 +844,11 @@ function saveState(options = {}) {
|
|
|
779
844
|
return false;
|
|
780
845
|
}
|
|
781
846
|
|
|
782
|
-
const data = {
|
|
847
|
+
const data = {
|
|
848
|
+
schemaVersion: STATE_SCHEMA_VERSION,
|
|
849
|
+
users: { ...(savedState.users || {}) },
|
|
850
|
+
rooms: { ...(savedState.rooms || {}) },
|
|
851
|
+
};
|
|
783
852
|
for (const [id, s] of userStates) {
|
|
784
853
|
data.users[id] = serializeUserState(s, data.users[id]);
|
|
785
854
|
}
|
|
@@ -795,6 +864,7 @@ function saveState(options = {}) {
|
|
|
795
864
|
markMigrationComponentActivated(snapshot.snapshotDir, "state", { configDir: CONFIG_DIR, sources });
|
|
796
865
|
savedState.schemaVersion = STATE_SCHEMA_VERSION;
|
|
797
866
|
savedState.users = data.users;
|
|
867
|
+
savedState.rooms = data.rooms;
|
|
798
868
|
}
|
|
799
869
|
catch (e) {
|
|
800
870
|
console.error("saveState: write failed:", e.message);
|
|
@@ -1409,6 +1479,10 @@ module.exports = {
|
|
|
1409
1479
|
freshUsage,
|
|
1410
1480
|
migrateStateDocument,
|
|
1411
1481
|
getActiveProvider,
|
|
1482
|
+
roomSettingsFor,
|
|
1483
|
+
roomOverlayFor,
|
|
1484
|
+
setRoomSetting,
|
|
1485
|
+
clearRoomSettings,
|
|
1412
1486
|
getProjectKey,
|
|
1413
1487
|
getProviderSession,
|
|
1414
1488
|
setProviderSession,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inetafrica/open-claudia",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
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-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-provider-coupling-audit.js && node test-provider-parity-e2e.js && node test-kazee-channel-health.js && node test-kazee-media-caption.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-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-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 && 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-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-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-queue-routing.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-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-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-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-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-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"
|
|
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 && 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-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-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-queue-routing.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-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-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-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-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-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-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-store-durability.js"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"bot.js",
|
package/setup.js
CHANGED
|
@@ -9,7 +9,7 @@ const { execSync } = require("child_process");
|
|
|
9
9
|
const Vault = require("./vault");
|
|
10
10
|
const CONFIG_DIR = require("./config-dir");
|
|
11
11
|
const { ensureConfigDir } = require("./core/config-dir");
|
|
12
|
-
const { atomicWriteFileSync } = require("./core/fsutil");
|
|
12
|
+
const { atomicWriteFileSync, readJsonWithFallback } = require("./core/fsutil");
|
|
13
13
|
const { inspectProviderConfiguration } = require("./core/provider-status");
|
|
14
14
|
const { ProviderRegistry } = require("./core/providers");
|
|
15
15
|
const { createClaudeProvider } = require("./core/providers/claude");
|
|
@@ -32,10 +32,7 @@ const ask = (q) => new Promise((r) => promptInterface().question(q, r));
|
|
|
32
32
|
// ── Auth file helpers ──────────────────────────────────────────────
|
|
33
33
|
|
|
34
34
|
function loadAuth() {
|
|
35
|
-
|
|
36
|
-
try { return JSON.parse(fs.readFileSync(AUTH_FILE, "utf-8")); } catch (e) {}
|
|
37
|
-
}
|
|
38
|
-
return { authorized: [], pending: [] };
|
|
35
|
+
return readJsonWithFallback(AUTH_FILE, null) || { authorized: [], pending: [] };
|
|
39
36
|
}
|
|
40
37
|
|
|
41
38
|
function saveAuth(auth) {
|
package/test-enforcer.js
CHANGED
|
@@ -378,5 +378,36 @@ const extSpeaker = relationship.speakerFor("telegram", CH_EXT);
|
|
|
378
378
|
await runInChat({ ...grantCtx, triageApprovalId: escReply.id }, () => enforcer.vetReply(extSpeaker, "reply-kind-grant-payload"));
|
|
379
379
|
assert.ok(!/TURN GRANT/.test(grantPrompt), "only a triage-kind approval grants the turn");
|
|
380
380
|
|
|
381
|
+
// ── stranded-mandate hint (dual-file trap): a grant written to the
|
|
382
|
+
// name-obvious note while the speaker's record reads a hashed slug does
|
|
383
|
+
// nothing — the card must SAY so at the moment it bites, and the stranded
|
|
384
|
+
// text must never be applied or quoted. ──
|
|
385
|
+
const rahil = people.add({ name: "Rahil Malde" });
|
|
386
|
+
people.setEntitySlug(rahil.id, "rahil-malde-abc123");
|
|
387
|
+
people.linkHandle(rahil.id, { adapter: "telegram", channelId: "333" });
|
|
388
|
+
entities.upsertEntity({ slug: "rahil-malde-abc123", name: "Rahil Malde", type: "person", relationship: "external" });
|
|
389
|
+
entities.upsertEntity({ slug: "rahil-malde", name: "Rahil Malde", type: "person", relationship: "external",
|
|
390
|
+
mandate: "- May coordinate ticket triage." });
|
|
391
|
+
const strandedSpeaker = relationship.speakerFor("telegram", "333");
|
|
392
|
+
assert.strictEqual(String(strandedSpeaker.mandate || "").trim(), "",
|
|
393
|
+
"the stranded text never reaches the live mandate");
|
|
394
|
+
sends.length = 0;
|
|
395
|
+
const escStr = await enforcer.escalate({ speaker: strandedSpeaker, kind: "reply", payloadText: "p", summary: "s", reason: "r" });
|
|
396
|
+
assert.strictEqual(escStr.escalated, true, "stranded-mandate escalation still posts");
|
|
397
|
+
const strCard = sends.find((s) => s.channelId === CH_OWNER);
|
|
398
|
+
assert.ok(strCard && /rahil-malde\.md/.test(strCard.text) && /NO effect/.test(strCard.text),
|
|
399
|
+
"the owner card names the file holding the dead grant");
|
|
400
|
+
assert.ok(!/ticket triage/.test(strCard.text), "the stranded text itself stays off the card");
|
|
401
|
+
|
|
402
|
+
// A different person legitimately holding the name slug silences the hint —
|
|
403
|
+
// that mandate is THEIRS, not a stranded copy of this speaker's.
|
|
404
|
+
const rahil2 = people.add({ name: "Rahil M Other" });
|
|
405
|
+
people.setEntitySlug(rahil2.id, "rahil-malde");
|
|
406
|
+
sends.length = 0;
|
|
407
|
+
await enforcer.escalate({ speaker: relationship.speakerFor("telegram", "333"), kind: "reply", payloadText: "p", summary: "s", reason: "r" });
|
|
408
|
+
const strCard2 = sends.find((s) => s.channelId === CH_OWNER);
|
|
409
|
+
assert.ok(strCard2 && !/NO effect/.test(strCard2.text),
|
|
410
|
+
"no hint when the sibling note belongs to a real other person");
|
|
411
|
+
|
|
381
412
|
console.log("enforcer OK");
|
|
382
413
|
})().catch((e) => { console.error(e); process.exit(1); });
|
package/test-mandate-hygiene.js
CHANGED
|
@@ -63,13 +63,31 @@ assert.strictEqual(entities.readEntity("busy-contact").sections.Mandate, nearFul
|
|
|
63
63
|
// ── no record: nothing to write to → refused with a reason ──
|
|
64
64
|
const r4 = appendMandate({ entitySlug: "", personName: "" }, "anything");
|
|
65
65
|
assert.strictEqual(r4.ok, false, "no contact record → refused");
|
|
66
|
-
assert.ok(/no contact record/.test(r4.reason));
|
|
66
|
+
assert.ok(/no linked contact record/.test(r4.reason));
|
|
67
67
|
|
|
68
|
-
// ──
|
|
68
|
+
// ── name-only records are REFUSED, never name-keyed: a display name is
|
|
69
|
+
// spoofable, and a name-keyed upsert could land security text on a same-named
|
|
70
|
+
// OTHER person's note (v3.3.2 — this replaced the legacy fallback). ──
|
|
69
71
|
const r5 = appendMandate({ entitySlug: "", personName: "Legacy Larry" }, "asking build status");
|
|
70
|
-
assert.strictEqual(r5.ok,
|
|
71
|
-
assert.ok(/
|
|
72
|
-
|
|
72
|
+
assert.strictEqual(r5.ok, false, "name-only record is refused");
|
|
73
|
+
assert.ok(/no linked contact record/.test(r5.reason));
|
|
74
|
+
assert.strictEqual(entities.readEntity("legacy-larry"), null,
|
|
75
|
+
"no entity is ever minted from a bare display name");
|
|
76
|
+
|
|
77
|
+
// ── personId resolution: a record without an entitySlug resolves through the
|
|
78
|
+
// person REGISTRY (id → entitySlug), never through the spoofable name ──
|
|
79
|
+
const people = require("./core/people");
|
|
80
|
+
const harry = people.add({ name: "Hash Harry" });
|
|
81
|
+
people.setEntitySlug(harry.id, "hash-harry-9f9f9f");
|
|
82
|
+
entities.upsertEntity({ slug: "hash-harry-9f9f9f", name: "Hash Harry", relationship: "external" });
|
|
83
|
+
const r6 = appendMandate({ personId: harry.id, personName: "Spoofed Name" }, "asking build status");
|
|
84
|
+
assert.strictEqual(r6.ok, true, "personId-only record resolves via the registry");
|
|
85
|
+
assert.ok(/build status/.test(entities.readEntity("hash-harry-9f9f9f").sections.Mandate || ""),
|
|
86
|
+
"the grant lands on the person's registered slug");
|
|
87
|
+
assert.strictEqual(entities.readEntity("spoofed-name"), null,
|
|
88
|
+
"the transport display name never picks the target");
|
|
89
|
+
const r7 = appendMandate({ personId: "missing-person-id", personName: "Ghost" }, "anything");
|
|
90
|
+
assert.strictEqual(r7.ok, false, "an unresolvable personId is refused, not name-keyed");
|
|
73
91
|
|
|
74
92
|
// ── P3: capability-scoped grants — expiry, refresh, ids, revoke ──
|
|
75
93
|
const mandates = require("./core/mandates");
|
package/test-provider-gateway.js
CHANGED
|
@@ -306,6 +306,7 @@ async function schedulerProbe() {
|
|
|
306
306
|
installStub("./core/identity", {
|
|
307
307
|
canonicalForTelegram: () => "fixture-user",
|
|
308
308
|
canonicalForChannel: () => "fixture-user",
|
|
309
|
+
conversationKeyFor: (transport, channelId) => `${transport}:${channelId}`,
|
|
309
310
|
});
|
|
310
311
|
installStub("./core/io", {
|
|
311
312
|
send: async () => {
|
|
@@ -379,7 +379,32 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
|
|
|
379
379
|
// say) voiceless. Carries the unpublished 3.3.0 content plus the Kazee calls
|
|
380
380
|
// batch (trickle ICE + streaming TTS playback, ringing-on-invite/deferred
|
|
381
381
|
// publish, werift ICE rejection suppression). Plus this pin repair.
|
|
382
|
-
|
|
382
|
+
// 3.3.2 approved by Sumeet 2026-08-20 ("Okay, I'll go with your
|
|
383
|
+
// recommendations. Can you make sure you fix everything ... push everything
|
|
384
|
+
// out once the new versions have landed for the dockers update the entire
|
|
385
|
+
// fleet"): the identity/guardrail batch behind the Coders Africa incident —
|
|
386
|
+
// transport-verified sender adoption (a known transport user id in an
|
|
387
|
+
// adapter-attested DM is never interrogated; the router falls through and
|
|
388
|
+
// handles the adopted turn), applyApproval mirrors person.isOwner instead of
|
|
389
|
+
// demoting approved owners, and the mandate writer is strictly slug-keyed
|
|
390
|
+
// (name-keyed fallback removed) with stranded dual-file grants surfaced on
|
|
391
|
+
// the escalation card.
|
|
392
|
+
// 3.4.0 approved by Sumeet 2026-08-25 ("Yeh go ahead and push"): the
|
|
393
|
+
// principal × room × capability rebuild. Who is speaking, where they are
|
|
394
|
+
// speaking, and what they may do there are now three separate values —
|
|
395
|
+
// fusing them was the shared root of the bugs this closes. Principal is
|
|
396
|
+
// always the transport USER id (never the chat id); the room owns the
|
|
397
|
+
// session and the provider/model/effort settings, which is why a wakeup
|
|
398
|
+
// scheduled in a Codex room kept firing on Claude; and capability is a
|
|
399
|
+
// deterministic (principal × room) grant looked up BEFORE the model, so an
|
|
400
|
+
// owner's decision is never re-litigated by a judge. Grants never travel
|
|
401
|
+
// between rooms — trusting someone in one group must not trust them in
|
|
402
|
+
// another — with "everywhere" a separate explicit tap. Approval cards now
|
|
403
|
+
// fan out to every owner (a second owner could previously never approve
|
|
404
|
+
// anything), and the people/auth stores got atomic backed-up writes plus
|
|
405
|
+
// cross-process locking, with hasOwner() failing CLOSED so a corrupt
|
|
406
|
+
// auth.json can no longer read as an unclaimed pod.
|
|
407
|
+
assert.strictEqual(pkg.version, "3.4.0", "release version must remain unchanged without explicit approval");
|
|
383
408
|
for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
|
|
384
409
|
assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
|
|
385
410
|
}
|
package/web.js
CHANGED
|
@@ -5,7 +5,7 @@ const crypto = require("crypto");
|
|
|
5
5
|
const https = require("https");
|
|
6
6
|
const CONFIG_DIR = require("./config-dir");
|
|
7
7
|
const { ensureConfigDir } = require("./core/config-dir");
|
|
8
|
-
const { atomicWriteFileSync } = require("./core/fsutil");
|
|
8
|
+
const { atomicWriteFileSync, readJsonWithFallback } = require("./core/fsutil");
|
|
9
9
|
const Vault = require("./vault");
|
|
10
10
|
const {
|
|
11
11
|
loadUsageHistory,
|
|
@@ -233,8 +233,11 @@ function maskEnv(env) {
|
|
|
233
233
|
return out;
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
// Falling back to `.bak` matters more here than it reads: the dashboard's very
|
|
237
|
+
// next write persists whatever this returned, so treating a corrupt file as an
|
|
238
|
+
// empty roster would overwrite the recoverable copy with the empty one.
|
|
236
239
|
function loadAuth() {
|
|
237
|
-
|
|
240
|
+
return readJsonWithFallback(AUTH_FILE, null) || { authorized: [], pending: [] };
|
|
238
241
|
}
|
|
239
242
|
|
|
240
243
|
function saveAuth(auth) {
|