switchroom 0.19.18 → 0.19.22
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 +56 -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 +5242 -2239
- package/dist/host-control/main.js +12241 -11375
- package/dist/vault/approvals/kernel-server.js +113 -7
- package/dist/vault/broker/server.js +259 -76
- package/package.json +6 -3
- package/profiles/_base/start.sh.hbs +61 -1
- package/skills/switchroom-release/SKILL.md +103 -20
- package/telegram-plugin/bridge/bridge.ts +14 -0
- package/telegram-plugin/card-format.ts +92 -3
- package/telegram-plugin/dist/bridge/bridge.js +13 -0
- package/telegram-plugin/dist/gateway/gateway.js +2356 -1159
- package/telegram-plugin/dist/server.js +13 -0
- package/telegram-plugin/edit-flood-fuse.ts +477 -0
- package/telegram-plugin/format.ts +19 -7
- package/telegram-plugin/gateway/always-allow-persist-queue.ts +97 -11
- package/telegram-plugin/gateway/boot-sweep-gate.ts +164 -0
- package/telegram-plugin/gateway/callback-query-handlers.ts +454 -81
- package/telegram-plugin/gateway/gateway.ts +66 -56
- package/telegram-plugin/gateway/inbound-interceptors.ts +27 -4
- package/telegram-plugin/gateway/missed-approvals-store.ts +66 -17
- package/telegram-plugin/gateway/narrative-lane.ts +49 -3
- package/telegram-plugin/gateway/pending-card-store.ts +46 -16
- package/telegram-plugin/gateway/scoped-grant-store.ts +39 -14
- package/telegram-plugin/gateway/status-pin-api.ts +145 -0
- package/telegram-plugin/gateway/store-file.ts +244 -0
- package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +325 -45
- package/telegram-plugin/hooks/tool-label-pretool.mjs +88 -2
- package/telegram-plugin/retry-api-call.ts +15 -2
- package/telegram-plugin/send-gate.ts +1 -1
- package/telegram-plugin/status-no-truncate.ts +64 -1
- package/telegram-plugin/status-pin-driver.ts +50 -27
- package/telegram-plugin/status-pin.ts +43 -5
- package/telegram-plugin/tests/activity-card-send-gate.test.ts +275 -0
- package/telegram-plugin/tests/activity-card-wiring.test.ts +16 -7
- package/telegram-plugin/tests/boot-pin-sweep-wiring.test.ts +101 -0
- package/telegram-plugin/tests/boot-sweep-gate.test.ts +293 -0
- package/telegram-plugin/tests/boot-version-string.test.ts +0 -0
- package/telegram-plugin/tests/bridge-tool-parity.test.ts +95 -0
- package/telegram-plugin/tests/edit-flood-fuse.test.ts +431 -0
- package/telegram-plugin/tests/pinned-card-collapse.test.ts +356 -0
- package/telegram-plugin/tests/status-pin-api.test.ts +178 -0
- package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +94 -11
- package/telegram-plugin/tests/status-pin.test.ts +106 -5
- package/telegram-plugin/tests/store-atomic-write.test.ts +411 -0
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +631 -1
- package/telegram-plugin/tests/tool-activity-summary.test.ts +28 -12
- package/telegram-plugin/tests/tool-label-pretool.test.ts +94 -0
- package/telegram-plugin/tests/vault-approval-posture.test.ts +6 -1
- package/telegram-plugin/tests/vault-passphrase-retry.test.ts +666 -0
- package/telegram-plugin/tests/vault-request-access-unlock-resume.test.ts +42 -21
- package/telegram-plugin/tests/worker-feed-coalesce.test.ts +233 -1
- package/telegram-plugin/tests/worker-feed-repeat-steps.test.ts +147 -0
- package/telegram-plugin/tool-activity-summary.ts +85 -13
- package/telegram-plugin/worker-activity-feed.ts +56 -2
- package/vendor/hindsight-memory/scripts/drain_pending.py +847 -67
- package/vendor/hindsight-memory/scripts/lib/client.py +124 -0
- package/vendor/hindsight-memory/scripts/lib/pending.py +944 -33
- package/vendor/hindsight-memory/scripts/lib/retain_split.py +460 -0
- package/vendor/hindsight-memory/scripts/recall.py +74 -5
- 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 +2275 -0
- package/vendor/hindsight-memory/scripts/tests/test_pending_failure_class.py +105 -0
- package/vendor/hindsight-memory/scripts/tests/test_pending_wedge.py +300 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_degraded_notice.py +365 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_envelope_strip_telemetry.py +12 -4
- package/vendor/hindsight-memory/scripts/tests/test_recall_transcript_fallback.py +27 -2
- package/vendor/hindsight-memory/scripts/tests/test_retain_split.py +438 -0
- package/vendor/hindsight-memory/scripts/tests/test_session_start_version_skew.py +204 -0
- package/vendor/hindsight-memory/tests/test_drain_pending.py +130 -8
- package/vendor/hindsight-memory/tests/test_pending.py +32 -7
|
@@ -18546,7 +18546,8 @@ var ApprovalDecisionMetaSchema = exports_external.object({
|
|
|
18546
18546
|
ttl_expires_at: exports_external.number().nullable(),
|
|
18547
18547
|
last_used_at: exports_external.number().nullable(),
|
|
18548
18548
|
revoked_at: exports_external.number().nullable(),
|
|
18549
|
-
revoke_reason: exports_external.string().nullable()
|
|
18549
|
+
revoke_reason: exports_external.string().nullable(),
|
|
18550
|
+
origin: exports_external.enum(["agent", "operator"]).optional()
|
|
18550
18551
|
});
|
|
18551
18552
|
var OkApprovalLookupResponseSchema = exports_external.object({
|
|
18552
18553
|
ok: exports_external.literal(true),
|
|
@@ -18710,6 +18711,7 @@ function migrateApprovalSchema(db) {
|
|
|
18710
18711
|
var DEFAULT_MAX_TTL_LIFETIME_MS = 7 * 24 * 60 * 60 * 1000;
|
|
18711
18712
|
|
|
18712
18713
|
// src/vault/approvals/kernel.ts
|
|
18714
|
+
var ALLOW_ONCE_CONSUMED_REASON = "allow_once_consumed";
|
|
18713
18715
|
function generateRequestId() {
|
|
18714
18716
|
return randomBytes(16).toString("hex");
|
|
18715
18717
|
}
|
|
@@ -18857,6 +18859,38 @@ function evaluateDecisionRow(db, row, currentCanonical, opts, now) {
|
|
|
18857
18859
|
if (decision.decision === "deny") {
|
|
18858
18860
|
return { state: "denied", decision };
|
|
18859
18861
|
}
|
|
18862
|
+
if (decision.decision === "allow_once") {
|
|
18863
|
+
const burn = db.run(`UPDATE approval_decisions
|
|
18864
|
+
SET last_used_at = ?, revoked_at = ?, revoke_reason = ?
|
|
18865
|
+
WHERE id = ? AND revoked_at IS NULL`, [now, now, ALLOW_ONCE_CONSUMED_REASON, decision.id]);
|
|
18866
|
+
if ((burn.changes ?? 0) === 0) {
|
|
18867
|
+
audit(db, "deny", {
|
|
18868
|
+
agent_unit: decision.agent_unit,
|
|
18869
|
+
scope: decision.scope,
|
|
18870
|
+
action: decision.action,
|
|
18871
|
+
decision_id: decision.id,
|
|
18872
|
+
context: { reason: ALLOW_ONCE_CONSUMED_REASON }
|
|
18873
|
+
});
|
|
18874
|
+
return { state: "denied", decision };
|
|
18875
|
+
}
|
|
18876
|
+
decision.last_used_at = now;
|
|
18877
|
+
decision.revoked_at = now;
|
|
18878
|
+
decision.revoke_reason = ALLOW_ONCE_CONSUMED_REASON;
|
|
18879
|
+
audit(db, "match", {
|
|
18880
|
+
agent_unit: decision.agent_unit,
|
|
18881
|
+
scope: decision.scope,
|
|
18882
|
+
action: decision.action,
|
|
18883
|
+
decision_id: decision.id
|
|
18884
|
+
});
|
|
18885
|
+
audit(db, "revoke", {
|
|
18886
|
+
agent_unit: decision.agent_unit,
|
|
18887
|
+
scope: decision.scope,
|
|
18888
|
+
action: decision.action,
|
|
18889
|
+
decision_id: decision.id,
|
|
18890
|
+
context: { actor: "kernel", reason: ALLOW_ONCE_CONSUMED_REASON }
|
|
18891
|
+
});
|
|
18892
|
+
return { state: "granted", decision };
|
|
18893
|
+
}
|
|
18860
18894
|
if (decision.decision === "allow_ttl") {
|
|
18861
18895
|
const maxLifetime = opts.max_ttl_lifetime_ms ?? DEFAULT_MAX_TTL_LIFETIME_MS;
|
|
18862
18896
|
const hardCap = decision.granted_at + maxLifetime;
|
|
@@ -18925,7 +18959,8 @@ function consumeNonce(db, request_id, now = Date.now()) {
|
|
|
18925
18959
|
}
|
|
18926
18960
|
function recordDecision(db, input, now = Date.now()) {
|
|
18927
18961
|
const id = randomUUID();
|
|
18928
|
-
const
|
|
18962
|
+
const maxLifetime = input.max_ttl_lifetime_ms ?? DEFAULT_MAX_TTL_LIFETIME_MS;
|
|
18963
|
+
const ttl_expires_at = input.decision === "allow_ttl" && input.ttl_ms ? now + Math.min(input.ttl_ms, maxLifetime) : null;
|
|
18929
18964
|
const canonical = canonicalizeApproverSet(input.approver_set);
|
|
18930
18965
|
db.run(`INSERT INTO approval_decisions
|
|
18931
18966
|
(id, agent_unit, scope, action, decision,
|
|
@@ -19112,6 +19147,47 @@ for (const name of SHARED_FRAGMENTS) {
|
|
|
19112
19147
|
}
|
|
19113
19148
|
}
|
|
19114
19149
|
|
|
19150
|
+
// src/litellm/timeout-budget.ts
|
|
19151
|
+
var LITELLM_ROUTER_MARGIN_S = 10;
|
|
19152
|
+
var LITELLM_TIMEOUT_TIERS = {
|
|
19153
|
+
interactive: {
|
|
19154
|
+
group: "gpt-oss-20b",
|
|
19155
|
+
localTimeoutS: 90,
|
|
19156
|
+
fallbackGroup: "gpt-oss-20b-openrouter",
|
|
19157
|
+
fallbackTimeoutS: 60
|
|
19158
|
+
},
|
|
19159
|
+
retain: {
|
|
19160
|
+
group: "gpt-oss-20b-retain",
|
|
19161
|
+
localTimeoutS: 200,
|
|
19162
|
+
fallbackGroup: "gpt-oss-20b-retain-openrouter",
|
|
19163
|
+
fallbackTimeoutS: 90
|
|
19164
|
+
},
|
|
19165
|
+
consolidation: {
|
|
19166
|
+
group: "gpt-oss-20b-consolidation",
|
|
19167
|
+
localTimeoutS: 200,
|
|
19168
|
+
fallbackGroup: "gpt-oss-20b-consolidation-openrouter",
|
|
19169
|
+
fallbackTimeoutS: 90
|
|
19170
|
+
}
|
|
19171
|
+
};
|
|
19172
|
+
function litellmChainSeconds(tier) {
|
|
19173
|
+
assertPositive(tier.localTimeoutS, `${tier.group} localTimeoutS`);
|
|
19174
|
+
if (tier.fallbackGroup === null)
|
|
19175
|
+
return tier.localTimeoutS;
|
|
19176
|
+
assertPositive(tier.fallbackTimeoutS, `${tier.fallbackGroup} fallbackTimeoutS`);
|
|
19177
|
+
return tier.localTimeoutS + tier.fallbackTimeoutS;
|
|
19178
|
+
}
|
|
19179
|
+
function minimumClientBudgetSeconds(tier, marginS = LITELLM_ROUTER_MARGIN_S) {
|
|
19180
|
+
return litellmChainSeconds(tier) + marginS;
|
|
19181
|
+
}
|
|
19182
|
+
function clientBudgetSeconds(tier, floorS = 0, marginS = LITELLM_ROUTER_MARGIN_S) {
|
|
19183
|
+
return Math.max(Math.ceil(floorS), minimumClientBudgetSeconds(tier, marginS));
|
|
19184
|
+
}
|
|
19185
|
+
function assertPositive(value, label) {
|
|
19186
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
19187
|
+
throw new Error(`litellm timeout budget: ${label} must be a positive number, got ${value}`);
|
|
19188
|
+
}
|
|
19189
|
+
}
|
|
19190
|
+
|
|
19115
19191
|
// src/setup/hindsight.ts
|
|
19116
19192
|
var HINDSIGHT_DEFAULT_API_PORT = 18888;
|
|
19117
19193
|
var HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
|
|
@@ -19121,8 +19197,21 @@ var HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight";
|
|
|
19121
19197
|
var HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
|
|
19122
19198
|
var HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`;
|
|
19123
19199
|
var HINDSIGHT_CREDS_MIRROR_VOLUME = `consumer-creds-${HINDSIGHT_CONSUMER_NAME}`;
|
|
19200
|
+
var HINDSIGHT_DEFAULT_RETAIN_MAX_COMPLETION_TOKENS = 16384;
|
|
19201
|
+
var HINDSIGHT_RETAIN_MIN_TOKENS_PER_SECOND = 80.6;
|
|
19202
|
+
function hindsightRetainLlmTimeoutSeconds(maxCompletionTokens = HINDSIGHT_DEFAULT_RETAIN_MAX_COMPLETION_TOKENS, tokensPerSecond = HINDSIGHT_RETAIN_MIN_TOKENS_PER_SECOND) {
|
|
19203
|
+
if (!(maxCompletionTokens > 0) || !(tokensPerSecond > 0)) {
|
|
19204
|
+
throw new Error(`hindsight retain budget: maxCompletionTokens (${maxCompletionTokens}) and ` + `tokensPerSecond (${tokensPerSecond}) must both be positive`);
|
|
19205
|
+
}
|
|
19206
|
+
return Math.ceil(maxCompletionTokens / tokensPerSecond);
|
|
19207
|
+
}
|
|
19208
|
+
function hindsightRetainClientTimeoutSeconds() {
|
|
19209
|
+
return clientBudgetSeconds(LITELLM_TIMEOUT_TIERS.retain, hindsightRetainLlmTimeoutSeconds());
|
|
19210
|
+
}
|
|
19211
|
+
var HINDSIGHT_RETAIN_CLIENT_DEADLINE_S = hindsightRetainClientTimeoutSeconds() + LITELLM_ROUTER_MARGIN_S;
|
|
19124
19212
|
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)';
|
|
19125
19213
|
var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
|
|
19214
|
+
var DOCKER_PROBE_TIMEOUT_MS = 60 * 1000;
|
|
19126
19215
|
|
|
19127
19216
|
// src/memory/hindsight.ts
|
|
19128
19217
|
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.
|
|
@@ -19385,6 +19474,9 @@ function handleConnection(socket, agent, db, isOperator = false) {
|
|
|
19385
19474
|
}
|
|
19386
19475
|
var KERNEL_OPERATOR_NAME = "operator";
|
|
19387
19476
|
var OPERATOR_ALLOWED_OPS = new Set(["approval_list"]);
|
|
19477
|
+
function listenerOrigin(isOperator) {
|
|
19478
|
+
return isOperator ? "operator" : "agent";
|
|
19479
|
+
}
|
|
19388
19480
|
function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
19389
19481
|
if (isOperator && !OPERATOR_ALLOWED_OPS.has(req.op)) {
|
|
19390
19482
|
socket.write(encodeResponse(errorResponse("DENIED", `kernel operator socket is read-only: '${req.op}' is not permitted (only ${[...OPERATOR_ALLOWED_OPS].join(", ")})`)));
|
|
@@ -19445,7 +19537,8 @@ function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
|
19445
19537
|
ttl_expires_at: r.decision.ttl_expires_at,
|
|
19446
19538
|
last_used_at: r.decision.last_used_at,
|
|
19447
19539
|
revoked_at: r.decision.revoked_at,
|
|
19448
|
-
revoke_reason: r.decision.revoke_reason
|
|
19540
|
+
revoke_reason: r.decision.revoke_reason,
|
|
19541
|
+
origin: r.decision.origin
|
|
19449
19542
|
} : null;
|
|
19450
19543
|
socket.write(encodeResponse({ ok: true, state: r.state, decision }));
|
|
19451
19544
|
return;
|
|
@@ -19472,7 +19565,8 @@ function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
|
19472
19565
|
ttl_expires_at: r.decision.ttl_expires_at,
|
|
19473
19566
|
last_used_at: r.decision.last_used_at,
|
|
19474
19567
|
revoked_at: r.decision.revoked_at,
|
|
19475
|
-
revoke_reason: r.decision.revoke_reason
|
|
19568
|
+
revoke_reason: r.decision.revoke_reason,
|
|
19569
|
+
origin: r.decision.origin
|
|
19476
19570
|
} : null;
|
|
19477
19571
|
socket.write(encodeResponse({ ok: true, state: r.state, decision }));
|
|
19478
19572
|
return;
|
|
@@ -19534,7 +19628,8 @@ function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
|
19534
19628
|
decision: req.decision,
|
|
19535
19629
|
approver_set: req.approver_set,
|
|
19536
19630
|
granted_by_user_id: req.granted_by_user_id,
|
|
19537
|
-
ttl_ms: req.ttl_ms ?? undefined
|
|
19631
|
+
ttl_ms: req.ttl_ms ?? undefined,
|
|
19632
|
+
origin: listenerOrigin(isOperator)
|
|
19538
19633
|
});
|
|
19539
19634
|
socket.write(encodeResponse({ ok: true, decision_id }));
|
|
19540
19635
|
return;
|
|
@@ -19553,7 +19648,8 @@ function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
|
19553
19648
|
decision: req.decision,
|
|
19554
19649
|
approver_set: req.approver_set,
|
|
19555
19650
|
granted_by_user_id: req.granted_by_user_id,
|
|
19556
|
-
ttl_ms: req.ttl_ms ?? undefined
|
|
19651
|
+
ttl_ms: req.ttl_ms ?? undefined,
|
|
19652
|
+
origin: listenerOrigin(isOperator)
|
|
19557
19653
|
});
|
|
19558
19654
|
if (!res.consumed) {
|
|
19559
19655
|
socket.write(encodeResponse({ ok: true, consumed: false }));
|
|
@@ -19571,7 +19667,16 @@ function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
|
19571
19667
|
return;
|
|
19572
19668
|
}
|
|
19573
19669
|
if (req.op === "approval_list") {
|
|
19574
|
-
|
|
19670
|
+
let listFilter = { agent_unit: req.agent_unit };
|
|
19671
|
+
if (!isOperator) {
|
|
19672
|
+
const acl = checkApprovalAclByAgent(agent, req.agent_unit ?? agent);
|
|
19673
|
+
if (!acl.allow) {
|
|
19674
|
+
socket.write(encodeResponse(errorResponse("DENIED", acl.reason)));
|
|
19675
|
+
return;
|
|
19676
|
+
}
|
|
19677
|
+
listFilter = { agent_unit: agent };
|
|
19678
|
+
}
|
|
19679
|
+
const decisions = listDecisions(db, listFilter);
|
|
19575
19680
|
const meta = decisions.map((d) => ({
|
|
19576
19681
|
id: d.id,
|
|
19577
19682
|
agent_unit: d.agent_unit,
|
|
@@ -19733,6 +19838,7 @@ if (import.meta.url === `file://${process.argv[1]}` && /(?:^|[/\\])(?:vault[/\\]
|
|
|
19733
19838
|
export {
|
|
19734
19839
|
openKernelDb,
|
|
19735
19840
|
main,
|
|
19841
|
+
listenerOrigin,
|
|
19736
19842
|
bootstrap,
|
|
19737
19843
|
KERNEL_OPERATOR_NAME
|
|
19738
19844
|
};
|