switchroom 0.19.18 → 0.19.19
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 +2 -1
- package/dist/auth-broker/index.js +3 -1
- package/dist/cli/drive-write-pretool.mjs +48 -5
- package/dist/cli/ms-365-write-pretool.mjs +40 -2
- package/dist/cli/notion-write-pretool.mjs +2 -1
- package/dist/cli/switchroom.js +3392 -1569
- package/dist/host-control/main.js +12209 -11396
- package/dist/vault/approvals/kernel-server.js +60 -7
- package/dist/vault/broker/server.js +206 -76
- package/package.json +4 -3
- package/profiles/_base/start.sh.hbs +61 -1
- package/telegram-plugin/bridge/bridge.ts +14 -0
- package/telegram-plugin/dist/bridge/bridge.js +13 -0
- package/telegram-plugin/dist/gateway/gateway.js +1644 -1044
- package/telegram-plugin/dist/server.js +13 -0
- package/telegram-plugin/gateway/always-allow-persist-queue.ts +97 -11
- package/telegram-plugin/gateway/missed-approvals-store.ts +66 -17
- package/telegram-plugin/gateway/pending-card-store.ts +46 -16
- package/telegram-plugin/gateway/scoped-grant-store.ts +39 -14
- package/telegram-plugin/gateway/store-file.ts +244 -0
- package/telegram-plugin/hooks/tool-label-pretool.mjs +88 -2
- package/telegram-plugin/tests/bridge-tool-parity.test.ts +95 -0
- package/telegram-plugin/tests/store-atomic-write.test.ts +411 -0
- package/telegram-plugin/tests/tool-activity-summary.test.ts +9 -2
- package/telegram-plugin/tests/tool-label-pretool.test.ts +94 -0
- package/telegram-plugin/tests/worker-feed-repeat-steps.test.ts +147 -0
- package/telegram-plugin/worker-activity-feed.ts +51 -1
- package/vendor/hindsight-memory/scripts/drain_pending.py +668 -56
- package/vendor/hindsight-memory/scripts/lib/client.py +124 -0
- package/vendor/hindsight-memory/scripts/lib/pending.py +865 -33
- package/vendor/hindsight-memory/scripts/lib/retain_split.py +449 -0
- package/vendor/hindsight-memory/scripts/session_start.py +48 -0
- package/vendor/hindsight-memory/scripts/tests/test_client_document_exists.py +470 -0
- package/vendor/hindsight-memory/scripts/tests/test_pending_drops.py +2121 -0
- package/vendor/hindsight-memory/scripts/tests/test_retain_split.py +430 -0
- package/vendor/hindsight-memory/scripts/tests/test_session_start_version_skew.py +204 -0
- package/vendor/hindsight-memory/tests/test_drain_pending.py +102 -6
- package/vendor/hindsight-memory/tests/test_pending.py +32 -7
|
@@ -13406,7 +13406,8 @@ var ApprovalDecisionMetaSchema = exports_external.object({
|
|
|
13406
13406
|
ttl_expires_at: exports_external.number().nullable(),
|
|
13407
13407
|
last_used_at: exports_external.number().nullable(),
|
|
13408
13408
|
revoked_at: exports_external.number().nullable(),
|
|
13409
|
-
revoke_reason: exports_external.string().nullable()
|
|
13409
|
+
revoke_reason: exports_external.string().nullable(),
|
|
13410
|
+
origin: exports_external.enum(["agent", "operator"]).optional()
|
|
13410
13411
|
});
|
|
13411
13412
|
var OkApprovalLookupResponseSchema = exports_external.object({
|
|
13412
13413
|
ok: exports_external.literal(true),
|
|
@@ -18363,6 +18363,7 @@ var HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`
|
|
|
18363
18363
|
var HINDSIGHT_CREDS_MIRROR_VOLUME = `consumer-creds-${HINDSIGHT_CONSUMER_NAME}`;
|
|
18364
18364
|
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)';
|
|
18365
18365
|
var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
|
|
18366
|
+
var DOCKER_PROBE_TIMEOUT_MS = 60 * 1000;
|
|
18366
18367
|
|
|
18367
18368
|
// src/memory/hindsight.ts
|
|
18368
18369
|
var DEFAULT_RETAIN_MISSION = "Extract durable facts that will still be true and useful weeks from now: " + "user preferences and standing rules, ongoing projects and recurring " + "commitments, technical and architectural decisions with their rationale, " + "and people/tool relationships. A preference revealed by a request is " + "durable — record the preference (what the user likes, wants, or always " + `does), not the request itself.
|
|
@@ -18695,7 +18696,8 @@ var ApprovalDecisionMetaSchema = exports_external.object({
|
|
|
18695
18696
|
ttl_expires_at: exports_external.number().nullable(),
|
|
18696
18697
|
last_used_at: exports_external.number().nullable(),
|
|
18697
18698
|
revoked_at: exports_external.number().nullable(),
|
|
18698
|
-
revoke_reason: exports_external.string().nullable()
|
|
18699
|
+
revoke_reason: exports_external.string().nullable(),
|
|
18700
|
+
origin: exports_external.enum(["agent", "operator"]).optional()
|
|
18699
18701
|
});
|
|
18700
18702
|
var OkApprovalLookupResponseSchema = exports_external.object({
|
|
18701
18703
|
ok: exports_external.literal(true),
|
|
@@ -5300,6 +5300,34 @@ function truncateLine2(s) {
|
|
|
5300
5300
|
return cleaned.length <= 60 ? cleaned : `${cleaned.slice(0, 59)}\u2026`;
|
|
5301
5301
|
}
|
|
5302
5302
|
|
|
5303
|
+
// src/vault/approvals/gated-write-policy.ts
|
|
5304
|
+
var REQUIRE_OPERATOR_WRITE_ENV = "SWITCHROOM_REQUIRE_OPERATOR_APPROVAL_WRITE";
|
|
5305
|
+
function requireOperatorApprovalForWrites(env = process.env) {
|
|
5306
|
+
return env[REQUIRE_OPERATOR_WRITE_ENV] === "1";
|
|
5307
|
+
}
|
|
5308
|
+
function isGatedWriteApproved(lookup, requireOperator) {
|
|
5309
|
+
if (lookup.state !== "granted") {
|
|
5310
|
+
return { allow: false, reason: `approval state is '${lookup.state ?? "unknown"}', not 'granted'` };
|
|
5311
|
+
}
|
|
5312
|
+
if (!requireOperator)
|
|
5313
|
+
return { allow: true };
|
|
5314
|
+
if (lookup.origin !== "operator") {
|
|
5315
|
+
return {
|
|
5316
|
+
allow: false,
|
|
5317
|
+
reason: `approval was recorded with origin='${lookup.origin ?? "unknown"}', not 'operator' \u2014 ` + `an agent-origin decision is not proof an operator tapped ` + `(${REQUIRE_OPERATOR_WRITE_ENV}=1)`
|
|
5318
|
+
};
|
|
5319
|
+
}
|
|
5320
|
+
return { allow: true };
|
|
5321
|
+
}
|
|
5322
|
+
function evaluateGatedWrite(lookup, requireOperator) {
|
|
5323
|
+
const verdict = isGatedWriteApproved(lookup, requireOperator);
|
|
5324
|
+
if (verdict.allow)
|
|
5325
|
+
return { kind: "allow" };
|
|
5326
|
+
if (lookup.state === "granted")
|
|
5327
|
+
return { kind: "block", reason: verdict.reason };
|
|
5328
|
+
return { kind: "wait" };
|
|
5329
|
+
}
|
|
5330
|
+
|
|
5303
5331
|
// src/cli/drive-write-pretool.ts
|
|
5304
5332
|
var HOOK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
5305
5333
|
var KERNEL_POLL_INTERVAL_MS = 2000;
|
|
@@ -5383,9 +5411,15 @@ async function approvalLookup(scope, approverSet) {
|
|
|
5383
5411
|
current_approver_set: approverSet
|
|
5384
5412
|
});
|
|
5385
5413
|
if (r === null || r.ok !== true)
|
|
5386
|
-
return null;
|
|
5387
|
-
|
|
5414
|
+
return { state: null };
|
|
5415
|
+
const dec = r.decision;
|
|
5416
|
+
const origin = dec != null && (dec.origin === "operator" || dec.origin === "agent") ? dec.origin : undefined;
|
|
5417
|
+
return {
|
|
5418
|
+
state: typeof r.state === "string" ? r.state : null,
|
|
5419
|
+
origin
|
|
5420
|
+
};
|
|
5388
5421
|
}
|
|
5422
|
+
var REQUIRE_OPERATOR_ORIGIN = requireOperatorApprovalForWrites();
|
|
5389
5423
|
function loadAllowFrom() {
|
|
5390
5424
|
const stateDir = process.env.TELEGRAM_STATE_DIR ?? join2(homedir2(), ".claude", "channels", "telegram");
|
|
5391
5425
|
const accessPath = join2(stateDir, "access.json");
|
|
@@ -5500,9 +5534,13 @@ async function main() {
|
|
|
5500
5534
|
block("no operator paired \u2014 cannot post approval card");
|
|
5501
5535
|
}
|
|
5502
5536
|
const existing = await approvalLookup(scope, allowFrom);
|
|
5503
|
-
|
|
5537
|
+
const existingAction = evaluateGatedWrite(existing, REQUIRE_OPERATOR_ORIGIN);
|
|
5538
|
+
if (existingAction.kind === "allow") {
|
|
5504
5539
|
allow();
|
|
5505
5540
|
}
|
|
5541
|
+
if (existingAction.kind === "block") {
|
|
5542
|
+
block(`Drive write refused: ${existingAction.reason}`);
|
|
5543
|
+
}
|
|
5506
5544
|
let handle;
|
|
5507
5545
|
try {
|
|
5508
5546
|
handle = await loadFromAuthBroker();
|
|
@@ -5546,10 +5584,15 @@ async function main() {
|
|
|
5546
5584
|
if (!first) {
|
|
5547
5585
|
await new Promise((r) => setTimeout(r, KERNEL_POLL_INTERVAL_MS));
|
|
5548
5586
|
}
|
|
5549
|
-
const
|
|
5550
|
-
|
|
5587
|
+
const lookup = await approvalLookup(scope, allowFrom);
|
|
5588
|
+
const state = lookup.state;
|
|
5589
|
+
const action = evaluateGatedWrite(lookup, REQUIRE_OPERATOR_ORIGIN);
|
|
5590
|
+
if (action.kind === "allow") {
|
|
5551
5591
|
allow();
|
|
5552
5592
|
}
|
|
5593
|
+
if (action.kind === "block") {
|
|
5594
|
+
block(`Drive write refused: ${action.reason}`);
|
|
5595
|
+
}
|
|
5553
5596
|
if (state === "denied" || state === "drift_revoked") {
|
|
5554
5597
|
block(`user denied Drive write (kernel verdict: ${state})`);
|
|
5555
5598
|
}
|
|
@@ -4944,6 +4944,34 @@ function buildBatchAbortReason(appliedInBatch) {
|
|
|
4944
4944
|
return `grant lapsed mid-batch \u2014 ${n} MS-365 write${n === 1 ? "" : "s"} already ` + `applied in this batch; remaining ops aborted. STOP: do not retry blindly \u2014 ` + `reconcile what landed before re-issuing.`;
|
|
4945
4945
|
}
|
|
4946
4946
|
|
|
4947
|
+
// src/vault/approvals/gated-write-policy.ts
|
|
4948
|
+
var REQUIRE_OPERATOR_WRITE_ENV = "SWITCHROOM_REQUIRE_OPERATOR_APPROVAL_WRITE";
|
|
4949
|
+
function requireOperatorApprovalForWrites(env = process.env) {
|
|
4950
|
+
return env[REQUIRE_OPERATOR_WRITE_ENV] === "1";
|
|
4951
|
+
}
|
|
4952
|
+
function isGatedWriteApproved(lookup, requireOperator) {
|
|
4953
|
+
if (lookup.state !== "granted") {
|
|
4954
|
+
return { allow: false, reason: `approval state is '${lookup.state ?? "unknown"}', not 'granted'` };
|
|
4955
|
+
}
|
|
4956
|
+
if (!requireOperator)
|
|
4957
|
+
return { allow: true };
|
|
4958
|
+
if (lookup.origin !== "operator") {
|
|
4959
|
+
return {
|
|
4960
|
+
allow: false,
|
|
4961
|
+
reason: `approval was recorded with origin='${lookup.origin ?? "unknown"}', not 'operator' \u2014 ` + `an agent-origin decision is not proof an operator tapped ` + `(${REQUIRE_OPERATOR_WRITE_ENV}=1)`
|
|
4962
|
+
};
|
|
4963
|
+
}
|
|
4964
|
+
return { allow: true };
|
|
4965
|
+
}
|
|
4966
|
+
function evaluateGatedWrite(lookup, requireOperator) {
|
|
4967
|
+
const verdict = isGatedWriteApproved(lookup, requireOperator);
|
|
4968
|
+
if (verdict.allow)
|
|
4969
|
+
return { kind: "allow" };
|
|
4970
|
+
if (lookup.state === "granted")
|
|
4971
|
+
return { kind: "block", reason: verdict.reason };
|
|
4972
|
+
return { kind: "wait" };
|
|
4973
|
+
}
|
|
4974
|
+
|
|
4947
4975
|
// src/cli/ms-365-write-pretool.ts
|
|
4948
4976
|
var HOOK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
4949
4977
|
var KERNEL_POLL_INTERVAL_MS = 2000;
|
|
@@ -5350,8 +5378,14 @@ async function approvalLookupByRequest(agentUnit, requestId, approverSet) {
|
|
|
5350
5378
|
});
|
|
5351
5379
|
if (!res.ok)
|
|
5352
5380
|
return null;
|
|
5353
|
-
|
|
5381
|
+
const value = res.value;
|
|
5382
|
+
const o = value.decision?.origin;
|
|
5383
|
+
return {
|
|
5384
|
+
state: value.state,
|
|
5385
|
+
origin: o === "operator" || o === "agent" ? o : undefined
|
|
5386
|
+
};
|
|
5354
5387
|
}
|
|
5388
|
+
var REQUIRE_OPERATOR_ORIGIN = requireOperatorApprovalForWrites();
|
|
5355
5389
|
function fail(reason) {
|
|
5356
5390
|
process.stdout.write(JSON.stringify({
|
|
5357
5391
|
decision: "block",
|
|
@@ -5426,10 +5460,14 @@ async function main() {
|
|
|
5426
5460
|
if (!lookup)
|
|
5427
5461
|
continue;
|
|
5428
5462
|
const state = lookup.state;
|
|
5429
|
-
|
|
5463
|
+
const action = evaluateGatedWrite({ state: state ?? null, origin: lookup.origin }, REQUIRE_OPERATOR_ORIGIN);
|
|
5464
|
+
if (action.kind === "allow") {
|
|
5430
5465
|
recordOutcome(Date.now(), { ...ledgerEntry, outcome: "applied" });
|
|
5431
5466
|
allow();
|
|
5432
5467
|
}
|
|
5468
|
+
if (action.kind === "block") {
|
|
5469
|
+
fail(action.reason);
|
|
5470
|
+
}
|
|
5433
5471
|
if (state === "expired" || state === "drift_revoked") {
|
|
5434
5472
|
const t = Date.now();
|
|
5435
5473
|
const applied = countCurrentBatchApplied(readLedger(t).entries, t);
|
|
@@ -11190,7 +11190,8 @@ var init_protocol = __esm(() => {
|
|
|
11190
11190
|
ttl_expires_at: exports_external.number().nullable(),
|
|
11191
11191
|
last_used_at: exports_external.number().nullable(),
|
|
11192
11192
|
revoked_at: exports_external.number().nullable(),
|
|
11193
|
-
revoke_reason: exports_external.string().nullable()
|
|
11193
|
+
revoke_reason: exports_external.string().nullable(),
|
|
11194
|
+
origin: exports_external.enum(["agent", "operator"]).optional()
|
|
11194
11195
|
});
|
|
11195
11196
|
OkApprovalLookupResponseSchema = exports_external.object({
|
|
11196
11197
|
ok: exports_external.literal(true),
|