switchroom 0.17.6 → 0.17.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-scheduler/index.js +38 -4
- package/dist/auth-broker/index.js +302 -203
- package/dist/cli/notion-write-pretool.mjs +35 -2
- package/dist/cli/switchroom.js +1178 -576
- package/dist/host-control/main.js +148 -14
- package/dist/vault/approvals/kernel-server.js +140 -55
- package/dist/vault/broker/server.js +142 -57
- package/package.json +1 -1
- package/profiles/_base/start.sh.hbs +50 -6
- package/profiles/default/CLAUDE.md +116 -0
- package/skills/mental-model-curator/SKILL.md +162 -0
- package/telegram-plugin/bridge/bridge.ts +80 -1
- package/telegram-plugin/bridge/ipc-client.ts +19 -0
- package/telegram-plugin/bridge/permission-ledger.ts +61 -0
- package/telegram-plugin/consolidation-legibility.ts +279 -0
- package/telegram-plugin/dist/bridge/bridge.js +85 -1
- package/telegram-plugin/dist/gateway/gateway.js +2565 -610
- package/telegram-plugin/dist/server.js +86 -2
- package/telegram-plugin/feed-heartbeat-climb.ts +206 -0
- package/telegram-plugin/gateway/activity-card-store.ts +293 -0
- package/telegram-plugin/gateway/gateway.ts +1376 -82
- package/telegram-plugin/gateway/inbound-spool.ts +22 -0
- package/telegram-plugin/gateway/mental-model-propose-card.ts +69 -0
- package/telegram-plugin/gateway/mental-model-propose-diff.ts +171 -0
- package/telegram-plugin/gateway/mental-model-propose-inbound-builders.ts +147 -0
- package/telegram-plugin/gateway/mental-model-propose-resolve.ts +201 -0
- package/telegram-plugin/gateway/missed-approvals-card.ts +161 -0
- package/telegram-plugin/gateway/missed-approvals-store.ts +167 -0
- package/telegram-plugin/gateway/permission-rearm.ts +115 -0
- package/telegram-plugin/gateway/scoped-grant-store.ts +89 -0
- package/telegram-plugin/memory-legibility.ts +217 -0
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/telegram-plugin/scoped-approval.ts +59 -0
- package/telegram-plugin/silent-end.ts +78 -0
- package/telegram-plugin/subagent-watcher.ts +60 -6
- package/telegram-plugin/tests/activity-card-store.test.ts +436 -0
- package/telegram-plugin/tests/activity-card-wiring.test.ts +88 -0
- package/telegram-plugin/tests/consolidation-legibility.test.ts +224 -0
- package/telegram-plugin/tests/emission-authority-facade.test.ts +25 -10
- package/telegram-plugin/tests/feed-heartbeat-liveness-open.test.ts +33 -9
- package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +3 -3
- package/telegram-plugin/tests/inbound-spool.test.ts +105 -0
- package/telegram-plugin/tests/memory-legibility.test.ts +216 -0
- package/telegram-plugin/tests/mental-model-propose-callback-gate.test.ts +67 -0
- package/telegram-plugin/tests/mental-model-propose-card.test.ts +56 -0
- package/telegram-plugin/tests/mental-model-propose-diff.test.ts +201 -0
- package/telegram-plugin/tests/mental-model-propose-inbound-builders.test.ts +68 -0
- package/telegram-plugin/tests/mental-model-propose-resolve.test.ts +157 -0
- package/telegram-plugin/tests/missed-approvals-card.test.ts +145 -0
- package/telegram-plugin/tests/missed-approvals-store.test.ts +147 -0
- package/telegram-plugin/tests/missed-approvals-wiring.test.ts +89 -0
- package/telegram-plugin/tests/permission-ledger.test.ts +166 -0
- package/telegram-plugin/tests/permission-no-repeat-wiring.test.ts +1 -1
- package/telegram-plugin/tests/permission-rearm-wiring.test.ts +175 -0
- package/telegram-plugin/tests/permission-rearm.test.ts +126 -0
- package/telegram-plugin/tests/scoped-grant-persist.test.ts +223 -0
- package/telegram-plugin/tests/silent-end-transport.test.ts +290 -0
- package/telegram-plugin/tests/silent-turn-climb-transport.test.ts +337 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +139 -0
- package/telegram-plugin/tests/worktree-watch-cwds.test.ts +103 -0
- package/telegram-plugin/uat/assertions.ts +88 -4
- package/telegram-plugin/uat/feed-matcher.test.ts +69 -0
- package/telegram-plugin/uat/scenarios/fuzz-liveness-climb-dm.test.ts +155 -0
- package/telegram-plugin/uat/scenarios/jtbd-directive-capture-nudge-dm.test.ts +185 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-climb-channel.test.ts +192 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-climb-dm.test.ts +220 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-channel.test.ts +137 -0
- package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-dm.test.ts +148 -0
- package/telegram-plugin/uat/scenarios/jtbd-memory-legibility-channel.test.ts +66 -0
- package/telegram-plugin/uat/scenarios/jtbd-memory-legibility-dm.test.ts +61 -0
- package/telegram-plugin/uat/scenarios/silent-end-recovery-channel.test.ts +136 -0
- package/telegram-plugin/uat/scenarios/silent-end-recovery-dm.test.ts +24 -2
- package/telegram-plugin/worktree-watch-cwds.ts +60 -0
- package/vendor/hindsight-memory/hooks/hooks.json +9 -0
- package/vendor/hindsight-memory/scripts/__pycache__/directive_verify.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/drain_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/recall.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/retain.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/session_end.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/directive_verify.py +445 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/bank.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/client.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/config.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/content.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/daemon.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/directives.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/gateway_ipc.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/llm.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/switchroom_envelope.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/config.py +37 -0
- package/vendor/hindsight-memory/scripts/lib/directives.py +88 -0
- package/vendor/hindsight-memory/scripts/lib/switchroom_envelope.py +77 -0
- package/vendor/hindsight-memory/scripts/recall.py +153 -4
- package/vendor/hindsight-memory/scripts/retain.py +17 -0
- package/vendor/hindsight-memory/scripts/setup_hooks.py +9 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/__init__.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_switchroom_envelope.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/test_directive_capture_nudge.py +185 -0
- package/vendor/hindsight-memory/scripts/tests/test_directive_verify.py +516 -0
- package/vendor/hindsight-memory/scripts/tests/test_directives.py +49 -0
- package/vendor/hindsight-memory/scripts/tests/test_retain_window.py +66 -1
- package/vendor/hindsight-memory/scripts/tests/test_switchroom_envelope.py +69 -0
- package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.0.3.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/test_recall_exit_codes.py +49 -2
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
2
4
|
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
3
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
4
33
|
var __returnValue = (v) => v;
|
|
5
34
|
function __exportSetter(name, newValue) {
|
|
@@ -14,6 +43,7 @@ var __export = (target, all) => {
|
|
|
14
43
|
set: __exportSetter.bind(all, name)
|
|
15
44
|
});
|
|
16
45
|
};
|
|
46
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
47
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
18
48
|
|
|
19
49
|
// node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/identity.js
|
|
@@ -6931,60 +6961,99 @@ var require_public_api = __commonJS((exports) => {
|
|
|
6931
6961
|
exports.stringify = stringify;
|
|
6932
6962
|
});
|
|
6933
6963
|
|
|
6964
|
+
// node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/index.js
|
|
6965
|
+
var require_dist = __commonJS((exports) => {
|
|
6966
|
+
var composer = require_composer();
|
|
6967
|
+
var Document = require_Document();
|
|
6968
|
+
var Schema = require_Schema();
|
|
6969
|
+
var errors = require_errors();
|
|
6970
|
+
var Alias = require_Alias();
|
|
6971
|
+
var identity = require_identity();
|
|
6972
|
+
var Pair = require_Pair();
|
|
6973
|
+
var Scalar = require_Scalar();
|
|
6974
|
+
var YAMLMap = require_YAMLMap();
|
|
6975
|
+
var YAMLSeq = require_YAMLSeq();
|
|
6976
|
+
var cst = require_cst();
|
|
6977
|
+
var lexer = require_lexer();
|
|
6978
|
+
var lineCounter = require_line_counter();
|
|
6979
|
+
var parser = require_parser();
|
|
6980
|
+
var publicApi = require_public_api();
|
|
6981
|
+
var visit = require_visit();
|
|
6982
|
+
exports.Composer = composer.Composer;
|
|
6983
|
+
exports.Document = Document.Document;
|
|
6984
|
+
exports.Schema = Schema.Schema;
|
|
6985
|
+
exports.YAMLError = errors.YAMLError;
|
|
6986
|
+
exports.YAMLParseError = errors.YAMLParseError;
|
|
6987
|
+
exports.YAMLWarning = errors.YAMLWarning;
|
|
6988
|
+
exports.Alias = Alias.Alias;
|
|
6989
|
+
exports.isAlias = identity.isAlias;
|
|
6990
|
+
exports.isCollection = identity.isCollection;
|
|
6991
|
+
exports.isDocument = identity.isDocument;
|
|
6992
|
+
exports.isMap = identity.isMap;
|
|
6993
|
+
exports.isNode = identity.isNode;
|
|
6994
|
+
exports.isPair = identity.isPair;
|
|
6995
|
+
exports.isScalar = identity.isScalar;
|
|
6996
|
+
exports.isSeq = identity.isSeq;
|
|
6997
|
+
exports.Pair = Pair.Pair;
|
|
6998
|
+
exports.Scalar = Scalar.Scalar;
|
|
6999
|
+
exports.YAMLMap = YAMLMap.YAMLMap;
|
|
7000
|
+
exports.YAMLSeq = YAMLSeq.YAMLSeq;
|
|
7001
|
+
exports.CST = cst;
|
|
7002
|
+
exports.Lexer = lexer.Lexer;
|
|
7003
|
+
exports.LineCounter = lineCounter.LineCounter;
|
|
7004
|
+
exports.Parser = parser.Parser;
|
|
7005
|
+
exports.parse = publicApi.parse;
|
|
7006
|
+
exports.parseAllDocuments = publicApi.parseAllDocuments;
|
|
7007
|
+
exports.parseDocument = publicApi.parseDocument;
|
|
7008
|
+
exports.stringify = publicApi.stringify;
|
|
7009
|
+
exports.visit = visit.visit;
|
|
7010
|
+
exports.visitAsync = visit.visitAsync;
|
|
7011
|
+
});
|
|
7012
|
+
|
|
7013
|
+
// src/util/atomic.ts
|
|
7014
|
+
import { randomBytes } from "node:crypto";
|
|
7015
|
+
import { closeSync, constants, fsyncSync, openSync, renameSync, rmSync, writeSync } from "node:fs";
|
|
7016
|
+
function atomicWriteFileSync(destPath, contents, mode = 384) {
|
|
7017
|
+
const tmp = `${destPath}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
7018
|
+
const buf = typeof contents === "string" ? Buffer.from(contents, "utf-8") : contents;
|
|
7019
|
+
let fd = null;
|
|
7020
|
+
try {
|
|
7021
|
+
fd = openSync(tmp, TMP_OPEN_FLAGS, mode);
|
|
7022
|
+
writeSync(fd, buf, 0, buf.length, 0);
|
|
7023
|
+
fsyncSync(fd);
|
|
7024
|
+
closeSync(fd);
|
|
7025
|
+
fd = null;
|
|
7026
|
+
renameSync(tmp, destPath);
|
|
7027
|
+
} catch (err) {
|
|
7028
|
+
if (fd !== null) {
|
|
7029
|
+
try {
|
|
7030
|
+
closeSync(fd);
|
|
7031
|
+
} catch {}
|
|
7032
|
+
}
|
|
7033
|
+
try {
|
|
7034
|
+
rmSync(tmp, { force: true });
|
|
7035
|
+
} catch {}
|
|
7036
|
+
throw err;
|
|
7037
|
+
}
|
|
7038
|
+
}
|
|
7039
|
+
function atomicWriteJsonSync(destPath, value, mode = 384) {
|
|
7040
|
+
atomicWriteFileSync(destPath, JSON.stringify(value, null, 2) + `
|
|
7041
|
+
`, mode);
|
|
7042
|
+
}
|
|
7043
|
+
var TMP_OPEN_FLAGS;
|
|
7044
|
+
var init_atomic = __esm(() => {
|
|
7045
|
+
TMP_OPEN_FLAGS = constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0);
|
|
7046
|
+
});
|
|
7047
|
+
|
|
6934
7048
|
// src/auth/broker/index.ts
|
|
6935
7049
|
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
|
|
6936
7050
|
|
|
6937
7051
|
// src/config/loader.ts
|
|
7052
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
6938
7053
|
import { readFileSync as readFileSync2, existsSync as existsSync3 } from "node:fs";
|
|
6939
7054
|
import { homedir } from "node:os";
|
|
6940
7055
|
import { resolve as resolve3 } from "node:path";
|
|
6941
7056
|
|
|
6942
|
-
// node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/index.js
|
|
6943
|
-
var composer = require_composer();
|
|
6944
|
-
var Document = require_Document();
|
|
6945
|
-
var Schema = require_Schema();
|
|
6946
|
-
var errors = require_errors();
|
|
6947
|
-
var Alias = require_Alias();
|
|
6948
|
-
var identity = require_identity();
|
|
6949
|
-
var Pair = require_Pair();
|
|
6950
|
-
var Scalar = require_Scalar();
|
|
6951
|
-
var YAMLMap = require_YAMLMap();
|
|
6952
|
-
var YAMLSeq = require_YAMLSeq();
|
|
6953
|
-
var cst = require_cst();
|
|
6954
|
-
var lexer = require_lexer();
|
|
6955
|
-
var lineCounter = require_line_counter();
|
|
6956
|
-
var parser = require_parser();
|
|
6957
|
-
var publicApi = require_public_api();
|
|
6958
|
-
var visit = require_visit();
|
|
6959
|
-
var $Composer = composer.Composer;
|
|
6960
|
-
var $Document = Document.Document;
|
|
6961
|
-
var $Schema = Schema.Schema;
|
|
6962
|
-
var $YAMLError = errors.YAMLError;
|
|
6963
|
-
var $YAMLParseError = errors.YAMLParseError;
|
|
6964
|
-
var $YAMLWarning = errors.YAMLWarning;
|
|
6965
|
-
var $Alias = Alias.Alias;
|
|
6966
|
-
var $isAlias = identity.isAlias;
|
|
6967
|
-
var $isCollection = identity.isCollection;
|
|
6968
|
-
var $isDocument = identity.isDocument;
|
|
6969
|
-
var $isMap = identity.isMap;
|
|
6970
|
-
var $isNode = identity.isNode;
|
|
6971
|
-
var $isPair = identity.isPair;
|
|
6972
|
-
var $isScalar = identity.isScalar;
|
|
6973
|
-
var $isSeq = identity.isSeq;
|
|
6974
|
-
var $Pair = Pair.Pair;
|
|
6975
|
-
var $Scalar = Scalar.Scalar;
|
|
6976
|
-
var $YAMLMap = YAMLMap.YAMLMap;
|
|
6977
|
-
var $YAMLSeq = YAMLSeq.YAMLSeq;
|
|
6978
|
-
var $Lexer = lexer.Lexer;
|
|
6979
|
-
var $LineCounter = lineCounter.LineCounter;
|
|
6980
|
-
var $Parser = parser.Parser;
|
|
6981
|
-
var $parse = publicApi.parse;
|
|
6982
|
-
var $parseAllDocuments = publicApi.parseAllDocuments;
|
|
6983
|
-
var $parseDocument = publicApi.parseDocument;
|
|
6984
|
-
var $stringify = publicApi.stringify;
|
|
6985
|
-
var $visit = visit.visit;
|
|
6986
|
-
var $visitAsync = visit.visitAsync;
|
|
6987
|
-
|
|
6988
7057
|
// node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
|
|
6989
7058
|
var exports_external = {};
|
|
6990
7059
|
__export(exports_external, {
|
|
@@ -11070,8 +11139,35 @@ var AgentMemorySchema = exports_external.object({
|
|
|
11070
11139
|
auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
|
|
11071
11140
|
file: exports_external.boolean().default(true).describe("Maintain a curated workspace MEMORY.md file (seeded once, " + "auto-loaded every turn). Set false for hindsight-only memory: " + "the file is not seeded or re-created, so once migrated into " + "Hindsight and deleted it stays gone. Recall + directives carry " + "the memory instead. Cascade: override (per-agent wins over default)."),
|
|
11072
11141
|
isolation: exports_external.enum(["default", "strict"]).default("default").describe("strict = never shared cross-agent, default = eligible for reflect"),
|
|
11073
|
-
bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize results"),
|
|
11074
|
-
|
|
11142
|
+
bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize " + "results. NOTE: this is an alias for the Hindsight engine's " + "`reflect_mission` field (verified live: switchroom's bank_mission " + "lands in `config.reflect_mission`). Prefer `reflect_mission` going " + "forward; `bank_mission` is retained for back-compat. If both are " + "set, `reflect_mission` wins. Cascade: override."),
|
|
11143
|
+
reflect_mission: exports_external.string().optional().describe("Mission/context steering Hindsight Reflect operations (the bank's " + "'who am I / what matters' framing applied during recall). The " + "engine-accurate name for what `bank_mission` sets. Cascade: override."),
|
|
11144
|
+
retain_mission: exports_external.string().optional().describe("Instructions for the fact extraction LLM during retain. Cascade: override."),
|
|
11145
|
+
mental_models: exports_external.array(exports_external.object({
|
|
11146
|
+
name: exports_external.string().min(1).describe("Stable model name (identity key for idempotent ensure). Two " + "declarations with the same name in one agent are rejected."),
|
|
11147
|
+
source_query: exports_external.string().min(1).max(2000).describe("The reflection query the model answers, semantically " + "refreshed from the bank's content. Capped at 2000 chars — a " + "standing reflection query, not a document; the ceiling also " + "bounds what an agent-proposed model can smuggle past the " + "operator approval card."),
|
|
11148
|
+
refresh_after_consolidation: exports_external.boolean().optional().describe("Refresh this model after each consolidation. Defaults OFF — " + "refresh adds bounded background model-spend + timeout risk " + "(RFC Phase 5), so it is opt-in per model."),
|
|
11149
|
+
max_tokens: exports_external.number().int().positive().max(8192).optional().describe("Cap on the synthesized model's token size. Upper-bounded at " + "8192 — a mental model is a standing summary, not a corpus; the " + "ceiling also caps what an agent-proposed model can request.")
|
|
11150
|
+
})).superRefine((models, ctx) => {
|
|
11151
|
+
const seen = new Set;
|
|
11152
|
+
for (let i = 0;i < models.length; i++) {
|
|
11153
|
+
const key = models[i].name;
|
|
11154
|
+
if (seen.has(key)) {
|
|
11155
|
+
ctx.addIssue({
|
|
11156
|
+
code: exports_external.ZodIssueCode.custom,
|
|
11157
|
+
path: [i, "name"],
|
|
11158
|
+
message: `duplicate mental_models name "${key}" — model names must be ` + `unique within an agent (they are the idempotent-ensure key)`
|
|
11159
|
+
});
|
|
11160
|
+
}
|
|
11161
|
+
seen.add(key);
|
|
11162
|
+
}
|
|
11163
|
+
}).optional().describe("Operator-declared, per-specialist Hindsight mental models (RFC " + "Phase 5). Named, opt-in curated reflections this agent's bank should " + "carry — e.g. a coach's 'training-plan-state' or a lawyer's " + "'open-matters'. Ensured idempotently at scaffold/reconcile: NOTHING " + "is created unless declared here (zero declarations = zero models, " + "matching post-#2447 behaviour), and no fixed identity model is " + "reintroduced — 'who the user is' stays owned by dedicated profile " + "banks (users.*.profile_bank), never a per-agent model. Per-agent " + "ONLY: intentionally not accepted at the defaults/profile tier, so a " + "model can never be fleet-seeded — each specialist opts in on its own " + "(the invariant-clean inverse of the retired blind auto-seeding)."),
|
|
11164
|
+
observations_mission: exports_external.string().optional().describe("Steers what the observation-consolidation LLM synthesises from raw " + "facts (the higher-order 'what patterns matter' lens). Cascade: override."),
|
|
11165
|
+
disposition: exports_external.object({
|
|
11166
|
+
skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
|
|
11167
|
+
literalism: exports_external.number().int().min(1).max(5).optional().describe("How literally the bank reads statements vs inferring intent (1-5; engine default 3)."),
|
|
11168
|
+
empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
|
|
11169
|
+
}).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis — a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
|
|
11170
|
+
directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true — Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never …", "from now on …", ' + `"stop doing …", a stated preference, "that's wrong, it's …") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex — the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
|
|
11075
11171
|
recall: exports_external.object({
|
|
11076
11172
|
max_memories: exports_external.number().int().min(0).optional().describe("Cap on the number of memories injected into the prompt by " + "auto-recall, regardless of token budget. Plugin default is 12. " + "0 disables the cap (all memories Hindsight returns are injected)."),
|
|
11077
11173
|
cache_ttl_secs: exports_external.number().int().min(0).optional().describe("Per-session recall cache TTL in seconds. When > 0, identical " + "(prompt, bank) within the same session reuse the cached recall " + "result instead of round-tripping to Hindsight. 0 disables. " + "Default is 600 (10 min) for switchroom-managed agents."),
|
|
@@ -11336,6 +11432,7 @@ var profileFields = {
|
|
|
11336
11432
|
auto_recall: exports_external.boolean().optional(),
|
|
11337
11433
|
file: exports_external.boolean().optional(),
|
|
11338
11434
|
isolation: exports_external.enum(["default", "strict"]).optional(),
|
|
11435
|
+
directive_capture_nudge: exports_external.boolean().optional(),
|
|
11339
11436
|
recall: exports_external.object({
|
|
11340
11437
|
max_memories: exports_external.number().int().min(0).optional(),
|
|
11341
11438
|
cache_ttl_secs: exports_external.number().int().min(0).optional(),
|
|
@@ -11687,6 +11784,7 @@ function resolveDualPath(pathStr) {
|
|
|
11687
11784
|
}
|
|
11688
11785
|
|
|
11689
11786
|
// src/config/overlay-loader.ts
|
|
11787
|
+
var import_yaml = __toESM(require_dist(), 1);
|
|
11690
11788
|
import { existsSync as existsSync2, readFileSync, readdirSync, statSync } from "node:fs";
|
|
11691
11789
|
import { basename, resolve as resolve2 } from "node:path";
|
|
11692
11790
|
|
|
@@ -11762,7 +11860,7 @@ function applyAgentOverlays(config) {
|
|
|
11762
11860
|
for (const file of files) {
|
|
11763
11861
|
try {
|
|
11764
11862
|
const raw = readFileSync(file, "utf-8");
|
|
11765
|
-
const parsed =
|
|
11863
|
+
const parsed = import_yaml.parse(raw);
|
|
11766
11864
|
const doc = OverlayDocSchema.parse(parsed);
|
|
11767
11865
|
const title = deriveOverlayTitle(raw, basename(file));
|
|
11768
11866
|
for (const entry of doc.schedule ?? []) {
|
|
@@ -11803,7 +11901,7 @@ function applyAgentOverlays(config) {
|
|
|
11803
11901
|
for (const file of skillFiles) {
|
|
11804
11902
|
try {
|
|
11805
11903
|
const raw = readFileSync(file, "utf-8");
|
|
11806
|
-
const parsed =
|
|
11904
|
+
const parsed = import_yaml.parse(raw);
|
|
11807
11905
|
const doc = OverlayDocSchema.parse(parsed);
|
|
11808
11906
|
for (const skillName of doc.skills ?? []) {
|
|
11809
11907
|
if (seen.has(skillName))
|
|
@@ -11979,6 +12077,11 @@ function mergeAgentConfig(defaultsIn, agentIn) {
|
|
|
11979
12077
|
continue;
|
|
11980
12078
|
if (k === "recall" && base.recall && typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
11981
12079
|
combined[k] = { ...base.recall, ...v };
|
|
12080
|
+
} else if (k === "disposition" && base.disposition && typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
12081
|
+
combined[k] = {
|
|
12082
|
+
...base.disposition,
|
|
12083
|
+
...v
|
|
12084
|
+
};
|
|
11982
12085
|
} else {
|
|
11983
12086
|
combined[k] = v;
|
|
11984
12087
|
}
|
|
@@ -12320,7 +12423,7 @@ function loadConfig(configPath) {
|
|
|
12320
12423
|
}
|
|
12321
12424
|
let parsed;
|
|
12322
12425
|
try {
|
|
12323
|
-
parsed =
|
|
12426
|
+
parsed = import_yaml2.parse(raw);
|
|
12324
12427
|
} catch (err) {
|
|
12325
12428
|
throw new ConfigError(`Invalid YAML in ${filePath}`, [
|
|
12326
12429
|
` ${err.message}`
|
|
@@ -12440,8 +12543,31 @@ function isReservedAgentName(name) {
|
|
|
12440
12543
|
return RESERVED_AGENT_NAMES.has(name);
|
|
12441
12544
|
}
|
|
12442
12545
|
|
|
12546
|
+
// src/setup/hindsight.ts
|
|
12547
|
+
var HINDSIGHT_DEFAULT_API_PORT = 18888;
|
|
12548
|
+
var HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
|
|
12549
|
+
var HINDSIGHT_DEFAULT_API_BASE_URL = HINDSIGHT_DEFAULT_MCP_URL.replace(/\/mcp\/?$/, "");
|
|
12550
|
+
var HINDSIGHT_CONSUMER_NAME = "hindsight";
|
|
12551
|
+
var HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight";
|
|
12552
|
+
var HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
|
|
12553
|
+
var HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`;
|
|
12554
|
+
var HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)';
|
|
12555
|
+
var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
|
|
12556
|
+
|
|
12443
12557
|
// src/memory/hindsight.ts
|
|
12444
12558
|
var DEFAULT_RETAIN_MISSION = "Extract user preferences, ongoing projects, recurring commitments, " + "important context, and durable facts that should help across future " + "conversations. Skip one-off chatter and temporary task noise.";
|
|
12559
|
+
var PROFILE_MEMORY_DEFAULTS = {
|
|
12560
|
+
"health-coach": {
|
|
12561
|
+
disposition: { skepticism: 2, literalism: 2, empathy: 5 },
|
|
12562
|
+
observations_mission: "Synthesise the person's wellbeing patterns, motivations, and emotional " + "context — how habits, setbacks, and encouragement connect over time."
|
|
12563
|
+
},
|
|
12564
|
+
"executive-assistant": {
|
|
12565
|
+
disposition: { skepticism: 4, literalism: 4, empathy: 3 }
|
|
12566
|
+
},
|
|
12567
|
+
coding: {
|
|
12568
|
+
disposition: { skepticism: 4, literalism: 5, empathy: 2 }
|
|
12569
|
+
}
|
|
12570
|
+
};
|
|
12445
12571
|
|
|
12446
12572
|
// src/agents/reconcile-default-skills.ts
|
|
12447
12573
|
var warnedMissingPool = new Set;
|
|
@@ -12657,37 +12783,8 @@ function resolveConsumerProbeIntervalMs(env) {
|
|
|
12657
12783
|
return DEFAULT_CONSUMER_PROBE_INTERVAL_MS;
|
|
12658
12784
|
}
|
|
12659
12785
|
|
|
12660
|
-
// src/
|
|
12661
|
-
|
|
12662
|
-
import { closeSync, constants, fsyncSync, openSync, renameSync, rmSync, writeSync } from "node:fs";
|
|
12663
|
-
var TMP_OPEN_FLAGS = constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0);
|
|
12664
|
-
function atomicWriteFileSync(destPath, contents, mode = 384) {
|
|
12665
|
-
const tmp = `${destPath}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
12666
|
-
const buf = typeof contents === "string" ? Buffer.from(contents, "utf-8") : contents;
|
|
12667
|
-
let fd = null;
|
|
12668
|
-
try {
|
|
12669
|
-
fd = openSync(tmp, TMP_OPEN_FLAGS, mode);
|
|
12670
|
-
writeSync(fd, buf, 0, buf.length, 0);
|
|
12671
|
-
fsyncSync(fd);
|
|
12672
|
-
closeSync(fd);
|
|
12673
|
-
fd = null;
|
|
12674
|
-
renameSync(tmp, destPath);
|
|
12675
|
-
} catch (err) {
|
|
12676
|
-
if (fd !== null) {
|
|
12677
|
-
try {
|
|
12678
|
-
closeSync(fd);
|
|
12679
|
-
} catch {}
|
|
12680
|
-
}
|
|
12681
|
-
try {
|
|
12682
|
-
rmSync(tmp, { force: true });
|
|
12683
|
-
} catch {}
|
|
12684
|
-
throw err;
|
|
12685
|
-
}
|
|
12686
|
-
}
|
|
12687
|
-
function atomicWriteJsonSync(destPath, value, mode = 384) {
|
|
12688
|
-
atomicWriteFileSync(destPath, JSON.stringify(value, null, 2) + `
|
|
12689
|
-
`, mode);
|
|
12690
|
-
}
|
|
12786
|
+
// src/auth/broker/server.ts
|
|
12787
|
+
init_atomic();
|
|
12691
12788
|
|
|
12692
12789
|
// src/auth/account-store.ts
|
|
12693
12790
|
import {
|
|
@@ -13341,6 +13438,7 @@ function resolveMicrosoftClientId(configClientId) {
|
|
|
13341
13438
|
}
|
|
13342
13439
|
|
|
13343
13440
|
// src/auth/broker/google-storage.ts
|
|
13441
|
+
init_atomic();
|
|
13344
13442
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync4, rmSync as rmSync3, statSync as statSync3 } from "node:fs";
|
|
13345
13443
|
import { dirname, join as join2, resolve as resolve5 } from "node:path";
|
|
13346
13444
|
function normalizeGoogleAccountForStorage(account) {
|
|
@@ -13409,6 +13507,7 @@ function listGoogleAccounts(stateDir) {
|
|
|
13409
13507
|
}
|
|
13410
13508
|
|
|
13411
13509
|
// src/auth/broker/microsoft-storage.ts
|
|
13510
|
+
init_atomic();
|
|
13412
13511
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readdirSync as readdirSync4, readFileSync as readFileSync5, rmSync as rmSync4, statSync as statSync4 } from "node:fs";
|
|
13413
13512
|
import { dirname as dirname2, join as join3, resolve as resolve6 } from "node:path";
|
|
13414
13513
|
function normalizeMicrosoftAccountForStorage(account) {
|
|
@@ -13527,19 +13626,19 @@ function classify(socketPath, config) {
|
|
|
13527
13626
|
return null;
|
|
13528
13627
|
}
|
|
13529
13628
|
function validateConsumerNames(config) {
|
|
13530
|
-
const
|
|
13629
|
+
const errors2 = [];
|
|
13531
13630
|
for (const c of config.consumers) {
|
|
13532
13631
|
if (RESERVED_NAMES.has(c)) {
|
|
13533
|
-
|
|
13632
|
+
errors2.push(`consumer name '${c}' is reserved`);
|
|
13534
13633
|
}
|
|
13535
13634
|
if (config.agents.includes(c)) {
|
|
13536
|
-
|
|
13635
|
+
errors2.push(`consumer name '${c}' collides with an agent name`);
|
|
13537
13636
|
}
|
|
13538
13637
|
if (config.adminAgents.includes(c)) {
|
|
13539
|
-
|
|
13638
|
+
errors2.push(`consumer name '${c}' is an admin agent (consumers cannot be admins)`);
|
|
13540
13639
|
}
|
|
13541
13640
|
}
|
|
13542
|
-
return
|
|
13641
|
+
return errors2;
|
|
13543
13642
|
}
|
|
13544
13643
|
|
|
13545
13644
|
// src/auth/broker/protocol.ts
|
|
@@ -14119,7 +14218,7 @@ class AuthBroker {
|
|
|
14119
14218
|
const sockPath = this.operatorSocketPath();
|
|
14120
14219
|
await this.bindListener(sockPath, operatorUid, 384, { kind: "operator" });
|
|
14121
14220
|
}
|
|
14122
|
-
async bindListener(sockPath, targetUid, sockMode,
|
|
14221
|
+
async bindListener(sockPath, targetUid, sockMode, identity) {
|
|
14123
14222
|
const dir = dirname3(sockPath);
|
|
14124
14223
|
if (existsSync7(dir)) {
|
|
14125
14224
|
try {
|
|
@@ -14138,7 +14237,7 @@ class AuthBroker {
|
|
|
14138
14237
|
}
|
|
14139
14238
|
await new Promise((resolveP, rejectP) => {
|
|
14140
14239
|
const server = net.createServer((sock) => {
|
|
14141
|
-
this.handleConnection(sock, sockPath,
|
|
14240
|
+
this.handleConnection(sock, sockPath, identity);
|
|
14142
14241
|
});
|
|
14143
14242
|
server.on("error", (err) => rejectP(err));
|
|
14144
14243
|
server.listen(sockPath, () => {
|
|
@@ -14151,7 +14250,7 @@ class AuthBroker {
|
|
|
14151
14250
|
try {
|
|
14152
14251
|
chownSync2(dir, targetUid, targetUid);
|
|
14153
14252
|
} catch {}
|
|
14154
|
-
this.listeners.set(sockPath, { server, identity
|
|
14253
|
+
this.listeners.set(sockPath, { server, identity, socketPath: sockPath });
|
|
14155
14254
|
resolveP();
|
|
14156
14255
|
});
|
|
14157
14256
|
});
|
|
@@ -14195,7 +14294,7 @@ class AuthBroker {
|
|
|
14195
14294
|
socket.write(encodeError(reqId, "INVALID_ARGS", err.message));
|
|
14196
14295
|
return;
|
|
14197
14296
|
}
|
|
14198
|
-
const
|
|
14297
|
+
const identity = classify(sockPath, configToShape(this.config)) ?? boundIdentity;
|
|
14199
14298
|
try {
|
|
14200
14299
|
switch (req.op) {
|
|
14201
14300
|
case "get-credentials": {
|
|
@@ -14205,22 +14304,22 @@ class AuthBroker {
|
|
|
14205
14304
|
break;
|
|
14206
14305
|
}
|
|
14207
14306
|
if (provider === "anthropic") {
|
|
14208
|
-
await this.opGetCredentials(socket, reqId,
|
|
14307
|
+
await this.opGetCredentials(socket, reqId, identity);
|
|
14209
14308
|
break;
|
|
14210
14309
|
}
|
|
14211
14310
|
if (provider === "google") {
|
|
14212
|
-
await this.opGoogleGetCredentials(socket, reqId,
|
|
14311
|
+
await this.opGoogleGetCredentials(socket, reqId, identity);
|
|
14213
14312
|
break;
|
|
14214
14313
|
}
|
|
14215
14314
|
if (provider === "microsoft") {
|
|
14216
|
-
await this.opMicrosoftGetCredentials(socket, reqId,
|
|
14315
|
+
await this.opMicrosoftGetCredentials(socket, reqId, identity);
|
|
14217
14316
|
break;
|
|
14218
14317
|
}
|
|
14219
14318
|
socket.write(encodeError(reqId, "INTERNAL", `unhandled provider '${provider}' in get-credentials dispatch`));
|
|
14220
14319
|
break;
|
|
14221
14320
|
}
|
|
14222
14321
|
case "list-state":
|
|
14223
|
-
await this.opListState(socket, reqId,
|
|
14322
|
+
await this.opListState(socket, reqId, identity);
|
|
14224
14323
|
break;
|
|
14225
14324
|
case "set-active": {
|
|
14226
14325
|
const provider = req.provider ?? "anthropic";
|
|
@@ -14228,11 +14327,11 @@ class AuthBroker {
|
|
|
14228
14327
|
socket.write(encodeError(reqId, "INVALID_ARGS", `set-active is Anthropic-only — Google's account-active model is per-agent via google_accounts.enabled_for[]`));
|
|
14229
14328
|
break;
|
|
14230
14329
|
}
|
|
14231
|
-
await this.opSetActive(socket, reqId,
|
|
14330
|
+
await this.opSetActive(socket, reqId, identity, req.account);
|
|
14232
14331
|
break;
|
|
14233
14332
|
}
|
|
14234
14333
|
case "mark-exhausted":
|
|
14235
|
-
await this.opMarkExhausted(socket, reqId,
|
|
14334
|
+
await this.opMarkExhausted(socket, reqId, identity, req.until);
|
|
14236
14335
|
break;
|
|
14237
14336
|
case "refresh-account": {
|
|
14238
14337
|
const provider = req.provider ?? "anthropic";
|
|
@@ -14241,7 +14340,7 @@ class AuthBroker {
|
|
|
14241
14340
|
break;
|
|
14242
14341
|
}
|
|
14243
14342
|
if (provider === "anthropic") {
|
|
14244
|
-
await this.opRefreshAccount(socket, reqId,
|
|
14343
|
+
await this.opRefreshAccount(socket, reqId, identity, req.account);
|
|
14245
14344
|
break;
|
|
14246
14345
|
}
|
|
14247
14346
|
socket.write(encodeError(reqId, "INVALID_ARGS", `refresh-account dispatch through provider '${provider}' storage path lands in Phase 3b.2c (vault-broker-mediated, not direct-to-disk like Anthropic)`));
|
|
@@ -14260,17 +14359,17 @@ class AuthBroker {
|
|
|
14260
14359
|
}
|
|
14261
14360
|
if (provider === "anthropic") {
|
|
14262
14361
|
const anthropicCreds = req.credentials;
|
|
14263
|
-
await this.opAddAccount(socket, reqId,
|
|
14362
|
+
await this.opAddAccount(socket, reqId, identity, req.label, anthropicCreds, req.replace ?? false);
|
|
14264
14363
|
break;
|
|
14265
14364
|
}
|
|
14266
14365
|
if (provider === "google") {
|
|
14267
14366
|
const googleCreds = req.credentials;
|
|
14268
|
-
await this.opGoogleAddAccount(socket, reqId,
|
|
14367
|
+
await this.opGoogleAddAccount(socket, reqId, identity, req.label, googleCreds, req.replace ?? false);
|
|
14269
14368
|
break;
|
|
14270
14369
|
}
|
|
14271
14370
|
if (provider === "microsoft") {
|
|
14272
14371
|
const microsoftCreds = req.credentials;
|
|
14273
|
-
await this.opMicrosoftAddAccount(socket, reqId,
|
|
14372
|
+
await this.opMicrosoftAddAccount(socket, reqId, identity, req.label, microsoftCreds, req.replace ?? false);
|
|
14274
14373
|
break;
|
|
14275
14374
|
}
|
|
14276
14375
|
socket.write(encodeError(reqId, "INTERNAL", `unhandled provider '${provider}' in add-account dispatch`));
|
|
@@ -14283,71 +14382,71 @@ class AuthBroker {
|
|
|
14283
14382
|
break;
|
|
14284
14383
|
}
|
|
14285
14384
|
if (provider === "anthropic") {
|
|
14286
|
-
await this.opRmAccount(socket, reqId,
|
|
14385
|
+
await this.opRmAccount(socket, reqId, identity, req.label);
|
|
14287
14386
|
break;
|
|
14288
14387
|
}
|
|
14289
14388
|
if (provider === "google") {
|
|
14290
|
-
await this.opGoogleRmAccount(socket, reqId,
|
|
14389
|
+
await this.opGoogleRmAccount(socket, reqId, identity, req.label);
|
|
14291
14390
|
break;
|
|
14292
14391
|
}
|
|
14293
14392
|
if (provider === "microsoft") {
|
|
14294
|
-
await this.opMicrosoftRmAccount(socket, reqId,
|
|
14393
|
+
await this.opMicrosoftRmAccount(socket, reqId, identity, req.label);
|
|
14295
14394
|
break;
|
|
14296
14395
|
}
|
|
14297
14396
|
socket.write(encodeError(reqId, "INTERNAL", `unhandled provider '${provider}' in rm-account dispatch`));
|
|
14298
14397
|
break;
|
|
14299
14398
|
}
|
|
14300
14399
|
case "set-override":
|
|
14301
|
-
await this.opSetOverride(socket, reqId,
|
|
14400
|
+
await this.opSetOverride(socket, reqId, identity, req.agent, req.account);
|
|
14302
14401
|
break;
|
|
14303
14402
|
case "list-google-accounts":
|
|
14304
|
-
await this.opListGoogleAccounts(socket, reqId,
|
|
14403
|
+
await this.opListGoogleAccounts(socket, reqId, identity);
|
|
14305
14404
|
break;
|
|
14306
14405
|
case "list-microsoft-accounts":
|
|
14307
|
-
await this.opListMicrosoftAccounts(socket, reqId,
|
|
14406
|
+
await this.opListMicrosoftAccounts(socket, reqId, identity);
|
|
14308
14407
|
break;
|
|
14309
14408
|
case "probe-quota":
|
|
14310
|
-
await this.opProbeQuota(socket, reqId,
|
|
14409
|
+
await this.opProbeQuota(socket, reqId, identity, req.accounts, req.timeoutMs, req.forceLive);
|
|
14311
14410
|
break;
|
|
14312
14411
|
case "claim-notification":
|
|
14313
|
-
this.opClaimNotification(socket, reqId,
|
|
14412
|
+
this.opClaimNotification(socket, reqId, identity, req.key, req.windowMs);
|
|
14314
14413
|
break;
|
|
14315
14414
|
}
|
|
14316
14415
|
} catch (err) {
|
|
14317
14416
|
socket.write(encodeError(reqId, "INTERNAL", err.message));
|
|
14318
14417
|
}
|
|
14319
14418
|
}
|
|
14320
|
-
isAdmin(
|
|
14321
|
-
if (
|
|
14419
|
+
isAdmin(identity) {
|
|
14420
|
+
if (identity.kind === "operator")
|
|
14322
14421
|
return true;
|
|
14323
|
-
if (
|
|
14324
|
-
return
|
|
14422
|
+
if (identity.kind === "agent")
|
|
14423
|
+
return identity.admin;
|
|
14325
14424
|
return false;
|
|
14326
14425
|
}
|
|
14327
14426
|
respondForbidden(socket, id, why) {
|
|
14328
14427
|
socket.write(encodeError(id, "FORBIDDEN", why));
|
|
14329
14428
|
}
|
|
14330
|
-
callerAccount(
|
|
14429
|
+
callerAccount(identity) {
|
|
14331
14430
|
const auth = this.config.auth;
|
|
14332
14431
|
if (!auth)
|
|
14333
14432
|
return null;
|
|
14334
|
-
if (
|
|
14433
|
+
if (identity.kind === "operator")
|
|
14335
14434
|
return auth.active ?? null;
|
|
14336
|
-
if (
|
|
14337
|
-
const c = (auth.consumers ?? []).find((x) => x.name ===
|
|
14435
|
+
if (identity.kind === "consumer") {
|
|
14436
|
+
const c = (auth.consumers ?? []).find((x) => x.name === identity.name);
|
|
14338
14437
|
if (!c)
|
|
14339
14438
|
return null;
|
|
14340
14439
|
return c.account ?? auth.active ?? null;
|
|
14341
14440
|
}
|
|
14342
|
-
const agent = (this.config.agents ?? {})[
|
|
14441
|
+
const agent = (this.config.agents ?? {})[identity.name];
|
|
14343
14442
|
const override = agent?.auth?.override;
|
|
14344
14443
|
if (override)
|
|
14345
14444
|
return override;
|
|
14346
14445
|
return auth.active ?? null;
|
|
14347
14446
|
}
|
|
14348
|
-
servingAccount(
|
|
14349
|
-
const account = this.callerAccount(
|
|
14350
|
-
if (
|
|
14447
|
+
servingAccount(identity) {
|
|
14448
|
+
const account = this.callerAccount(identity);
|
|
14449
|
+
if (identity.kind === "operator")
|
|
14351
14450
|
return account;
|
|
14352
14451
|
return this.accountWithFailover(account);
|
|
14353
14452
|
}
|
|
@@ -14439,24 +14538,24 @@ class AuthBroker {
|
|
|
14439
14538
|
}
|
|
14440
14539
|
return account;
|
|
14441
14540
|
}
|
|
14442
|
-
async opGetCredentials(socket, id,
|
|
14443
|
-
const account = this.servingAccount(
|
|
14541
|
+
async opGetCredentials(socket, id, identity) {
|
|
14542
|
+
const account = this.servingAccount(identity);
|
|
14444
14543
|
if (!account) {
|
|
14445
|
-
this.audit({ op: "get-credentials", identity
|
|
14544
|
+
this.audit({ op: "get-credentials", identity, accountKind: "claude", ok: false, error: "no-active-account" });
|
|
14446
14545
|
socket.write(encodeError(id, "ACCOUNT_NOT_FOUND", "no active account configured"));
|
|
14447
14546
|
return;
|
|
14448
14547
|
}
|
|
14449
14548
|
const creds = readAccountCredentials(account, this.home);
|
|
14450
14549
|
if (!creds) {
|
|
14451
|
-
this.audit({ op: "get-credentials", identity
|
|
14550
|
+
this.audit({ op: "get-credentials", identity, account, accountKind: "claude", ok: false, error: "missing-credentials" });
|
|
14452
14551
|
socket.write(encodeError(id, "ACCOUNT_NOT_FOUND", `no credentials for account '${account}'`));
|
|
14453
14552
|
return;
|
|
14454
14553
|
}
|
|
14455
14554
|
const expiresAt = creds.claudeAiOauth?.expiresAt;
|
|
14456
|
-
this.audit({ op: "get-credentials", identity
|
|
14555
|
+
this.audit({ op: "get-credentials", identity, account, accountKind: "claude", ok: true });
|
|
14457
14556
|
socket.write(encodeSuccess(id, { account, credentials: creds, expiresAt }));
|
|
14458
14557
|
}
|
|
14459
|
-
async opListState(socket, id,
|
|
14558
|
+
async opListState(socket, id, identity) {
|
|
14460
14559
|
const auth = this.config.auth ?? {};
|
|
14461
14560
|
const accounts = listAccounts(this.home).map((label) => {
|
|
14462
14561
|
const creds = readAccountCredentials(label, this.home);
|
|
@@ -14484,8 +14583,8 @@ class AuthBroker {
|
|
|
14484
14583
|
account: c.account ?? auth.active ?? "",
|
|
14485
14584
|
last_seen_at: this.consumerLastSeen[c.name] ?? null
|
|
14486
14585
|
}));
|
|
14487
|
-
const active_overage_serving = this.isActiveOverageServing(this.callerAccount(
|
|
14488
|
-
this.audit({ op: "list-state", identity
|
|
14586
|
+
const active_overage_serving = this.isActiveOverageServing(this.callerAccount(identity));
|
|
14587
|
+
this.audit({ op: "list-state", identity, ok: true });
|
|
14489
14588
|
socket.write(encodeSuccess(id, {
|
|
14490
14589
|
active: auth.active ?? "",
|
|
14491
14590
|
fallback_order: auth.fallback_order ?? [],
|
|
@@ -14495,7 +14594,7 @@ class AuthBroker {
|
|
|
14495
14594
|
active_overage_serving
|
|
14496
14595
|
}));
|
|
14497
14596
|
}
|
|
14498
|
-
async opListGoogleAccounts(socket, id,
|
|
14597
|
+
async opListGoogleAccounts(socket, id, identity) {
|
|
14499
14598
|
const accounts = listGoogleAccounts(this.stateDir).map((account) => {
|
|
14500
14599
|
const creds = readGoogleAccountCredentials(this.stateDir, account);
|
|
14501
14600
|
if (!creds)
|
|
@@ -14507,10 +14606,10 @@ class AuthBroker {
|
|
|
14507
14606
|
clientId: creds.googleOauth.clientId
|
|
14508
14607
|
};
|
|
14509
14608
|
}).filter((entry) => entry !== null).sort((a, b) => a.account.localeCompare(b.account));
|
|
14510
|
-
this.audit({ op: "list-google-accounts", identity
|
|
14609
|
+
this.audit({ op: "list-google-accounts", identity, accountKind: "google", ok: true });
|
|
14511
14610
|
socket.write(encodeSuccess(id, { accounts }));
|
|
14512
14611
|
}
|
|
14513
|
-
async opProbeQuota(socket, id,
|
|
14612
|
+
async opProbeQuota(socket, id, identity, accounts, timeoutMs, forceLive) {
|
|
14514
14613
|
const ttlMs = forceLive ? 0 : quotaProbeTtlMs();
|
|
14515
14614
|
const results = await Promise.all(accounts.map(async (label) => {
|
|
14516
14615
|
const cached = this.lastQuotaCache[label];
|
|
@@ -14527,13 +14626,13 @@ class AuthBroker {
|
|
|
14527
14626
|
ok: false,
|
|
14528
14627
|
reason: "no credentials for account in broker store"
|
|
14529
14628
|
};
|
|
14530
|
-
this.audit({ op: "probe-quota", identity
|
|
14629
|
+
this.audit({ op: "probe-quota", identity, account: label, accountKind: "claude", ok: false, error: "missing-credentials" });
|
|
14531
14630
|
return { label, result: result2 };
|
|
14532
14631
|
}
|
|
14533
14632
|
const result = await this.probeQuotaSingleFlight(label, token, timeoutMs);
|
|
14534
14633
|
this.audit({
|
|
14535
14634
|
op: "probe-quota",
|
|
14536
|
-
identity
|
|
14635
|
+
identity,
|
|
14537
14636
|
account: label,
|
|
14538
14637
|
accountKind: "claude",
|
|
14539
14638
|
ok: result.ok,
|
|
@@ -14654,14 +14753,14 @@ class AuthBroker {
|
|
|
14654
14753
|
this.fanoutToAffectedConsumers(label);
|
|
14655
14754
|
}
|
|
14656
14755
|
}
|
|
14657
|
-
async opSetActive(socket, id,
|
|
14658
|
-
if (!this.isAdmin(
|
|
14659
|
-
this.audit({ op: "set-active", identity
|
|
14756
|
+
async opSetActive(socket, id, identity, account) {
|
|
14757
|
+
if (!this.isAdmin(identity)) {
|
|
14758
|
+
this.audit({ op: "set-active", identity, account, accountKind: "claude", ok: false, error: "FORBIDDEN" });
|
|
14660
14759
|
this.respondForbidden(socket, id, "set-active requires admin");
|
|
14661
14760
|
return;
|
|
14662
14761
|
}
|
|
14663
14762
|
if (!accountExists(account, this.home)) {
|
|
14664
|
-
this.audit({ op: "set-active", identity
|
|
14763
|
+
this.audit({ op: "set-active", identity, account, accountKind: "claude", ok: false, error: "ACCOUNT_NOT_FOUND" });
|
|
14665
14764
|
socket.write(encodeError(id, "ACCOUNT_NOT_FOUND", `account '${account}' not found`));
|
|
14666
14765
|
return;
|
|
14667
14766
|
}
|
|
@@ -14673,21 +14772,21 @@ class AuthBroker {
|
|
|
14673
14772
|
this.persistActiveOverride(account);
|
|
14674
14773
|
const fanned = this.fanoutToAffectedAgents(account);
|
|
14675
14774
|
this.fanoutAllConsumers();
|
|
14676
|
-
this.audit({ op: "set-active", identity
|
|
14775
|
+
this.audit({ op: "set-active", identity, account, accountKind: "claude", ok: true });
|
|
14677
14776
|
socket.write(encodeSuccess(id, { active: account, fanned }));
|
|
14678
14777
|
}
|
|
14679
|
-
async opMarkExhausted(socket, id,
|
|
14680
|
-
const account = this.callerAccount(
|
|
14778
|
+
async opMarkExhausted(socket, id, identity, until) {
|
|
14779
|
+
const account = this.callerAccount(identity);
|
|
14681
14780
|
if (!account) {
|
|
14682
|
-
this.audit({ op: "mark-exhausted", identity
|
|
14781
|
+
this.audit({ op: "mark-exhausted", identity, accountKind: "claude", ok: false, error: "no-active-account" });
|
|
14683
14782
|
socket.write(encodeError(id, "ACCOUNT_NOT_FOUND", "no active account configured"));
|
|
14684
14783
|
return;
|
|
14685
14784
|
}
|
|
14686
|
-
const { rolled, rolledTo } = await this.markExhaustedAndRoll(account, until,
|
|
14687
|
-
this.audit({ op: "mark-exhausted", identity
|
|
14785
|
+
const { rolled, rolledTo } = await this.markExhaustedAndRoll(account, until, identity);
|
|
14786
|
+
this.audit({ op: "mark-exhausted", identity, account, accountKind: "claude", ok: true });
|
|
14688
14787
|
socket.write(encodeSuccess(id, { account, rolled, rolledTo }));
|
|
14689
14788
|
}
|
|
14690
|
-
async markExhaustedAndRoll(account, until,
|
|
14789
|
+
async markExhaustedAndRoll(account, until, identity) {
|
|
14691
14790
|
const now = this.now();
|
|
14692
14791
|
const exhaustedUntil = clampMarkExpiry({
|
|
14693
14792
|
proposedUntil: until ?? now + MARK_EXHAUSTED_DEFAULT_MS,
|
|
@@ -14707,7 +14806,7 @@ class AuthBroker {
|
|
|
14707
14806
|
};
|
|
14708
14807
|
this.persistActiveOverride(rolledTo);
|
|
14709
14808
|
this.fanoutAllConsumers();
|
|
14710
|
-
this.audit({ op: "auto-promote-active", identity
|
|
14809
|
+
this.audit({ op: "auto-promote-active", identity, account: rolledTo, accountKind: "claude", ok: true });
|
|
14711
14810
|
process.stdout.write(`auth-broker: auto-promoted auth.active ${account} → ${rolledTo} ` + `(${account} exhausted until ${new Date(exhaustedUntil).toISOString()}) — persisted
|
|
14712
14811
|
`);
|
|
14713
14812
|
}
|
|
@@ -14721,7 +14820,7 @@ class AuthBroker {
|
|
|
14721
14820
|
return false;
|
|
14722
14821
|
return !overageLiftsWall(snapshot, this.isOverageAllowed(account));
|
|
14723
14822
|
}
|
|
14724
|
-
opClaimNotification(socket, id,
|
|
14823
|
+
opClaimNotification(socket, id, identity, key, windowMs) {
|
|
14725
14824
|
const now = this.now();
|
|
14726
14825
|
const prev = this.notificationClaims[key];
|
|
14727
14826
|
const granted = prev === undefined || now - prev >= windowMs;
|
|
@@ -14732,35 +14831,35 @@ class AuthBroker {
|
|
|
14732
14831
|
delete this.notificationClaims[k];
|
|
14733
14832
|
}
|
|
14734
14833
|
this.persistNotificationClaims();
|
|
14735
|
-
this.audit({ op: "claim-notification", identity
|
|
14834
|
+
this.audit({ op: "claim-notification", identity, account: key, ok: true });
|
|
14736
14835
|
}
|
|
14737
14836
|
socket.write(encodeSuccess(id, { granted }));
|
|
14738
14837
|
}
|
|
14739
|
-
async opRefreshAccount(socket, id,
|
|
14740
|
-
if (!this.isAdmin(
|
|
14741
|
-
this.audit({ op: "refresh-account", identity
|
|
14838
|
+
async opRefreshAccount(socket, id, identity, account) {
|
|
14839
|
+
if (!this.isAdmin(identity)) {
|
|
14840
|
+
this.audit({ op: "refresh-account", identity, account, accountKind: "claude", ok: false, error: "FORBIDDEN" });
|
|
14742
14841
|
this.respondForbidden(socket, id, "refresh-account requires admin");
|
|
14743
14842
|
return;
|
|
14744
14843
|
}
|
|
14745
14844
|
if (!accountExists(account, this.home)) {
|
|
14746
|
-
this.audit({ op: "refresh-account", identity
|
|
14845
|
+
this.audit({ op: "refresh-account", identity, account, accountKind: "claude", ok: false, error: "ACCOUNT_NOT_FOUND" });
|
|
14747
14846
|
socket.write(encodeError(id, "ACCOUNT_NOT_FOUND", `account '${account}' not found`));
|
|
14748
14847
|
return;
|
|
14749
14848
|
}
|
|
14750
14849
|
const result = await this.refreshOneAccount(account, true);
|
|
14751
14850
|
if (result.kind === "failed") {
|
|
14752
|
-
this.audit({ op: "refresh-account", identity
|
|
14851
|
+
this.audit({ op: "refresh-account", identity, account, accountKind: "claude", ok: false, error: result.error });
|
|
14753
14852
|
socket.write(encodeError(id, "REFRESH_FAILED", result.error));
|
|
14754
14853
|
return;
|
|
14755
14854
|
}
|
|
14756
14855
|
const creds = readAccountCredentials(account, this.home);
|
|
14757
14856
|
const expiresAt = creds?.claudeAiOauth?.expiresAt;
|
|
14758
|
-
this.audit({ op: "refresh-account", identity
|
|
14857
|
+
this.audit({ op: "refresh-account", identity, account, accountKind: "claude", ok: true });
|
|
14759
14858
|
socket.write(encodeSuccess(id, { account, expiresAt }));
|
|
14760
14859
|
}
|
|
14761
|
-
async opAddAccount(socket, id,
|
|
14762
|
-
if (!this.isAdmin(
|
|
14763
|
-
this.audit({ op: "add-account", identity
|
|
14860
|
+
async opAddAccount(socket, id, identity, label, credentials, replace) {
|
|
14861
|
+
if (!this.isAdmin(identity)) {
|
|
14862
|
+
this.audit({ op: "add-account", identity, account: label, accountKind: "claude", ok: false, error: "FORBIDDEN" });
|
|
14764
14863
|
this.respondForbidden(socket, id, "add-account requires admin");
|
|
14765
14864
|
return;
|
|
14766
14865
|
}
|
|
@@ -14771,7 +14870,7 @@ class AuthBroker {
|
|
|
14771
14870
|
return;
|
|
14772
14871
|
}
|
|
14773
14872
|
if (accountExists(label, this.home) && !replace) {
|
|
14774
|
-
this.audit({ op: "add-account", identity
|
|
14873
|
+
this.audit({ op: "add-account", identity, account: label, accountKind: "claude", ok: false, error: "ACCOUNT_ALREADY_EXISTS" });
|
|
14775
14874
|
socket.write(encodeError(id, "ACCOUNT_ALREADY_EXISTS", `account '${label}' already exists; pass replace:true to overwrite`));
|
|
14776
14875
|
return;
|
|
14777
14876
|
}
|
|
@@ -14789,12 +14888,12 @@ class AuthBroker {
|
|
|
14789
14888
|
this.persistShaIndex();
|
|
14790
14889
|
this.fanoutToAffectedAgents(label);
|
|
14791
14890
|
const expiresAt = credentials.claudeAiOauth?.expiresAt;
|
|
14792
|
-
this.audit({ op: "add-account", identity
|
|
14891
|
+
this.audit({ op: "add-account", identity, account: label, accountKind: "claude", ok: true, replace });
|
|
14793
14892
|
socket.write(encodeSuccess(id, { label, expiresAt }));
|
|
14794
14893
|
}
|
|
14795
|
-
async opRmAccount(socket, id,
|
|
14796
|
-
if (!this.isAdmin(
|
|
14797
|
-
this.audit({ op: "rm-account", identity
|
|
14894
|
+
async opRmAccount(socket, id, identity, label) {
|
|
14895
|
+
if (!this.isAdmin(identity)) {
|
|
14896
|
+
this.audit({ op: "rm-account", identity, account: label, accountKind: "claude", ok: false, error: "FORBIDDEN" });
|
|
14798
14897
|
this.respondForbidden(socket, id, "rm-account requires admin");
|
|
14799
14898
|
return;
|
|
14800
14899
|
}
|
|
@@ -14827,42 +14926,42 @@ class AuthBroker {
|
|
|
14827
14926
|
this.persistQuota();
|
|
14828
14927
|
this.persistThresholdViolations();
|
|
14829
14928
|
this.persistLastQuotaCache();
|
|
14830
|
-
this.audit({ op: "rm-account", identity
|
|
14929
|
+
this.audit({ op: "rm-account", identity, account: label, accountKind: "claude", ok: true });
|
|
14831
14930
|
socket.write(encodeSuccess(id, { label }));
|
|
14832
14931
|
}
|
|
14833
|
-
async opGoogleGetCredentials(socket, id,
|
|
14834
|
-
if (
|
|
14835
|
-
socket.write(encodeError(id, "INVALID_ARGS", `Google get-credentials is per-agent only (caller kind '${
|
|
14932
|
+
async opGoogleGetCredentials(socket, id, identity) {
|
|
14933
|
+
if (identity.kind !== "agent") {
|
|
14934
|
+
socket.write(encodeError(id, "INVALID_ARGS", `Google get-credentials is per-agent only (caller kind '${identity.kind}' not supported); use the agent's per-agent socket bind`));
|
|
14836
14935
|
return;
|
|
14837
14936
|
}
|
|
14838
|
-
const agentName =
|
|
14937
|
+
const agentName = identity.name;
|
|
14839
14938
|
const agent = (this.config.agents ?? {})[agentName];
|
|
14840
14939
|
const account = agent?.google_workspace?.account;
|
|
14841
14940
|
if (!account) {
|
|
14842
|
-
this.audit({ op: "get-credentials", identity
|
|
14941
|
+
this.audit({ op: "get-credentials", identity, accountKind: "google", ok: false, error: "no-google-account-configured" });
|
|
14843
14942
|
socket.write(encodeError(id, "ACCOUNT_NOT_FOUND", `agent '${agentName}' has no google_workspace.account configured in switchroom.yaml`));
|
|
14844
14943
|
return;
|
|
14845
14944
|
}
|
|
14846
14945
|
const ga = this.config.google_accounts;
|
|
14847
14946
|
const enabledFor = ga?.[account]?.enabled_for ?? [];
|
|
14848
14947
|
if (!enabledFor.includes(agentName)) {
|
|
14849
|
-
this.audit({ op: "get-credentials", identity
|
|
14948
|
+
this.audit({ op: "get-credentials", identity, account, accountKind: "google", ok: false, error: "acl-deny" });
|
|
14850
14949
|
socket.write(encodeError(id, "FORBIDDEN", `agent '${agentName}' not in google_accounts['${account}'].enabled_for[] — operator must run \`switchroom auth google enable ${account} ${agentName}\``));
|
|
14851
14950
|
return;
|
|
14852
14951
|
}
|
|
14853
14952
|
const creds = readGoogleAccountCredentials(this.stateDir, account);
|
|
14854
14953
|
if (!creds) {
|
|
14855
|
-
this.audit({ op: "get-credentials", identity
|
|
14954
|
+
this.audit({ op: "get-credentials", identity, account, accountKind: "google", ok: false, error: "missing-credentials" });
|
|
14856
14955
|
socket.write(encodeError(id, "ACCOUNT_NOT_FOUND", `no Google credentials for account '${account}' — operator must run \`switchroom auth google account add ${account}\``));
|
|
14857
14956
|
return;
|
|
14858
14957
|
}
|
|
14859
14958
|
const expiresAt = creds.googleOauth?.expiresAt;
|
|
14860
|
-
this.audit({ op: "get-credentials", identity
|
|
14959
|
+
this.audit({ op: "get-credentials", identity, account, accountKind: "google", ok: true });
|
|
14861
14960
|
socket.write(encodeSuccess(id, { account, credentials: creds, expiresAt }));
|
|
14862
14961
|
}
|
|
14863
|
-
async opGoogleAddAccount(socket, id,
|
|
14864
|
-
if (!this.isAdmin(
|
|
14865
|
-
this.audit({ op: "add-account", identity
|
|
14962
|
+
async opGoogleAddAccount(socket, id, identity, label, credentials, replace) {
|
|
14963
|
+
if (!this.isAdmin(identity)) {
|
|
14964
|
+
this.audit({ op: "add-account", identity, account: label, accountKind: "google", ok: false, error: "FORBIDDEN" });
|
|
14866
14965
|
this.respondForbidden(socket, id, "add-account requires admin");
|
|
14867
14966
|
return;
|
|
14868
14967
|
}
|
|
@@ -14873,7 +14972,7 @@ class AuthBroker {
|
|
|
14873
14972
|
return;
|
|
14874
14973
|
}
|
|
14875
14974
|
if (googleAccountExists(this.stateDir, label) && !replace) {
|
|
14876
|
-
this.audit({ op: "add-account", identity
|
|
14975
|
+
this.audit({ op: "add-account", identity, account: label, accountKind: "google", ok: false, error: "ACCOUNT_ALREADY_EXISTS" });
|
|
14877
14976
|
socket.write(encodeError(id, "ACCOUNT_ALREADY_EXISTS", `google account '${label}' already exists; pass replace:true to overwrite`));
|
|
14878
14977
|
return;
|
|
14879
14978
|
}
|
|
@@ -14884,12 +14983,12 @@ class AuthBroker {
|
|
|
14884
14983
|
return;
|
|
14885
14984
|
}
|
|
14886
14985
|
const expiresAt = credentials.googleOauth?.expiresAt;
|
|
14887
|
-
this.audit({ op: "add-account", identity
|
|
14986
|
+
this.audit({ op: "add-account", identity, account: label, accountKind: "google", ok: true, replace });
|
|
14888
14987
|
socket.write(encodeSuccess(id, { label, expiresAt }));
|
|
14889
14988
|
}
|
|
14890
|
-
async opGoogleRmAccount(socket, id,
|
|
14891
|
-
if (!this.isAdmin(
|
|
14892
|
-
this.audit({ op: "rm-account", identity
|
|
14989
|
+
async opGoogleRmAccount(socket, id, identity, label) {
|
|
14990
|
+
if (!this.isAdmin(identity)) {
|
|
14991
|
+
this.audit({ op: "rm-account", identity, account: label, accountKind: "google", ok: false, error: "FORBIDDEN" });
|
|
14893
14992
|
this.respondForbidden(socket, id, "rm-account requires admin");
|
|
14894
14993
|
return;
|
|
14895
14994
|
}
|
|
@@ -14915,42 +15014,42 @@ class AuthBroker {
|
|
|
14915
15014
|
socket.write(encodeError(id, "INTERNAL", err.message));
|
|
14916
15015
|
return;
|
|
14917
15016
|
}
|
|
14918
|
-
this.audit({ op: "rm-account", identity
|
|
15017
|
+
this.audit({ op: "rm-account", identity, account: label, accountKind: "google", ok: true });
|
|
14919
15018
|
socket.write(encodeSuccess(id, { label }));
|
|
14920
15019
|
}
|
|
14921
|
-
async opMicrosoftGetCredentials(socket, id,
|
|
14922
|
-
if (
|
|
14923
|
-
socket.write(encodeError(id, "INVALID_ARGS", `Microsoft get-credentials is per-agent only (caller kind '${
|
|
15020
|
+
async opMicrosoftGetCredentials(socket, id, identity) {
|
|
15021
|
+
if (identity.kind !== "agent") {
|
|
15022
|
+
socket.write(encodeError(id, "INVALID_ARGS", `Microsoft get-credentials is per-agent only (caller kind '${identity.kind}' not supported); use the agent's per-agent socket bind`));
|
|
14924
15023
|
return;
|
|
14925
15024
|
}
|
|
14926
|
-
const agentName =
|
|
15025
|
+
const agentName = identity.name;
|
|
14927
15026
|
const agent = (this.config.agents ?? {})[agentName];
|
|
14928
15027
|
const account = agent?.microsoft_workspace?.account;
|
|
14929
15028
|
if (!account) {
|
|
14930
|
-
this.audit({ op: "get-credentials", identity
|
|
15029
|
+
this.audit({ op: "get-credentials", identity, accountKind: "microsoft", ok: false, error: "no-microsoft-account-configured" });
|
|
14931
15030
|
socket.write(encodeError(id, "ACCOUNT_NOT_FOUND", `agent '${agentName}' has no microsoft_workspace.account configured in switchroom.yaml`));
|
|
14932
15031
|
return;
|
|
14933
15032
|
}
|
|
14934
15033
|
const ma = this.config.microsoft_accounts;
|
|
14935
15034
|
const enabledFor = ma?.[account]?.enabled_for ?? [];
|
|
14936
15035
|
if (!enabledFor.includes(agentName)) {
|
|
14937
|
-
this.audit({ op: "get-credentials", identity
|
|
15036
|
+
this.audit({ op: "get-credentials", identity, account, accountKind: "microsoft", ok: false, error: "acl-deny" });
|
|
14938
15037
|
socket.write(encodeError(id, "FORBIDDEN", `agent '${agentName}' not in microsoft_accounts['${account}'].enabled_for[] — operator must run \`switchroom auth microsoft enable ${account} ${agentName}\``));
|
|
14939
15038
|
return;
|
|
14940
15039
|
}
|
|
14941
15040
|
const creds = readMicrosoftAccountCredentials(this.stateDir, account);
|
|
14942
15041
|
if (!creds) {
|
|
14943
|
-
this.audit({ op: "get-credentials", identity
|
|
15042
|
+
this.audit({ op: "get-credentials", identity, account, accountKind: "microsoft", ok: false, error: "missing-credentials" });
|
|
14944
15043
|
socket.write(encodeError(id, "ACCOUNT_NOT_FOUND", `no Microsoft credentials for account '${account}' — operator must run \`switchroom auth microsoft account add ${account}\``));
|
|
14945
15044
|
return;
|
|
14946
15045
|
}
|
|
14947
15046
|
const expiresAt = creds.microsoftOauth?.expiresAt;
|
|
14948
|
-
this.audit({ op: "get-credentials", identity
|
|
15047
|
+
this.audit({ op: "get-credentials", identity, account, accountKind: "microsoft", ok: true });
|
|
14949
15048
|
socket.write(encodeSuccess(id, { account, credentials: creds, expiresAt }));
|
|
14950
15049
|
}
|
|
14951
|
-
async opMicrosoftAddAccount(socket, id,
|
|
14952
|
-
if (!this.isAdmin(
|
|
14953
|
-
this.audit({ op: "add-account", identity
|
|
15050
|
+
async opMicrosoftAddAccount(socket, id, identity, label, credentials, replace) {
|
|
15051
|
+
if (!this.isAdmin(identity)) {
|
|
15052
|
+
this.audit({ op: "add-account", identity, account: label, accountKind: "microsoft", ok: false, error: "FORBIDDEN" });
|
|
14954
15053
|
this.respondForbidden(socket, id, "add-account requires admin");
|
|
14955
15054
|
return;
|
|
14956
15055
|
}
|
|
@@ -14961,7 +15060,7 @@ class AuthBroker {
|
|
|
14961
15060
|
return;
|
|
14962
15061
|
}
|
|
14963
15062
|
if (microsoftAccountExists(this.stateDir, label) && !replace) {
|
|
14964
|
-
this.audit({ op: "add-account", identity
|
|
15063
|
+
this.audit({ op: "add-account", identity, account: label, accountKind: "microsoft", ok: false, error: "ACCOUNT_ALREADY_EXISTS" });
|
|
14965
15064
|
socket.write(encodeError(id, "ACCOUNT_ALREADY_EXISTS", `microsoft account '${label}' already exists; pass replace:true to overwrite`));
|
|
14966
15065
|
return;
|
|
14967
15066
|
}
|
|
@@ -14972,12 +15071,12 @@ class AuthBroker {
|
|
|
14972
15071
|
return;
|
|
14973
15072
|
}
|
|
14974
15073
|
const expiresAt = credentials.microsoftOauth?.expiresAt;
|
|
14975
|
-
this.audit({ op: "add-account", identity
|
|
15074
|
+
this.audit({ op: "add-account", identity, account: label, accountKind: "microsoft", ok: true, replace });
|
|
14976
15075
|
socket.write(encodeSuccess(id, { label, expiresAt }));
|
|
14977
15076
|
}
|
|
14978
|
-
async opMicrosoftRmAccount(socket, id,
|
|
14979
|
-
if (!this.isAdmin(
|
|
14980
|
-
this.audit({ op: "rm-account", identity
|
|
15077
|
+
async opMicrosoftRmAccount(socket, id, identity, label) {
|
|
15078
|
+
if (!this.isAdmin(identity)) {
|
|
15079
|
+
this.audit({ op: "rm-account", identity, account: label, accountKind: "microsoft", ok: false, error: "FORBIDDEN" });
|
|
14981
15080
|
this.respondForbidden(socket, id, "rm-account requires admin");
|
|
14982
15081
|
return;
|
|
14983
15082
|
}
|
|
@@ -15003,10 +15102,10 @@ class AuthBroker {
|
|
|
15003
15102
|
socket.write(encodeError(id, "INTERNAL", err.message));
|
|
15004
15103
|
return;
|
|
15005
15104
|
}
|
|
15006
|
-
this.audit({ op: "rm-account", identity
|
|
15105
|
+
this.audit({ op: "rm-account", identity, account: label, accountKind: "microsoft", ok: true });
|
|
15007
15106
|
socket.write(encodeSuccess(id, { label }));
|
|
15008
15107
|
}
|
|
15009
|
-
async opListMicrosoftAccounts(socket, id,
|
|
15108
|
+
async opListMicrosoftAccounts(socket, id, identity) {
|
|
15010
15109
|
const accounts = listMicrosoftAccounts(this.stateDir).map((account) => {
|
|
15011
15110
|
const creds = readMicrosoftAccountCredentials(this.stateDir, account);
|
|
15012
15111
|
if (!creds)
|
|
@@ -15019,12 +15118,12 @@ class AuthBroker {
|
|
|
15019
15118
|
accountType: creds.microsoftOauth.accountType
|
|
15020
15119
|
};
|
|
15021
15120
|
}).filter((entry) => entry !== null).sort((a, b) => a.account.localeCompare(b.account));
|
|
15022
|
-
this.audit({ op: "list-microsoft-accounts", identity
|
|
15121
|
+
this.audit({ op: "list-microsoft-accounts", identity, accountKind: "microsoft", ok: true });
|
|
15023
15122
|
socket.write(encodeSuccess(id, { accounts }));
|
|
15024
15123
|
}
|
|
15025
|
-
async opSetOverride(socket, id,
|
|
15026
|
-
if (!this.isAdmin(
|
|
15027
|
-
this.audit({ op: "set-override", identity
|
|
15124
|
+
async opSetOverride(socket, id, identity, agentName, account) {
|
|
15125
|
+
if (!this.isAdmin(identity)) {
|
|
15126
|
+
this.audit({ op: "set-override", identity, account: account ?? undefined, accountKind: "claude", ok: false, error: "FORBIDDEN" });
|
|
15028
15127
|
this.respondForbidden(socket, id, "set-override requires admin");
|
|
15029
15128
|
return;
|
|
15030
15129
|
}
|
|
@@ -15046,7 +15145,7 @@ class AuthBroker {
|
|
|
15046
15145
|
agents[agentName] = { ...cur, auth };
|
|
15047
15146
|
this.config = { ...this.config, agents };
|
|
15048
15147
|
this.fanoutForAgent(agentName);
|
|
15049
|
-
this.audit({ op: "set-override", identity
|
|
15148
|
+
this.audit({ op: "set-override", identity, account: account ?? undefined, accountKind: "claude", ok: true });
|
|
15050
15149
|
socket.write(encodeSuccess(id, { agent: agentName, account }));
|
|
15051
15150
|
}
|
|
15052
15151
|
async refreshTick() {
|