@inetafrica/open-claudia 3.2.3 → 3.3.1
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 +11 -0
- package/bin/loopback-client.js +4 -0
- package/bot.js +10 -1
- package/channels/kazee/call/audio.js +79 -0
- package/channels/kazee/call/manager.js +6 -0
- package/channels/kazee/call/session.js +356 -48
- package/channels/kazee/call/speech-client.js +38 -3
- package/core/actions.js +80 -0
- package/core/enforcer.js +22 -1
- package/core/handlers.js +46 -2
- package/core/loopback.js +37 -0
- package/core/media.js +71 -18
- package/core/pack-review.js +18 -0
- package/core/relationship.js +18 -2
- package/core/runner.js +7 -1
- package/core/speaker-attest.js +56 -0
- package/core/system-prompt.js +21 -4
- package/core/verify-contact.js +156 -0
- package/package.json +2 -2
- package/test-kazee-call-manager.js +4 -1
- package/test-kazee-call-session.js +100 -4
- package/test-provider-language.js +15 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Owner verification of auto-added contacts — the second half of the
|
|
2
|
+
// ensureContact story. ensureContact deliberately NEVER links a first-time
|
|
3
|
+
// speaker to an existing note by display name (names are claimable, so that
|
|
4
|
+
// would be an impersonation path); it mints an unverified stub instead. This
|
|
5
|
+
// module gives the OWNER the explicit, audited act that closes the loop:
|
|
6
|
+
// confirm who the speaker is, optionally bind them to the existing trusted
|
|
7
|
+
// note, retire the stub, and clear the unverified flag the enforcer surfaces.
|
|
8
|
+
//
|
|
9
|
+
// Security invariants (each refusal below is one of them):
|
|
10
|
+
// - Verification is an OWNER decision arriving via a chat-envelope surface
|
|
11
|
+
// (approval-card button / /people slash command). There is deliberately no
|
|
12
|
+
// CLI or loopback verb: identity decisions never run on agent-forgeable
|
|
13
|
+
// input. Callers must gate on the transport envelope BEFORE calling here.
|
|
14
|
+
// - Verification is NOT trust: it settles who the person is. Relationship
|
|
15
|
+
// upgrades and mandate lines remain separate explicit owner grants.
|
|
16
|
+
// - Never link to an owner-relationship note (R1 defense-in-depth: speakerFor
|
|
17
|
+
// already coerces such a claim, but this surface refuses to create it).
|
|
18
|
+
// - Never link two people to one note: a persona pack carries a Mandate, and
|
|
19
|
+
// sharing one across identities would silently share granted capabilities.
|
|
20
|
+
// - Only auto-added (autoCreated) contacts are verifiable here. Re-pointing an
|
|
21
|
+
// already-verified person is intentionally NOT offered, so this surface can
|
|
22
|
+
// never be used as a general re-identity tool.
|
|
23
|
+
|
|
24
|
+
const fs = require("fs");
|
|
25
|
+
const path = require("path");
|
|
26
|
+
const people = require("./people");
|
|
27
|
+
const entities = require("./entities");
|
|
28
|
+
const audit = require("./audit");
|
|
29
|
+
|
|
30
|
+
// Same-name suggestions for the owner surfaces: existing person-type notes
|
|
31
|
+
// this unverified contact could plausibly BE. Excludes the contact's own stub,
|
|
32
|
+
// owner-relationship notes, and notes already bound to another person record.
|
|
33
|
+
// Suggestion-only — verifyContact re-validates whatever the owner picks.
|
|
34
|
+
function candidateEntitiesFor(person) {
|
|
35
|
+
if (!person) return [];
|
|
36
|
+
const stubSlug = people.resolveEntitySlug(person);
|
|
37
|
+
const wanted = String(person.name || "").trim().toLowerCase();
|
|
38
|
+
if (!wanted) return [];
|
|
39
|
+
const taken = new Set(
|
|
40
|
+
people.list()
|
|
41
|
+
.filter((p) => p.id !== person.id)
|
|
42
|
+
.map((p) => people.resolveEntitySlug(p))
|
|
43
|
+
.filter(Boolean),
|
|
44
|
+
);
|
|
45
|
+
const out = [];
|
|
46
|
+
for (const e of entities.listEntities()) {
|
|
47
|
+
if (e.type !== "person") continue;
|
|
48
|
+
if (e.slug === stubSlug) continue;
|
|
49
|
+
if (e.relationship === "owner") continue;
|
|
50
|
+
if (taken.has(e.slug)) continue;
|
|
51
|
+
const names = [e.name, ...(e.aliases || [])]
|
|
52
|
+
.map((n) => String(n || "").trim().toLowerCase())
|
|
53
|
+
.filter(Boolean);
|
|
54
|
+
if (names.includes(wanted)) out.push(e);
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function refuse(reason, extra = {}) {
|
|
60
|
+
return { ok: false, reason, ...extra };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Verify (and optionally link) one auto-added contact.
|
|
64
|
+
// personId — the people.json record to verify
|
|
65
|
+
// linkSlug — optional existing note to bind them to; omitted = "this stub
|
|
66
|
+
// IS the right note, just mark them verified"
|
|
67
|
+
// by / via — provenance for the audit trail (owner id, surface name)
|
|
68
|
+
function verifyContact({ personId, linkSlug = null, by = "", via = "" } = {}) {
|
|
69
|
+
const person = people.findById(String(personId || ""));
|
|
70
|
+
if (!person) return refuse("person not found");
|
|
71
|
+
if (person.isOwner) return refuse("the owner record is never routed through contact verification");
|
|
72
|
+
if (!person.autoCreated) return refuse(`${person.name} is already verified`, { already: true });
|
|
73
|
+
|
|
74
|
+
const stubSlug = people.resolveEntitySlug(person);
|
|
75
|
+
let targetSlug = stubSlug;
|
|
76
|
+
let supersededStub = false;
|
|
77
|
+
|
|
78
|
+
if (linkSlug) {
|
|
79
|
+
const wantedSlug = entities.slugify(String(linkSlug));
|
|
80
|
+
if (wantedSlug && wantedSlug !== stubSlug) {
|
|
81
|
+
const target = entities.readEntity(wantedSlug);
|
|
82
|
+
if (!target) return refuse(`no note found at ${wantedSlug}`);
|
|
83
|
+
if (target.type !== "person") return refuse(`${wantedSlug} is a ${target.type} note, not a person`);
|
|
84
|
+
if (target.relationship === "owner") {
|
|
85
|
+
return refuse("refusing to link a contact to an owner-relationship note — ownership only ever comes from the people record");
|
|
86
|
+
}
|
|
87
|
+
const holder = people.list().find(
|
|
88
|
+
(p) => p.id !== person.id && people.resolveEntitySlug(p) === target.slug,
|
|
89
|
+
);
|
|
90
|
+
if (holder) {
|
|
91
|
+
return refuse(`that note already belongs to ${holder.name} — two people can never share one persona pack`);
|
|
92
|
+
}
|
|
93
|
+
targetSlug = target.slug;
|
|
94
|
+
|
|
95
|
+
// Provenance onto the surviving note, then retire the stub file as
|
|
96
|
+
// .superseded (kept on disk — the merge is inspectable, not destructive).
|
|
97
|
+
entities.upsertEntity({
|
|
98
|
+
slug: target.slug,
|
|
99
|
+
name: target.name,
|
|
100
|
+
log: `owner verified ${person.name} as this contact (absorbed auto-added stub ${stubSlug})`,
|
|
101
|
+
}, "owner-verify");
|
|
102
|
+
if (stubSlug) {
|
|
103
|
+
const stubFile = path.join(entities.ENTITIES_DIR, `${stubSlug}.md`);
|
|
104
|
+
try {
|
|
105
|
+
if (fs.existsSync(stubFile)) {
|
|
106
|
+
fs.renameSync(stubFile, `${stubFile}.superseded`);
|
|
107
|
+
supersededStub = true;
|
|
108
|
+
}
|
|
109
|
+
} catch (e) { /* best-effort: a stranded stub is inert once unlinked */ }
|
|
110
|
+
try { entities.markIndexDirty(); } catch (e) {}
|
|
111
|
+
}
|
|
112
|
+
people.setEntitySlug(person.id, target.slug);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (targetSlug === stubSlug && stubSlug) {
|
|
117
|
+
// Verify-only: the stub becomes the canonical note for this person. The
|
|
118
|
+
// description keeps its first-seen provenance, minus the unverified label.
|
|
119
|
+
const stub = entities.readEntity(stubSlug);
|
|
120
|
+
const description = stub && stub.description
|
|
121
|
+
? stub.description
|
|
122
|
+
.replace(/^Auto-added contact \(unverified\)/, "Verified contact")
|
|
123
|
+
.replace(/\s*Possible existing note:.*$/, "")
|
|
124
|
+
: "Verified contact — identity confirmed by the owner.";
|
|
125
|
+
entities.upsertEntity({
|
|
126
|
+
slug: stubSlug,
|
|
127
|
+
name: person.name,
|
|
128
|
+
description,
|
|
129
|
+
log: "owner verified this contact; this note is now their canonical persona pack",
|
|
130
|
+
}, "owner-verify");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const fresh = people.findById(person.id) || person;
|
|
134
|
+
const updated = people.update(person.id, {
|
|
135
|
+
autoCreated: false,
|
|
136
|
+
verifiedAt: new Date().toISOString(),
|
|
137
|
+
verifiedBy: String(by || "owner"),
|
|
138
|
+
verifiedVia: String(via || ""),
|
|
139
|
+
handles: (fresh.handles || []).map((h) => (
|
|
140
|
+
h.approvedBy === "auto-contact" ? { ...h, approvedBy: String(by || "owner") } : h
|
|
141
|
+
)),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
audit.log("people.verify", {
|
|
145
|
+
by: String(by || "owner"),
|
|
146
|
+
via: String(via || ""),
|
|
147
|
+
personId: person.id,
|
|
148
|
+
name: person.name,
|
|
149
|
+
slug: targetSlug,
|
|
150
|
+
supersededStub: supersededStub ? stubSlug : null,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return { ok: true, person: updated, slug: targetSlug, superseded: supersededStub ? stubSlug : null };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = { verifyContact, candidateEntitiesFor };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inetafrica/open-claudia",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.1",
|
|
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"
|
|
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"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"bot.js",
|
|
@@ -25,7 +25,10 @@ function fixture() {
|
|
|
25
25
|
assert.strictEqual(f.events[0].payload.reason, "unsupported_media");
|
|
26
26
|
f = fixture();
|
|
27
27
|
await f.manager.incoming({ fromUserId: "owner-1", fromUserName: "Owner", chatId: "chat", roomId: 12, callType: "audio" });
|
|
28
|
-
|
|
28
|
+
// Ringing must go out first (caller UI feedback), then the accept.
|
|
29
|
+
assert.strictEqual(f.events[0].name, "call:ringing");
|
|
30
|
+
assert.deepStrictEqual(f.events[0].payload, { toUserId: "owner-1", chatId: "chat" });
|
|
31
|
+
assert.strictEqual(f.events[1].name, "call:accept");
|
|
29
32
|
f.manager.session.options.onTranscript("What is on my agenda?");
|
|
30
33
|
assert.strictEqual(f.inbound[0].payload.raw.ephemeralAudio, true);
|
|
31
34
|
// Call turns must mute observer banners (spoken otherwise) and flag live-call
|
|
@@ -29,8 +29,9 @@ class FakePC {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
class FakeSpeechStream {
|
|
32
|
-
constructor({ onTranscript } = {}) {
|
|
32
|
+
constructor({ onTranscript, onPartial } = {}) {
|
|
33
33
|
this.onTranscript = onTranscript;
|
|
34
|
+
this.onPartial = onPartial;
|
|
34
35
|
this.audioFrames = [];
|
|
35
36
|
this.commits = 0;
|
|
36
37
|
this.closedStream = false;
|
|
@@ -41,12 +42,12 @@ class FakeSpeechStream {
|
|
|
41
42
|
close() { this.closedStream = true; }
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
function makeSession({ synthesize, maxCallMs, idleMs, asCaller, greeting } = {}) {
|
|
45
|
+
function makeSession({ synthesize, maxCallMs, idleMs, finalWaitMs, asCaller, greeting } = {}) {
|
|
45
46
|
const ended = [];
|
|
46
47
|
const transcripts = [];
|
|
47
48
|
const session = new KazeeCallSession({
|
|
48
49
|
roomId: 7, chatId: "chat", callerId: "owner", botUserId: "bot", janusUrl: "ws://test",
|
|
49
|
-
maxCallMs, idleMs, asCaller, greeting,
|
|
50
|
+
maxCallMs, idleMs, finalWaitMs, asCaller, greeting,
|
|
50
51
|
synthesize: synthesize || (async () => ({ pcm: Buffer.alloc(WEBRTC_SAMPLES * 2 * 2), sampleRate: 48000 })),
|
|
51
52
|
onTranscript: (text, meta) => transcripts.push({ text, meta }),
|
|
52
53
|
onEnded: (reason) => ended.push(reason),
|
|
@@ -127,12 +128,98 @@ async function testPrebufferAndCommit() {
|
|
|
127
128
|
assert.strictEqual(sub.stt.audioFrames.length, 0, "silence stays out of STT");
|
|
128
129
|
for (let i = 0; i < 3; i += 1) session._onFeedRtp(101, { payload: toneOpus(gen, 12000) });
|
|
129
130
|
assert.strictEqual(sub.stt.audioFrames.length, 8, "onset flushes the prebuffer (5 quiet + 2 onset + current)");
|
|
131
|
+
sub.stt.onPartial("hello there"); // recogniser heard words, so the energy close commits directly
|
|
130
132
|
for (let i = 0; i < 36; i += 1) session._onFeedRtp(101, { payload: toneOpus(gen, 30) });
|
|
131
133
|
assert.strictEqual(sub.stt.commits, 1, "turn commits after sustained silence");
|
|
132
134
|
gen.close();
|
|
133
135
|
await session.close();
|
|
134
136
|
}
|
|
135
137
|
|
|
138
|
+
async function testHoldForWords() {
|
|
139
|
+
const { session } = makeSession({});
|
|
140
|
+
await session._subscribe(101, "caller");
|
|
141
|
+
const sub = session.subscribers.get(101);
|
|
142
|
+
const gen = new OpusAudioCodec();
|
|
143
|
+
for (let i = 0; i < 3; i += 1) session._onFeedRtp(101, { payload: toneOpus(gen, 12000) });
|
|
144
|
+
for (let i = 0; i < 36; i += 1) session._onFeedRtp(101, { payload: toneOpus(gen, 30) });
|
|
145
|
+
assert.strictEqual(sub.stt.commits, 0, "energy close with zero words holds the commit");
|
|
146
|
+
assert(sub.awaitingWordsSince > 0, "hold state armed");
|
|
147
|
+
sub.stt.onPartial("hello there");
|
|
148
|
+
assert.strictEqual(sub.stt.commits, 1, "first words release the held commit");
|
|
149
|
+
assert.strictEqual(sub.awaitingWordsSince, 0);
|
|
150
|
+
assert(session._thinkTimer, "released commit acks and thinks like a normal turn");
|
|
151
|
+
gen.close();
|
|
152
|
+
await session.close();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function testHoldTimeoutDropsNoiseTurn() {
|
|
156
|
+
const { session, transcripts } = makeSession({});
|
|
157
|
+
await session._subscribe(101, "caller");
|
|
158
|
+
const sub = session.subscribers.get(101);
|
|
159
|
+
const gen = new OpusAudioCodec();
|
|
160
|
+
for (let i = 0; i < 3; i += 1) session._onFeedRtp(101, { payload: toneOpus(gen, 12000) });
|
|
161
|
+
for (let i = 0; i < 36; i += 1) session._onFeedRtp(101, { payload: toneOpus(gen, 30) });
|
|
162
|
+
sub.awaitingWordsSince = Date.now() - 5000; // hold expired
|
|
163
|
+
session._onFeedRtp(101, { payload: toneOpus(gen, 30) });
|
|
164
|
+
assert.strictEqual(sub.stt.commits, 1, "expired hold still finalizes the audio");
|
|
165
|
+
assert.strictEqual(sub.noiseCommit, true);
|
|
166
|
+
assert.strictEqual(session._thinkTimer, null, "noise commit stays silent — no ack, no ticking");
|
|
167
|
+
sub.stt.onTranscript("uh"); // babble leaked a stray word into the transcript
|
|
168
|
+
assert.strictEqual(transcripts.length, 0, "sub-threshold noise transcript never dispatches");
|
|
169
|
+
gen.close();
|
|
170
|
+
await session.close();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function testWordStallRescue() {
|
|
174
|
+
const { session } = makeSession({});
|
|
175
|
+
await session._subscribe(101, "caller");
|
|
176
|
+
const sub = session.subscribers.get(101);
|
|
177
|
+
const gen = new OpusAudioCodec();
|
|
178
|
+
for (let i = 0; i < 3; i += 1) session._onFeedRtp(101, { payload: toneOpus(gen, 12000) });
|
|
179
|
+
sub.stt.onPartial("so the deploy");
|
|
180
|
+
sub.lastWordAt = Date.now() - 4500; // word timeline stalled well past its cadence
|
|
181
|
+
session._onFeedRtp(101, { payload: toneOpus(gen, 12000) }); // background keeps energy loud
|
|
182
|
+
assert.strictEqual(sub.stt.commits, 1, "stalled words commit even while energy stays loud");
|
|
183
|
+
assert.strictEqual(sub.vad.active, false, "rescue resets the energy turn");
|
|
184
|
+
gen.close();
|
|
185
|
+
await session.close();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function testInterimFallbackAndLateFinal() {
|
|
189
|
+
const { session, transcripts } = makeSession({ finalWaitMs: 30 });
|
|
190
|
+
await session._subscribe(101, "caller");
|
|
191
|
+
const sub = session.subscribers.get(101);
|
|
192
|
+
const gen = new OpusAudioCodec();
|
|
193
|
+
for (let i = 0; i < 3; i += 1) session._onFeedRtp(101, { payload: toneOpus(gen, 12000) });
|
|
194
|
+
sub.stt.onPartial("here is my answer");
|
|
195
|
+
for (let i = 0; i < 36; i += 1) session._onFeedRtp(101, { payload: toneOpus(gen, 30) });
|
|
196
|
+
assert.strictEqual(sub.stt.commits, 1);
|
|
197
|
+
assert.strictEqual(transcripts.length, 0, "dispatch waits for the final first");
|
|
198
|
+
await wait(80);
|
|
199
|
+
assert.strictEqual(transcripts.length, 1, "missing final falls back to the interim");
|
|
200
|
+
assert.strictEqual(transcripts[0].text, "here is my answer");
|
|
201
|
+
sub.stt.onTranscript("here is my answer sir");
|
|
202
|
+
assert.strictEqual(transcripts.length, 1, "late final after fallback is dropped, not double-answered");
|
|
203
|
+
gen.close();
|
|
204
|
+
await session.close();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function testTrickleBuffering() {
|
|
208
|
+
const { session } = makeSession({});
|
|
209
|
+
const added = [];
|
|
210
|
+
session.publisher = { pc: { addIceCandidate: async (c) => { added.push(c.candidate); } }, pending: [], remoteSet: false };
|
|
211
|
+
session._onPublisherEvent({ janus: "trickle", candidate: { candidate: "cand-early", sdpMid: "0" } });
|
|
212
|
+
assert.strictEqual(added.length, 0, "candidates before the answer are buffered");
|
|
213
|
+
assert.strictEqual(session.publisher.pending.length, 1);
|
|
214
|
+
session._flushTrickle(session.publisher);
|
|
215
|
+
assert.deepStrictEqual(added, ["cand-early"], "buffered candidates flush once the answer lands");
|
|
216
|
+
session._onPublisherEvent({ janus: "trickle", candidate: { candidate: "cand-late", sdpMid: "0" } });
|
|
217
|
+
assert.deepStrictEqual(added, ["cand-early", "cand-late"], "later candidates apply directly");
|
|
218
|
+
session._onPublisherEvent({ janus: "trickle", candidate: { completed: true } });
|
|
219
|
+
assert.strictEqual(added.length, 2, "end-of-candidates marker is ignored");
|
|
220
|
+
await session.close();
|
|
221
|
+
}
|
|
222
|
+
|
|
136
223
|
async function testTranscriptAttribution() {
|
|
137
224
|
const { session, transcripts } = makeSession({});
|
|
138
225
|
await session._subscribe(101, "alice@kazee");
|
|
@@ -245,8 +332,11 @@ async function testCallerModeRoomLifecycle() {
|
|
|
245
332
|
const spoken = [];
|
|
246
333
|
session.speak = async (text) => { spoken.push(text); return true; };
|
|
247
334
|
await session._subscribe(101, "sumeet@kazee");
|
|
335
|
+
assert.deepStrictEqual(spoken, [], "no greeting while the publisher media leg is still down");
|
|
336
|
+
session._publisherUp = true;
|
|
337
|
+
session._maybeGreet();
|
|
248
338
|
await session._subscribe(102, "second@kazee");
|
|
249
|
-
assert.deepStrictEqual(spoken, ["hi there"], "greeting speaks exactly once,
|
|
339
|
+
assert.deepStrictEqual(spoken, ["hi there"], "greeting speaks exactly once, after media-up + first remote feed");
|
|
250
340
|
await session.close();
|
|
251
341
|
const destroy = messages.find((m) => m?.request === "destroy");
|
|
252
342
|
assert.ok(destroy && destroy.room === 7, "created room is destroyed on close");
|
|
@@ -283,6 +373,7 @@ async function testThinkingCues() {
|
|
|
283
373
|
// plays an audible "heard you" chime, and starts ticking right away — STT
|
|
284
374
|
// finalization takes seconds and must not sound like a dead line.
|
|
285
375
|
for (let i = 0; i < 3; i += 1) session._onFeedRtp(101, { payload: toneOpus(gen, 12000) });
|
|
376
|
+
sub.stt.onPartial("what is the status");
|
|
286
377
|
for (let i = 0; i < 36; i += 1) session._onFeedRtp(101, { payload: toneOpus(gen, 30) });
|
|
287
378
|
assert.strictEqual(sub.stt.commits, 1);
|
|
288
379
|
await wait(60);
|
|
@@ -335,6 +426,11 @@ async function testCloseResolvesSpeech() {
|
|
|
335
426
|
await testSpeakCancellation();
|
|
336
427
|
await testEchoGateAndBargeIn();
|
|
337
428
|
await testPrebufferAndCommit();
|
|
429
|
+
await testHoldForWords();
|
|
430
|
+
await testHoldTimeoutDropsNoiseTurn();
|
|
431
|
+
await testWordStallRescue();
|
|
432
|
+
await testInterimFallbackAndLateFinal();
|
|
433
|
+
await testTrickleBuffering();
|
|
338
434
|
await testTranscriptAttribution();
|
|
339
435
|
await testIdleTimeout();
|
|
340
436
|
await testIdleBargeInKeepsCall();
|
|
@@ -365,7 +365,21 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
|
|
|
365
365
|
// 2h); a lapsed deadline with a still-failing check fires the wake anyway as
|
|
366
366
|
// a marked "Watch expired" prompt and removes the watch, so a watch whose
|
|
367
367
|
// condition never comes true can no longer end silently.
|
|
368
|
-
|
|
368
|
+
// 3.3.0 cut 2026-08-14 (verify-contact batch: owner-only verify flow, HMAC
|
|
369
|
+
// speaker attest on owner-gated loopback kinds, closed relay/transcript
|
|
370
|
+
// egress) — but the release commit bumped package.json WITHOUT this pin, so
|
|
371
|
+
// the tag's build job failed on this very assert: npm kept 3.2.3 and nothing
|
|
372
|
+
// was published. Never retag a failed version — re-cut as +1.
|
|
373
|
+
// 3.3.1 approved by Sumeet 2026-08-19 ("The tts timeout for long messages is
|
|
374
|
+
// getting to be a problem Please fix it", with the sales-bot failure
|
|
375
|
+
// screenshot): pocket TTS sentence-packs long text into ≤450-char chunks,
|
|
376
|
+
// synthesizes each inside its own 120s window (one retry per chunk), and
|
|
377
|
+
// ffmpeg-concats the wavs into ONE ogg — message length can no longer abort
|
|
378
|
+
// the single gateway request and strand pods (no ElevenLabs key, no macOS
|
|
379
|
+
// say) voiceless. Carries the unpublished 3.3.0 content plus the Kazee calls
|
|
380
|
+
// batch (trickle ICE + streaming TTS playback, ringing-on-invite/deferred
|
|
381
|
+
// publish, werift ICE rejection suppression). Plus this pin repair.
|
|
382
|
+
assert.strictEqual(pkg.version, "3.3.1", "release version must remain unchanged without explicit approval");
|
|
369
383
|
for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
|
|
370
384
|
assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
|
|
371
385
|
}
|