@inetafrica/open-claudia 3.5.12 → 3.5.13
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/.env.example +2 -0
- package/CHANGELOG.md +8 -0
- package/README.md +3 -2
- package/core/runner.js +19 -3
- package/core/usage-log.js +10 -1
- package/package.json +3 -2
- package/test-group-status-notes.js +140 -0
- package/test-provider-language.js +8 -1
package/.env.example
CHANGED
|
@@ -63,6 +63,8 @@ OC_THREAD_TITLES=on
|
|
|
63
63
|
THREAD_TITLE_PROVIDER=
|
|
64
64
|
THREAD_TITLE_MODEL_CLAUDE=
|
|
65
65
|
THREAD_TITLE_MODEL_CODEX=
|
|
66
|
+
# Token usage alerts to the owner: on | off (default off). Never posted in group chats.
|
|
67
|
+
USAGE_ALERTS=off
|
|
66
68
|
USAGE_ALERT_CONTEXT_TOKENS=120000
|
|
67
69
|
USAGE_ALERT_RATE_MULTIPLIER=1.75
|
|
68
70
|
USAGE_ALERT_BASELINE_TURNS=20
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.5.13
|
|
4
|
+
|
|
5
|
+
Group chats stop seeing notes meant only for the owner, and token usage alerts are off by default. Canary first, on the sales bot.
|
|
6
|
+
|
|
7
|
+
- **Fix: no more junk "📌 standing rule" posts.** After every owner turn the rule detector scanned the whole prompt, including the group or thread messages the bot was shown, so it saved the bot's own lines as the owner's rules. Its own "📌 Got it — I'll keep this loaded as a standing rule from now on" note then set off the next one, one more after every owner message in a staff group (sales bot, 4 Oct; same cause as the 1 Oct thread report). Rules now come only from the owner's own words (the typed message, voice transcript or caption), and only in a direct chat with the bot: group chats never capture rules or see 📌 notes.
|
|
8
|
+
- **Token usage alerts are off by default** (Sumeet, 4 Oct: "turn off this token usage thing by default on all the bots"). `USAGE_ALERTS=on` brings them back, with the same `USAGE_ALERT_*` thresholds, and even then they never go to a group chat.
|
|
9
|
+
- **Tests:** `test-group-status-notes.js` (new).
|
|
10
|
+
|
|
3
11
|
## 3.5.12
|
|
4
12
|
|
|
5
13
|
Two fixes: "No response requested." no longer reaches the chat, and voice notes wait out a busy speech server. Canary first, on the sales bot. (3.5.11 was tagged but never published: its release run failed on the new test, which assumed an agent CLI is installed; CI has none.)
|
package/README.md
CHANGED
|
@@ -448,8 +448,9 @@ All stored in `~/.open-claudia/`:
|
|
|
448
448
|
| `AGENT_ENV_PASSTHROUGH` | No | Comma-separated project environment keys to pass to provider children; reserved provider/control/keyring keys are always filtered and re-added only by the selected provider |
|
|
449
449
|
| `CLAUDE_MODEL` / `CODEX_MODEL` | No | Optional default model for the matching provider |
|
|
450
450
|
| `AUTO_COMPACT_TOKENS` | No | Auto-compact threshold in tokens (also settable via `/compactwindow`) |
|
|
451
|
-
| `
|
|
452
|
-
| `
|
|
451
|
+
| `USAGE_ALERTS` | No | Token usage alerts to the owner: `on` or `off` (default `off`). Never posted in group chats. The thresholds below apply only when on |
|
|
452
|
+
| `USAGE_ALERT_CONTEXT_TOKENS` | No | With alerts on: alert when one completed turn's context tokens exceed this ceiling (default `120000`, `off` disables) |
|
|
453
|
+
| `USAGE_ALERT_RATE_MULTIPLIER` | No | With alerts on: alert when the latest context-token rate exceeds the recent baseline by this multiple (default `1.75`, `off` disables) |
|
|
453
454
|
| `USAGE_ALERT_BASELINE_TURNS` / `USAGE_ALERT_MIN_BASELINE_TURNS` / `USAGE_ALERT_COOLDOWN_MS` | No | Tune token-rate baseline size, minimum sample size, and alert cooldown |
|
|
454
455
|
| `MEMORY_RECALL_MAX_CHARS` | No | Hard cap for auto-injected pack/entity memory per turn (default `9000`, `off` disables auto recall injection) |
|
|
455
456
|
| `RECALL_ENGINE` | No | Default recall engine when a chat hasn't set one via `/engine` (`classic` or `discoverer`, default `discoverer`) |
|
package/core/runner.js
CHANGED
|
@@ -234,10 +234,13 @@ function logTurnUsage(state, usage, costUsd, announce, opts = {}) {
|
|
|
234
234
|
if (result.alert) {
|
|
235
235
|
state.usageAlertLastAt = Date.now();
|
|
236
236
|
// Owner-only: version/model/context internals must never surface to an
|
|
237
|
-
// external speaker, regardless of the usage-alert policy.
|
|
237
|
+
// external speaker, regardless of the usage-alert policy. Never in a group
|
|
238
|
+
// chat either: everyone else in the room sees only noise.
|
|
238
239
|
let usageOwnerOnly = true;
|
|
239
240
|
try { usageOwnerOnly = !require("./relationship").isCurrentSpeakerGuarded(); } catch (e) {}
|
|
240
|
-
|
|
241
|
+
let inGroup = false;
|
|
242
|
+
try { inGroup = require("./context").currentIsGroup(); } catch (e) {}
|
|
243
|
+
if (announce && usageOwnerOnly && !inGroup) {
|
|
241
244
|
const baseline = result.trend?.baselineAvgContextTokens || 0;
|
|
242
245
|
const rate = result.trend?.latestRate || 0;
|
|
243
246
|
const contextLabel = usageScope === "session_total" && !hasPeak ? "Session context total" : "Context this turn";
|
|
@@ -2231,16 +2234,27 @@ function nameThreadAfterTurn(result, runContext, store) {
|
|
|
2231
2234
|
// detector spots durable-rule statements ("from now on / never / stop") in the
|
|
2232
2235
|
// owner's message and captures them into lessons.md (or asks first). Owner-only
|
|
2233
2236
|
// and best-effort: it never blocks or breaks a turn.
|
|
2237
|
+
//
|
|
2238
|
+
// Only the owner's own words count: store.inboundText (the typed message,
|
|
2239
|
+
// voice transcript or caption, set by admitGuardedTurn), never the prompt,
|
|
2240
|
+
// which also carries injected group/thread messages, quoted replies and the
|
|
2241
|
+
// bot's own earlier lines. Reading the prompt saved the bot's own "📌 ... from
|
|
2242
|
+
// now on" notes as the owner's rules, one more after every owner turn in a
|
|
2243
|
+
// group (sales bot, 4 Oct 2026). Group chats never capture: a rule note there
|
|
2244
|
+
// is noise for everyone else, so rules are set in a direct chat with the bot.
|
|
2234
2245
|
async function considerGuardrail(result, runContext, store) {
|
|
2235
2246
|
try {
|
|
2236
2247
|
if (!result.ok || runContext.purpose !== "foreground") return;
|
|
2248
|
+
if (store?.isGroup) return;
|
|
2249
|
+
const ownWords = String(store?.inboundText || "").trim();
|
|
2250
|
+
if (!ownWords) return;
|
|
2237
2251
|
const guardrail = require("./guardrail");
|
|
2238
2252
|
if (!guardrail.enabled()) return;
|
|
2239
2253
|
const relationship = require("./relationship");
|
|
2240
2254
|
const speaker = relationship.speakerFor(store?.adapter?.type, store?.channelId, store?.userId);
|
|
2241
2255
|
if (relationship.guardActive(speaker)) return; // external speakers never capture
|
|
2242
2256
|
const announce = (text, opts) => chatContext.run(store, () => (statusChatterMuted() ? true : send(text, opts)));
|
|
2243
|
-
await guardrail.consider({ text:
|
|
2257
|
+
await guardrail.consider({ text: ownWords, speaker, announce });
|
|
2244
2258
|
} catch (e) { /* best-effort — a guardrail miss must never break a turn */ }
|
|
2245
2259
|
}
|
|
2246
2260
|
|
|
@@ -2718,4 +2732,6 @@ module.exports = {
|
|
|
2718
2732
|
archiveCompactionBrief,
|
|
2719
2733
|
collectRepoStateFacts,
|
|
2720
2734
|
collectRecentTurns,
|
|
2735
|
+
considerGuardrail,
|
|
2736
|
+
logTurnUsage,
|
|
2721
2737
|
};
|
package/core/usage-log.js
CHANGED
|
@@ -56,11 +56,20 @@ function floatEnv(env, key, fallback) {
|
|
|
56
56
|
return Number.isFinite(n) ? n : fallback;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// Token usage alerts are an operator's tuning aid, so they are off unless
|
|
60
|
+
// USAGE_ALERTS=on (Sumeet, 4 Oct 2026: "turn off this token usage thing by
|
|
61
|
+
// default on all the bots", after one landed in a staff group). The
|
|
62
|
+
// thresholds below only apply once they are switched on.
|
|
63
|
+
function usageAlertsOn(env = {}) {
|
|
64
|
+
const raw = env.USAGE_ALERTS ?? process.env.USAGE_ALERTS;
|
|
65
|
+
return /^(on|true|yes|1)$/i.test(String(raw ?? "").trim());
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
function usageAlertPolicy(env = {}) {
|
|
60
69
|
const absoluteContextTokens = intEnv(env, "USAGE_ALERT_CONTEXT_TOKENS", 120000);
|
|
61
70
|
const rateMultiplier = floatEnv(env, "USAGE_ALERT_RATE_MULTIPLIER", 1.75);
|
|
62
71
|
return {
|
|
63
|
-
enabled: absoluteContextTokens > 0 || rateMultiplier > 0,
|
|
72
|
+
enabled: usageAlertsOn(env) && (absoluteContextTokens > 0 || rateMultiplier > 0),
|
|
64
73
|
absoluteContextTokens,
|
|
65
74
|
rateMultiplier,
|
|
66
75
|
baselineTurns: intEnv(env, "USAGE_ALERT_BASELINE_TURNS", 20),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inetafrica/open-claudia",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.13",
|
|
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": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"test:kazee-call": "node test-kazee-call-audio.js && node test-kazee-call-janus.js && node test-kazee-call-webrtc.js && node test-kazee-call-speech.js && node test-kazee-call-manager.js && node test-call-speech.js && node test-kazee-call-session.js",
|
|
11
11
|
"setup": "node setup.js",
|
|
12
12
|
"start": "node bot.js",
|
|
13
|
-
"pretest": "node test-provider-fixture.js && node test-provider-core-prompt.js && node test-turn-observer.js && node test-provider-prompt-parity.js && node test-provider-env-isolation.js && node test-provider-resume-parity.js && node test-provider-bootstrap.js && node test-provider-registry.js && node test-provider-stream-decoder.js && node test-provider-events.js && node test-provider-claude.js && node test-provider-codex.js && node test-codex-device-login.js && node test-provider-runner.js && node test-provider-refusal-recovery.js && node test-provider-run-lifecycle.js && node test-provider-gateway.js && node test-provider-side-chat.js && node test-single-runtime.js && node test-provider-migration-backup.js && node test-provider-state-migration.js && node test-provider-session-history.js && node test-provider-session-commands.js && node test-provider-compaction.js && node test-provider-scheduler.js && node test-credential-capture.js && node test-cursor-removal.js && node test-utility-provider-policy.js && node test-provider-subagent.js && node test-provider-recall.js && node test-provider-pack-review.js && node test-memory-mutation-queue.js && node test-provider-dream.js && node test-provider-enforcer.js && node test-provider-tool-hooks.js && node test-provider-boot-matrix.js && node test-provider-capabilities.js && node test-provider-language.js && node test-upgrade-path.js && node test-provider-coupling-audit.js && node test-provider-parity-e2e.js && node test-kazee-channel-health.js && node test-kazee-media-caption.js && node test-kazee-context-media.js && node test-kazee-typing-join.js && node test-kazee-inbound-dedup.js && node test-typing-phase-push.js && node test-socket-reconnect.js && node test-speech-loopback.js && node test-prompt-budget.js && node test-agency-ledger.js && node test-agency-deliberate.js && node test-guardrail.js && node test-spaces-comment-content.js && node test-boot-health.js && node test-lexical-floor.js && node test-safe-mode.js && node test-logout.js && node test-kazee-call-audio.js && node test-kazee-call-janus.js && node test-kazee-call-webrtc.js && node test-kazee-call-speech.js && node test-kazee-call-manager.js && node test-call-speech.js && node test-kazee-call-session.js",
|
|
13
|
+
"pretest": "node test-provider-fixture.js && node test-provider-core-prompt.js && node test-turn-observer.js && node test-provider-prompt-parity.js && node test-provider-env-isolation.js && node test-provider-resume-parity.js && node test-provider-bootstrap.js && node test-provider-registry.js && node test-provider-stream-decoder.js && node test-provider-events.js && node test-provider-claude.js && node test-provider-codex.js && node test-codex-device-login.js && node test-provider-runner.js && node test-provider-refusal-recovery.js && node test-provider-run-lifecycle.js && node test-provider-gateway.js && node test-provider-side-chat.js && node test-single-runtime.js && node test-provider-migration-backup.js && node test-provider-state-migration.js && node test-provider-session-history.js && node test-provider-session-commands.js && node test-provider-compaction.js && node test-provider-scheduler.js && node test-credential-capture.js && node test-cursor-removal.js && node test-utility-provider-policy.js && node test-provider-subagent.js && node test-provider-recall.js && node test-provider-pack-review.js && node test-memory-mutation-queue.js && node test-provider-dream.js && node test-provider-enforcer.js && node test-provider-tool-hooks.js && node test-provider-boot-matrix.js && node test-provider-capabilities.js && node test-provider-language.js && node test-upgrade-path.js && node test-provider-coupling-audit.js && node test-provider-parity-e2e.js && node test-kazee-channel-health.js && node test-kazee-media-caption.js && node test-kazee-context-media.js && node test-kazee-typing-join.js && node test-kazee-inbound-dedup.js && node test-typing-phase-push.js && node test-socket-reconnect.js && node test-speech-loopback.js && node test-prompt-budget.js && node test-agency-ledger.js && node test-agency-deliberate.js && node test-guardrail.js && node test-group-status-notes.js && node test-spaces-comment-content.js && node test-boot-health.js && node test-lexical-floor.js && node test-safe-mode.js && node test-logout.js && node test-kazee-call-audio.js && node test-kazee-call-janus.js && node test-kazee-call-webrtc.js && node test-kazee-call-speech.js && node test-kazee-call-manager.js && node test-call-speech.js && node test-kazee-call-session.js",
|
|
14
14
|
"test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && node test-codex-rollout.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-query.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-walker-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-score-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cokept-warming.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-transcript-room-scope.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-search.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-extractive-injection.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-mandate-hygiene.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-external-rule-scopes.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-cards.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-group-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-people-owner-seed.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-thread-lanes.js && node test-thread-titles.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-thread-stop.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-typing-heartbeat.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-web-sessions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-poll-recovery.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-409-grace.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-conflict-heal-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-single-instance.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-streaming-split.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-stream-preview-purpose.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-spaces-autoenable.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-say-loopback.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-loopback-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-transcript-threads.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-union.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-subst-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-section-budgets.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-dedup-adjudicator.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-prospective.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-regressions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cost-report.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-context.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-mining.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-certification.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-apprenticeship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-attest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-attest.js && node test-sandbox-probe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-bot-endpoint.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cli-owner-gates.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-verify-contact.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-loopback-owner-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-watch-check-env.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-intro-adoption.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-grants.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-room-provider-settings.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-store-durability.js && node test-kazee-cards.js && node test-passive-replies.js && node test-kazee-bot-loops.js && node test-placeholder-reply.js && node test-media-speech.js && node test-passive-replies-router.js && node test-loopback-cards.js"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
@@ -85,6 +85,7 @@
|
|
|
85
85
|
"test-agency-ledger.js",
|
|
86
86
|
"test-agency-deliberate.js",
|
|
87
87
|
"test-guardrail.js",
|
|
88
|
+
"test-group-status-notes.js",
|
|
88
89
|
"test-delivery-contract.js",
|
|
89
90
|
"test-run-lock.js",
|
|
90
91
|
"test-thread-lanes.js",
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Notes meant only for the owner stay out of group chats, and a standing rule
|
|
2
|
+
// comes only from the owner's own words. Sales bot, 4 Oct 2026: after each of
|
|
3
|
+
// Sumeet's turns in a staff group it posted "📌 Got it — I'll keep this loaded
|
|
4
|
+
// as a standing rule from now on" and saved its OWN previous 📌 line (shown to
|
|
5
|
+
// it as group context) as his rule, one more per turn; a "Token usage alert"
|
|
6
|
+
// landed in the same group. Token alerts are now off unless USAGE_ALERTS=on.
|
|
7
|
+
// No network, redirected config dir.
|
|
8
|
+
|
|
9
|
+
const assert = require("assert");
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const os = require("os");
|
|
12
|
+
const path = require("path");
|
|
13
|
+
|
|
14
|
+
const TMP = fs.mkdtempSync(path.join(os.tmpdir(), "oc-group-notes-"));
|
|
15
|
+
process.env.OPEN_CLAUDIA_TEST = "1";
|
|
16
|
+
process.env.OPEN_CLAUDIA_CONFIG_DIR = TMP;
|
|
17
|
+
process.env.WORKSPACE = TMP;
|
|
18
|
+
process.env.GUARDRAIL_CAPTURE = "on";
|
|
19
|
+
delete process.env.USAGE_ALERTS;
|
|
20
|
+
|
|
21
|
+
const lessons = require("./core/lessons");
|
|
22
|
+
const guardrail = require("./core/guardrail");
|
|
23
|
+
const relationship = require("./core/relationship");
|
|
24
|
+
const { runInChat } = require("./core/context");
|
|
25
|
+
const { usageAlertPolicy, evaluateUsageAlert } = require("./core/usage-log");
|
|
26
|
+
const { considerGuardrail, logTurnUsage } = require("./core/runner");
|
|
27
|
+
|
|
28
|
+
// The speaker is the owner throughout: who may capture at all is
|
|
29
|
+
// test-guardrail.js's job, this file is about which words and which rooms.
|
|
30
|
+
relationship.speakerFor = () => ({ isOwner: true, channelId: "kazee:room" });
|
|
31
|
+
relationship.guardActive = () => false;
|
|
32
|
+
relationship.isCurrentSpeakerGuarded = () => false;
|
|
33
|
+
|
|
34
|
+
function resetLessons() {
|
|
35
|
+
try { fs.rmSync(lessons.LESSONS_FILE, { force: true }); } catch (e) {}
|
|
36
|
+
try { fs.rmSync(lessons.LESSONS_META_FILE, { force: true }); } catch (e) {}
|
|
37
|
+
try { fs.rmSync(guardrail.PENDING_FILE, { force: true }); } catch (e) {}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// A chat store as the router builds it: inboundText is what the person said
|
|
41
|
+
// (typed text, voice transcript or caption), isGroup marks a group room.
|
|
42
|
+
function chat({ isGroup, inboundText }) {
|
|
43
|
+
const sent = [];
|
|
44
|
+
const adapter = {
|
|
45
|
+
type: "kazee",
|
|
46
|
+
send: async (channelId, text) => { sent.push(text); return { ok: true, messageId: `m${sent.length}` }; },
|
|
47
|
+
};
|
|
48
|
+
return { sent, store: { adapter, channelId: "kazee:room", userId: "owner", isGroup, inboundText } };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const OK = { ok: true };
|
|
52
|
+
const turn = (userPrompt) => ({ purpose: "foreground", userPrompt });
|
|
53
|
+
const RULE = "From now on, always send the deep link after posting a Spaces comment.";
|
|
54
|
+
const PIN = "📌 Got it — I'll keep this loaded as a standing rule from now on";
|
|
55
|
+
|
|
56
|
+
(async () => {
|
|
57
|
+
// 1. The live case: a question in a group whose context starts with the
|
|
58
|
+
// bot's own last 📌 note. Nothing is saved and nothing is posted.
|
|
59
|
+
resetLessons();
|
|
60
|
+
let c = chat({ isGroup: true, inboundText: "How many leads did we get last month?" });
|
|
61
|
+
await considerGuardrail(OK, turn([
|
|
62
|
+
"[Group context] Messages since your last reply:",
|
|
63
|
+
` [1d ago] You: ${PIN}`,
|
|
64
|
+
" [2m ago] Sumeet Rajpal: How many leads did we get last month?",
|
|
65
|
+
"",
|
|
66
|
+
"How many leads did we get last month?",
|
|
67
|
+
].join("\n")), c.store);
|
|
68
|
+
assert.deepStrictEqual(lessons.readLessons(), [], "the bot's own 📌 line in the context is not the owner's rule");
|
|
69
|
+
assert.deepStrictEqual(c.sent, [], "no 📌 note in the group");
|
|
70
|
+
|
|
71
|
+
// 2. A direct thread with the bot quoting its earlier 📌 note: the owner's
|
|
72
|
+
// own words hold no rule, so nothing is saved.
|
|
73
|
+
resetLessons();
|
|
74
|
+
c = chat({ isGroup: false, inboundText: "And deals?" });
|
|
75
|
+
await considerGuardrail(OK, turn(`[Kazee thread context]\n [36s ago] You: ${PIN}\n\nAnd deals?`), c.store);
|
|
76
|
+
assert.deepStrictEqual(lessons.readLessons(), [], "a quoted bot line never becomes a rule");
|
|
77
|
+
assert.deepStrictEqual(c.sent, []);
|
|
78
|
+
|
|
79
|
+
// 3. The owner states a rule in a direct chat: saved, and the 📌 note goes
|
|
80
|
+
// to that chat with its Undo.
|
|
81
|
+
resetLessons();
|
|
82
|
+
c = chat({ isGroup: false, inboundText: RULE });
|
|
83
|
+
await considerGuardrail(OK, turn(`[Kazee thread context]\n [5m ago] You: Done.\n\n${RULE}`), c.store);
|
|
84
|
+
const saved = lessons.readLessons();
|
|
85
|
+
assert.strictEqual(saved.length, 1, "the owner's own rule is saved");
|
|
86
|
+
assert.ok(/deep link/i.test(saved[0].text), saved[0].text);
|
|
87
|
+
assert.strictEqual(c.sent.length, 1);
|
|
88
|
+
assert.ok(c.sent[0].startsWith("📌"), c.sent[0]);
|
|
89
|
+
|
|
90
|
+
// 4. The same words in a group: rules are set in a direct chat, so the
|
|
91
|
+
// group gets no note and nothing is saved.
|
|
92
|
+
resetLessons();
|
|
93
|
+
c = chat({ isGroup: true, inboundText: RULE });
|
|
94
|
+
await considerGuardrail(OK, turn(RULE), c.store);
|
|
95
|
+
assert.deepStrictEqual(lessons.readLessons(), [], "groups never capture");
|
|
96
|
+
assert.deepStrictEqual(c.sent, []);
|
|
97
|
+
|
|
98
|
+
// 5. A turn with none of the owner's words (wake-up, cron): the prompt alone
|
|
99
|
+
// never captures, even when it reads like a rule.
|
|
100
|
+
resetLessons();
|
|
101
|
+
c = chat({ isGroup: false, inboundText: "" });
|
|
102
|
+
await considerGuardrail(OK, turn(`[Scheduled wakeup "x" fired] ${RULE}`), c.store);
|
|
103
|
+
assert.deepStrictEqual(lessons.readLessons(), [], "no own words, no capture");
|
|
104
|
+
|
|
105
|
+
// Token usage alerts: off by default; thresholds alone do not switch them
|
|
106
|
+
// on; USAGE_ALERTS=on does, unless both thresholds are off.
|
|
107
|
+
const big = { contextTokens: 127758 };
|
|
108
|
+
assert.strictEqual(usageAlertPolicy({}).enabled, false, "alerts are off by default");
|
|
109
|
+
assert.strictEqual(evaluateUsageAlert([], big, usageAlertPolicy({})).alert, false);
|
|
110
|
+
assert.strictEqual(usageAlertPolicy({ USAGE_ALERT_CONTEXT_TOKENS: "120000" }).enabled, false);
|
|
111
|
+
assert.strictEqual(usageAlertPolicy({ USAGE_ALERTS: "on" }).enabled, true);
|
|
112
|
+
assert.strictEqual(evaluateUsageAlert([], big, usageAlertPolicy({ USAGE_ALERTS: "on" })).alert, true);
|
|
113
|
+
assert.strictEqual(usageAlertPolicy({
|
|
114
|
+
USAGE_ALERTS: "on", USAGE_ALERT_CONTEXT_TOKENS: "off", USAGE_ALERT_RATE_MULTIPLIER: "off",
|
|
115
|
+
}).enabled, false);
|
|
116
|
+
|
|
117
|
+
// The turn's usage log: a 127.8k-context turn announces nothing by default,
|
|
118
|
+
// announces once in a direct chat with USAGE_ALERTS=on, and never in a group.
|
|
119
|
+
const usage = { input_tokens: 1000, output_tokens: 200, cache_read_input_tokens: 126558 };
|
|
120
|
+
async function alertsFor({ isGroup }) {
|
|
121
|
+
const alerts = [];
|
|
122
|
+
const announce = async (text) => { alerts.push(text); };
|
|
123
|
+
const state = { userId: "owner", sessionUsage: {} };
|
|
124
|
+
const { store } = chat({ isGroup, inboundText: "And deals?" });
|
|
125
|
+
await runInChat({ ...store }, async () => logTurnUsage(state, usage, 0, announce, {
|
|
126
|
+
backend: "claude", model: "claude-test", sessionId: "s1", liveContextTokens: 127758,
|
|
127
|
+
}));
|
|
128
|
+
return alerts;
|
|
129
|
+
}
|
|
130
|
+
assert.deepStrictEqual(await alertsFor({ isGroup: false }), [], "off by default in a direct chat too");
|
|
131
|
+
process.env.USAGE_ALERTS = "on";
|
|
132
|
+
const direct = await alertsFor({ isGroup: false });
|
|
133
|
+
assert.strictEqual(direct.length, 1, "USAGE_ALERTS=on announces in a direct chat");
|
|
134
|
+
assert.ok(direct[0].startsWith("Token usage alert:"), direct[0]);
|
|
135
|
+
assert.deepStrictEqual(await alertsFor({ isGroup: true }), [], "never in a group");
|
|
136
|
+
delete process.env.USAGE_ALERTS;
|
|
137
|
+
|
|
138
|
+
console.log("group status notes: ok");
|
|
139
|
+
process.exit(0);
|
|
140
|
+
})().catch((error) => { console.error(error); process.exit(1); });
|
|
@@ -546,7 +546,14 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
|
|
|
546
546
|
// the speech queue and say why they fail. 3.5.11 was tagged but never
|
|
547
547
|
// published (its release run failed on a test), so the same release ships as
|
|
548
548
|
// 3.5.12. Canary on the sales bot only.
|
|
549
|
-
|
|
549
|
+
// 3.5.13 approved by Sumeet 2026-10-04 12:27 EAT ("Yes, fix and ship" on the card
|
|
550
|
+
// "Switch token alerts off on every bot, fix the junk 📌 posts, and ship 3.5.13
|
|
551
|
+
// to the sales bot?"), after asking at 11:44 EAT ("Can we turn of[f] this token
|
|
552
|
+
// usage thing by default on all the bots"): rules come only from the owner's own
|
|
553
|
+
// words in a direct chat, so group chats never get junk "standing rule" posts,
|
|
554
|
+
// and token usage alerts are off by default and never go to a group chat.
|
|
555
|
+
// Canary on the sales bot only.
|
|
556
|
+
assert.strictEqual(pkg.version, "3.5.13", "release version must remain unchanged without explicit approval");
|
|
550
557
|
for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
|
|
551
558
|
assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
|
|
552
559
|
}
|