switchroom 0.18.28 → 0.18.30
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/bin/handoff-briefing.sh +15 -2
- package/dist/agent-scheduler/index.js +111 -7
- package/dist/auth-broker/index.js +154 -73
- package/dist/cli/autoaccept-poll.js +8 -3
- package/dist/cli/drive-write-pretool.mjs +8 -3
- package/dist/cli/ms-365-write-pretool.mjs +158 -11
- package/dist/cli/notion-write-pretool.mjs +103 -4
- package/dist/cli/switchroom.js +2712 -2219
- package/dist/host-control/main.js +110 -70
- package/dist/vault/approvals/kernel-server.js +116 -70
- package/dist/vault/broker/server.js +314 -202
- package/package.json +3 -3
- package/profiles/_base/start.sh.hbs +105 -34
- package/telegram-plugin/dist/bridge/bridge.js +71 -47
- package/telegram-plugin/dist/gateway/gateway.js +1128 -666
- package/telegram-plugin/dist/server.js +89 -64
- package/telegram-plugin/gateway/backstop-delivery.ts +272 -0
- package/telegram-plugin/gateway/forward-origin.ts +9 -1
- package/telegram-plugin/gateway/gateway.ts +656 -388
- package/telegram-plugin/gateway/model-command.ts +331 -602
- package/telegram-plugin/gateway/session-model-file.ts +40 -0
- package/telegram-plugin/gateway/turn-record-status.ts +45 -0
- package/telegram-plugin/gateway/unhandled-message.ts +177 -0
- package/telegram-plugin/history.ts +153 -23
- package/telegram-plugin/llm-error-present.ts +24 -0
- package/telegram-plugin/model-unavailable.ts +55 -0
- package/telegram-plugin/operator-events.ts +113 -0
- package/telegram-plugin/pending-user-notice.ts +88 -0
- package/telegram-plugin/shared/local-time.ts +99 -0
- package/telegram-plugin/tests/backstop-delivery.test.ts +250 -0
- package/telegram-plugin/tests/catch-all-forwarded-history.test.ts +103 -0
- package/telegram-plugin/tests/catch-all-unhandled-message.test.ts +264 -0
- package/telegram-plugin/tests/forward-origin.test.ts +30 -3
- package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +111 -60
- package/telegram-plugin/tests/history.test.ts +88 -0
- package/telegram-plugin/tests/litellm-proxy-auth-misconfig.test.ts +278 -0
- package/telegram-plugin/tests/local-time.test.ts +135 -0
- package/telegram-plugin/tests/model-command.test.ts +427 -1512
- package/telegram-plugin/tests/session-model-file.test.ts +23 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +34 -0
- package/telegram-plugin/tier-downgrade.ts +4 -3
- package/telegram-plugin/turn-flush-safety.ts +25 -1
- package/vendor/hindsight-memory/scripts/backfill_transcripts.py +399 -2
- package/vendor/hindsight-memory/scripts/lib/client.py +47 -0
- package/vendor/hindsight-memory/scripts/lib/content.py +93 -7
- package/vendor/hindsight-memory/scripts/lib/turnlog.py +450 -0
- package/vendor/hindsight-memory/scripts/tests/test_backfill_from_logs.py +467 -0
- package/vendor/hindsight-memory/tests/test_content.py +63 -7
|
@@ -18991,11 +18991,52 @@ var AgentGoogleWorkspaceConfigSchema = exports_external.object({
|
|
|
18991
18991
|
approvers: exports_external.array(ApproverIdSchema).min(1).optional().describe("Per-agent approver override. When set, replaces (does not extend) " + "the top-level drive.approvers list for this agent's onboarding card."),
|
|
18992
18992
|
tier: GoogleWorkspaceTierSchema.optional().describe("Per-agent tier override (RFC G Phase 1). When set, replaces the " + "top-level google_workspace.tier for this agent. Common case: most " + "agents on `core`, one specialist on `extended` for Slides access.")
|
|
18993
18993
|
}).optional();
|
|
18994
|
+
var MicrosoftAccountEmailSchema = exports_external.string().regex(/^[^@\s:]+@[^@\s:]+\.[^@\s:]+$/, {
|
|
18995
|
+
message: "microsoft_workspace.account must be a Microsoft account email like " + "'alice@outlook.com' or 'alice@contoso.com' (colons not allowed)"
|
|
18996
|
+
}).transform((v) => v.trim().toLowerCase());
|
|
18997
|
+
var MicrosoftToolTokenSchema = exports_external.string().min(1).regex(/^[a-z0-9-]+$/, {
|
|
18998
|
+
message: "microsoft_workspace tools[] tokens are forwarded to softeria's " + "--enabled-tools regex; each must be lowercase kebab-case " + "([a-z0-9-]+, e.g. 'mail', 'calendar', 'send-mail') so an unescaped " + "regex metacharacter can't crash the launcher"
|
|
18999
|
+
});
|
|
19000
|
+
var MicrosoftAccountBindingSchema = exports_external.object({
|
|
19001
|
+
account: MicrosoftAccountEmailSchema.describe("The Microsoft account this binding uses. Must be a key in top-level " + "`microsoft_accounts:` with this agent in its `enabled_for[]`."),
|
|
19002
|
+
tools: exports_external.array(MicrosoftToolTokenSchema).min(1).optional().describe("Per-account tool allowlist → softeria `--enabled-tools <regex>` " + "(tokens joined with `|`). Omitted = all tools exposed for this account."),
|
|
19003
|
+
org_mode: exports_external.boolean().optional().describe("Per-binding org_mode override (RFC #1873 §6.4).")
|
|
19004
|
+
});
|
|
18994
19005
|
var AgentMicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
18995
|
-
account:
|
|
18996
|
-
|
|
18997
|
-
|
|
18998
|
-
|
|
19006
|
+
account: MicrosoftAccountEmailSchema.optional().describe("RFC #1873: the Microsoft account this agent uses for the M365 MCP. " + "Must be a key in top-level `microsoft_accounts:` with this agent " + "listed in its `enabled_for[]`. Read by the auth-broker " + "(get-credentials, provider=microsoft) and by the scaffold to " + "decide whether to emit the `ms-365` MCP entry. Normalized to " + "lowercase so it matches the microsoft_accounts key (which is " + "also normalized). Mutually exclusive with `accounts` (plural)."),
|
|
19007
|
+
tools: exports_external.array(MicrosoftToolTokenSchema).min(1).optional().describe("Per-account tool allowlist for the SINGULAR `account` form → " + "softeria `--enabled-tools <regex>`. Omitted = all tools. Only " + "valid with the singular `account`; using it together with the " + "plural `accounts` (which carries per-binding `tools`) is an error."),
|
|
19008
|
+
org_mode: exports_external.boolean().optional().describe("Per-agent org_mode override (RFC #1873 §6.4). When set, replaces " + "the top-level microsoft_workspace.org_mode for this agent. " + "Defaults to top-level value (which defaults to false)."),
|
|
19009
|
+
accounts: exports_external.array(MicrosoftAccountBindingSchema).min(1).optional().describe("Plural multi-account form: bind MULTIPLE Microsoft accounts to " + "this agent, each with its own tool scope. Mutually exclusive with " + "the singular `account`. Each account gets its own `ms-365-<slug>` " + "MCP server.")
|
|
19010
|
+
}).superRefine((v, ctx) => {
|
|
19011
|
+
const hasSingular = v.account !== undefined;
|
|
19012
|
+
const hasPlural = v.accounts !== undefined;
|
|
19013
|
+
if (hasSingular && hasPlural) {
|
|
19014
|
+
ctx.addIssue({
|
|
19015
|
+
code: exports_external.ZodIssueCode.custom,
|
|
19016
|
+
message: "microsoft_workspace: use EITHER `account` (singular) OR " + "`accounts` (plural array), not both",
|
|
19017
|
+
path: ["accounts"]
|
|
19018
|
+
});
|
|
19019
|
+
}
|
|
19020
|
+
if (hasPlural && v.tools !== undefined) {
|
|
19021
|
+
ctx.addIssue({
|
|
19022
|
+
code: exports_external.ZodIssueCode.custom,
|
|
19023
|
+
message: "microsoft_workspace: block-level `tools` applies to the singular " + "`account` only; with `accounts` put `tools` inside each binding",
|
|
19024
|
+
path: ["tools"]
|
|
19025
|
+
});
|
|
19026
|
+
}
|
|
19027
|
+
if (hasPlural) {
|
|
19028
|
+
const seen = new Set;
|
|
19029
|
+
for (const b of v.accounts) {
|
|
19030
|
+
if (seen.has(b.account)) {
|
|
19031
|
+
ctx.addIssue({
|
|
19032
|
+
code: exports_external.ZodIssueCode.custom,
|
|
19033
|
+
message: `microsoft_workspace: duplicate account '${b.account}' in accounts[]`,
|
|
19034
|
+
path: ["accounts"]
|
|
19035
|
+
});
|
|
19036
|
+
}
|
|
19037
|
+
seen.add(b.account);
|
|
19038
|
+
}
|
|
19039
|
+
}
|
|
18999
19040
|
}).optional();
|
|
19000
19041
|
var AgentNotionWorkspaceConfigSchema = exports_external.object({
|
|
19001
19042
|
databases: exports_external.array(exports_external.string().regex(/^[a-z0-9][a-z0-9_-]{0,62}$/, {
|
|
@@ -19370,6 +19411,26 @@ var SwitchroomConfigSchema = exports_external.object({
|
|
|
19370
19411
|
for (const [name, a] of Object.entries(cfg.agents ?? {})) {
|
|
19371
19412
|
checkServes(a.serves, ["agents", name, "serves"]);
|
|
19372
19413
|
}
|
|
19414
|
+
const microsoftAccounts = cfg.microsoft_accounts;
|
|
19415
|
+
for (const [name, a] of Object.entries(cfg.agents ?? {})) {
|
|
19416
|
+
const mw = a.microsoft_workspace;
|
|
19417
|
+
const accounts = mw?.accounts;
|
|
19418
|
+
if (!accounts)
|
|
19419
|
+
continue;
|
|
19420
|
+
accounts.forEach((b, i) => {
|
|
19421
|
+
const acct = b?.account?.trim().toLowerCase();
|
|
19422
|
+
if (!acct)
|
|
19423
|
+
return;
|
|
19424
|
+
const enabledFor = microsoftAccounts?.[acct]?.enabled_for ?? [];
|
|
19425
|
+
if (!enabledFor.includes(name)) {
|
|
19426
|
+
ctx.addIssue({
|
|
19427
|
+
code: exports_external.ZodIssueCode.custom,
|
|
19428
|
+
message: `agent '${name}' binds Microsoft account '${acct}' but is not in ` + `microsoft_accounts['${acct}'].enabled_for[] — operator must run ` + `\`switchroom auth microsoft enable ${acct} ${name}\``,
|
|
19429
|
+
path: ["agents", name, "microsoft_workspace", "accounts", i, "account"]
|
|
19430
|
+
});
|
|
19431
|
+
}
|
|
19432
|
+
});
|
|
19433
|
+
}
|
|
19373
19434
|
});
|
|
19374
19435
|
|
|
19375
19436
|
// src/config/paths.ts
|
|
@@ -19974,6 +20035,22 @@ function validateNotionWorkspaceConfig(config) {
|
|
|
19974
20035
|
return issues;
|
|
19975
20036
|
}
|
|
19976
20037
|
|
|
20038
|
+
// src/config/timezone.ts
|
|
20039
|
+
function isResolvableTimezone(zone) {
|
|
20040
|
+
try {
|
|
20041
|
+
new Intl.DateTimeFormat("en-US", { timeZone: zone });
|
|
20042
|
+
return true;
|
|
20043
|
+
} catch {
|
|
20044
|
+
return false;
|
|
20045
|
+
}
|
|
20046
|
+
}
|
|
20047
|
+
var CONTAINER_DEFAULT_UTC_ZONES = new Set([
|
|
20048
|
+
"UTC",
|
|
20049
|
+
"Etc/UTC",
|
|
20050
|
+
"Etc/Universal",
|
|
20051
|
+
"Universal"
|
|
20052
|
+
]);
|
|
20053
|
+
|
|
19977
20054
|
// src/config/loader.ts
|
|
19978
20055
|
class ConfigError extends Error {
|
|
19979
20056
|
details;
|
|
@@ -20097,8 +20174,30 @@ function loadConfig(configPath) {
|
|
|
20097
20174
|
if (notionIssues.length > 0) {
|
|
20098
20175
|
throw new ConfigError(`Invalid notion_workspace configuration in ${filePath}`, notionIssues);
|
|
20099
20176
|
}
|
|
20177
|
+
validateAllTimezones(config, filePath);
|
|
20100
20178
|
return config;
|
|
20101
20179
|
}
|
|
20180
|
+
function validateAllTimezones(config, filePath) {
|
|
20181
|
+
const issues = [];
|
|
20182
|
+
const check = (zone, where) => {
|
|
20183
|
+
if (zone == null)
|
|
20184
|
+
return;
|
|
20185
|
+
if (!isResolvableTimezone(zone)) {
|
|
20186
|
+
issues.push(` ${where}: "${zone}" is not a resolvable IANA timezone ` + `(shape is valid but no such zone exists — check for a typo, ` + `e.g. "Australia/Melbourne", "America/New_York", "UTC").`);
|
|
20187
|
+
}
|
|
20188
|
+
};
|
|
20189
|
+
check(config.switchroom?.timezone, "switchroom.timezone");
|
|
20190
|
+
check(config.defaults?.timezone, "defaults.timezone");
|
|
20191
|
+
for (const [profileName, profile] of Object.entries(config.profiles ?? {})) {
|
|
20192
|
+
check(profile?.timezone, `profiles.${profileName}.timezone`);
|
|
20193
|
+
}
|
|
20194
|
+
for (const [agentName, agentRaw] of Object.entries(config.agents)) {
|
|
20195
|
+
check(agentRaw?.timezone, `agents.${agentName}.timezone`);
|
|
20196
|
+
}
|
|
20197
|
+
if (issues.length > 0) {
|
|
20198
|
+
throw new ConfigError(`Invalid timezone configuration in ${filePath}`, issues);
|
|
20199
|
+
}
|
|
20200
|
+
}
|
|
20102
20201
|
function validateAllCronTopicAliases(config, filePath) {
|
|
20103
20202
|
const issues = [];
|
|
20104
20203
|
for (const [agentName, agentRaw] of Object.entries(config.agents)) {
|
|
@@ -20568,14 +20667,6 @@ function allocateAgentUid(name) {
|
|
|
20568
20667
|
return AGENT_UID_MIN + u32 % range;
|
|
20569
20668
|
}
|
|
20570
20669
|
|
|
20571
|
-
// src/config/timezone.ts
|
|
20572
|
-
var CONTAINER_DEFAULT_UTC_ZONES = new Set([
|
|
20573
|
-
"UTC",
|
|
20574
|
-
"Etc/UTC",
|
|
20575
|
-
"Etc/Universal",
|
|
20576
|
-
"Universal"
|
|
20577
|
-
]);
|
|
20578
|
-
|
|
20579
20670
|
// src/cli/agent-config.ts
|
|
20580
20671
|
import { join as join2 } from "node:path";
|
|
20581
20672
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -20610,63 +20701,6 @@ for (const name of SHARED_FRAGMENTS) {
|
|
|
20610
20701
|
}
|
|
20611
20702
|
}
|
|
20612
20703
|
|
|
20613
|
-
// src/agents/pane-lock.ts
|
|
20614
|
-
var tails = new Map;
|
|
20615
|
-
|
|
20616
|
-
// src/agents/inject.ts
|
|
20617
|
-
var INJECT_COMMANDS = new Map([
|
|
20618
|
-
["/cost", { description: "Show session cost", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
20619
|
-
["/status", { description: "Show session status", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
20620
|
-
["/usage", { description: "Show plan quota", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
20621
|
-
["/hooks", { description: "List configured hooks", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
20622
|
-
["/memory", { description: "Open memory picker", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
20623
|
-
["/help", { description: "Show help / command discovery", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
20624
|
-
["/context", { description: "Show context window usage", expectsOutput: true, argsAllowed: false }],
|
|
20625
|
-
["/release-notes", { description: "Show release-notes version list", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
20626
|
-
["/model", { description: "Open model picker", expectsOutput: true, argsAllowed: true }],
|
|
20627
|
-
[
|
|
20628
|
-
"/clear",
|
|
20629
|
-
{
|
|
20630
|
-
description: "Clear session screen",
|
|
20631
|
-
expectsOutput: false,
|
|
20632
|
-
silentNote: "context cleared — fresh slate",
|
|
20633
|
-
argsAllowed: false
|
|
20634
|
-
}
|
|
20635
|
-
],
|
|
20636
|
-
[
|
|
20637
|
-
"/compact",
|
|
20638
|
-
{
|
|
20639
|
-
description: "Compact conversation history",
|
|
20640
|
-
expectsOutput: false,
|
|
20641
|
-
silentNote: "compaction runs silently",
|
|
20642
|
-
argsAllowed: false
|
|
20643
|
-
}
|
|
20644
|
-
]
|
|
20645
|
-
]);
|
|
20646
|
-
var INJECT_ALLOWLIST = new Set(INJECT_COMMANDS.keys());
|
|
20647
|
-
var INJECT_BLOCKED = new Map([
|
|
20648
|
-
["/login", { reason: "would mutate auth state" }],
|
|
20649
|
-
["/logout", { reason: "would terminate the agent's auth session" }],
|
|
20650
|
-
["/exit", { reason: "would kill the agent process" }],
|
|
20651
|
-
["/quit", { reason: "would kill the agent process" }],
|
|
20652
|
-
["/upgrade", { reason: "mutates the Claude Code installation" }],
|
|
20653
|
-
["/init", { reason: "generates/overwrites CLAUDE.md and runs a model turn" }],
|
|
20654
|
-
["/mcp", { reason: "opens an interactive MCP server management dialog" }],
|
|
20655
|
-
["/permissions", { reason: "opens an interactive permissions editor that mutates tool policy" }],
|
|
20656
|
-
["/install-github-app", { reason: "runs a network/OAuth install flow" }],
|
|
20657
|
-
["/add-dir", { reason: "mutates the session's working-directory set" }],
|
|
20658
|
-
["/terminal-setup", { reason: "mutates terminal keybinding configuration" }],
|
|
20659
|
-
["/privacy-settings", { reason: "opens an interactive privacy-settings dialog" }],
|
|
20660
|
-
["/bug", { reason: "submits a bug report over the network" }],
|
|
20661
|
-
[
|
|
20662
|
-
"/effort",
|
|
20663
|
-
{
|
|
20664
|
-
reason: "leaves a blocking confirmation modal open and wedges the pane; use the /effort command (it drives the modal), not raw inject"
|
|
20665
|
-
}
|
|
20666
|
-
]
|
|
20667
|
-
]);
|
|
20668
|
-
var INJECT_BLOCKLIST = new Set(INJECT_BLOCKED.keys());
|
|
20669
|
-
|
|
20670
20704
|
// src/setup/hindsight.ts
|
|
20671
20705
|
var HINDSIGHT_DEFAULT_API_PORT = 18888;
|
|
20672
20706
|
var HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
|
|
@@ -21159,6 +21193,12 @@ var SWITCHROOM_OWNED_SETTINGS_KEYS = new Set([
|
|
|
21159
21193
|
var DOCKER_TELEGRAM_PLUGIN_PATH = "/opt/switchroom/telegram-plugin";
|
|
21160
21194
|
var DOCKER_HOOKS_PATH = `${DOCKER_TELEGRAM_PLUGIN_PATH}/hooks`;
|
|
21161
21195
|
|
|
21196
|
+
// src/vault/grants-db-path.ts
|
|
21197
|
+
var GRANTS_DB_DIRNAME = "vault-broker";
|
|
21198
|
+
var GRANTS_DB_FILENAME = "vault-grants.db";
|
|
21199
|
+
var GRANTS_DB_CONTAINER_DIR = `/root/.switchroom/${GRANTS_DB_DIRNAME}`;
|
|
21200
|
+
var GRANTS_DB_CONTAINER_PATH = `${GRANTS_DB_CONTAINER_DIR}/${GRANTS_DB_FILENAME}`;
|
|
21201
|
+
|
|
21162
21202
|
// src/agents/compose.ts
|
|
21163
21203
|
var BIND_MOUNT_EXACT_SOURCE_DENY = new Set(["/var/run/docker.sock"]);
|
|
21164
21204
|
|
|
@@ -26605,7 +26645,7 @@ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:f
|
|
|
26605
26645
|
import { dirname as dirname4, join as join7 } from "node:path";
|
|
26606
26646
|
|
|
26607
26647
|
// src/build-info.ts
|
|
26608
|
-
var VERSION = "0.18.
|
|
26648
|
+
var VERSION = "0.18.30";
|
|
26609
26649
|
|
|
26610
26650
|
// src/cli/resolve-version.ts
|
|
26611
26651
|
function readPackageVersion() {
|
|
@@ -4013,7 +4013,7 @@ var init_zod = __esm(() => {
|
|
|
4013
4013
|
});
|
|
4014
4014
|
|
|
4015
4015
|
// src/config/schema.ts
|
|
4016
|
-
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, ReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
4016
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, ReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
4017
4017
|
var init_schema = __esm(() => {
|
|
4018
4018
|
init_zod();
|
|
4019
4019
|
CodeRepoEntrySchema = exports_external.object({
|
|
@@ -4411,11 +4411,52 @@ var init_schema = __esm(() => {
|
|
|
4411
4411
|
approvers: exports_external.array(ApproverIdSchema).min(1).optional().describe("Per-agent approver override. When set, replaces (does not extend) " + "the top-level drive.approvers list for this agent's onboarding card."),
|
|
4412
4412
|
tier: GoogleWorkspaceTierSchema.optional().describe("Per-agent tier override (RFC G Phase 1). When set, replaces the " + "top-level google_workspace.tier for this agent. Common case: most " + "agents on `core`, one specialist on `extended` for Slides access.")
|
|
4413
4413
|
}).optional();
|
|
4414
|
+
MicrosoftAccountEmailSchema = exports_external.string().regex(/^[^@\s:]+@[^@\s:]+\.[^@\s:]+$/, {
|
|
4415
|
+
message: "microsoft_workspace.account must be a Microsoft account email like " + "'alice@outlook.com' or 'alice@contoso.com' (colons not allowed)"
|
|
4416
|
+
}).transform((v) => v.trim().toLowerCase());
|
|
4417
|
+
MicrosoftToolTokenSchema = exports_external.string().min(1).regex(/^[a-z0-9-]+$/, {
|
|
4418
|
+
message: "microsoft_workspace tools[] tokens are forwarded to softeria's " + "--enabled-tools regex; each must be lowercase kebab-case " + "([a-z0-9-]+, e.g. 'mail', 'calendar', 'send-mail') so an unescaped " + "regex metacharacter can't crash the launcher"
|
|
4419
|
+
});
|
|
4420
|
+
MicrosoftAccountBindingSchema = exports_external.object({
|
|
4421
|
+
account: MicrosoftAccountEmailSchema.describe("The Microsoft account this binding uses. Must be a key in top-level " + "`microsoft_accounts:` with this agent in its `enabled_for[]`."),
|
|
4422
|
+
tools: exports_external.array(MicrosoftToolTokenSchema).min(1).optional().describe("Per-account tool allowlist → softeria `--enabled-tools <regex>` " + "(tokens joined with `|`). Omitted = all tools exposed for this account."),
|
|
4423
|
+
org_mode: exports_external.boolean().optional().describe("Per-binding org_mode override (RFC #1873 §6.4).")
|
|
4424
|
+
});
|
|
4414
4425
|
AgentMicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
4415
|
-
account:
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4426
|
+
account: MicrosoftAccountEmailSchema.optional().describe("RFC #1873: the Microsoft account this agent uses for the M365 MCP. " + "Must be a key in top-level `microsoft_accounts:` with this agent " + "listed in its `enabled_for[]`. Read by the auth-broker " + "(get-credentials, provider=microsoft) and by the scaffold to " + "decide whether to emit the `ms-365` MCP entry. Normalized to " + "lowercase so it matches the microsoft_accounts key (which is " + "also normalized). Mutually exclusive with `accounts` (plural)."),
|
|
4427
|
+
tools: exports_external.array(MicrosoftToolTokenSchema).min(1).optional().describe("Per-account tool allowlist for the SINGULAR `account` form → " + "softeria `--enabled-tools <regex>`. Omitted = all tools. Only " + "valid with the singular `account`; using it together with the " + "plural `accounts` (which carries per-binding `tools`) is an error."),
|
|
4428
|
+
org_mode: exports_external.boolean().optional().describe("Per-agent org_mode override (RFC #1873 §6.4). When set, replaces " + "the top-level microsoft_workspace.org_mode for this agent. " + "Defaults to top-level value (which defaults to false)."),
|
|
4429
|
+
accounts: exports_external.array(MicrosoftAccountBindingSchema).min(1).optional().describe("Plural multi-account form: bind MULTIPLE Microsoft accounts to " + "this agent, each with its own tool scope. Mutually exclusive with " + "the singular `account`. Each account gets its own `ms-365-<slug>` " + "MCP server.")
|
|
4430
|
+
}).superRefine((v, ctx) => {
|
|
4431
|
+
const hasSingular = v.account !== undefined;
|
|
4432
|
+
const hasPlural = v.accounts !== undefined;
|
|
4433
|
+
if (hasSingular && hasPlural) {
|
|
4434
|
+
ctx.addIssue({
|
|
4435
|
+
code: exports_external.ZodIssueCode.custom,
|
|
4436
|
+
message: "microsoft_workspace: use EITHER `account` (singular) OR " + "`accounts` (plural array), not both",
|
|
4437
|
+
path: ["accounts"]
|
|
4438
|
+
});
|
|
4439
|
+
}
|
|
4440
|
+
if (hasPlural && v.tools !== undefined) {
|
|
4441
|
+
ctx.addIssue({
|
|
4442
|
+
code: exports_external.ZodIssueCode.custom,
|
|
4443
|
+
message: "microsoft_workspace: block-level `tools` applies to the singular " + "`account` only; with `accounts` put `tools` inside each binding",
|
|
4444
|
+
path: ["tools"]
|
|
4445
|
+
});
|
|
4446
|
+
}
|
|
4447
|
+
if (hasPlural) {
|
|
4448
|
+
const seen = new Set;
|
|
4449
|
+
for (const b of v.accounts) {
|
|
4450
|
+
if (seen.has(b.account)) {
|
|
4451
|
+
ctx.addIssue({
|
|
4452
|
+
code: exports_external.ZodIssueCode.custom,
|
|
4453
|
+
message: `microsoft_workspace: duplicate account '${b.account}' in accounts[]`,
|
|
4454
|
+
path: ["accounts"]
|
|
4455
|
+
});
|
|
4456
|
+
}
|
|
4457
|
+
seen.add(b.account);
|
|
4458
|
+
}
|
|
4459
|
+
}
|
|
4419
4460
|
}).optional();
|
|
4420
4461
|
AgentNotionWorkspaceConfigSchema = exports_external.object({
|
|
4421
4462
|
databases: exports_external.array(exports_external.string().regex(/^[a-z0-9][a-z0-9_-]{0,62}$/, {
|
|
@@ -4790,6 +4831,26 @@ var init_schema = __esm(() => {
|
|
|
4790
4831
|
for (const [name, a] of Object.entries(cfg.agents ?? {})) {
|
|
4791
4832
|
checkServes(a.serves, ["agents", name, "serves"]);
|
|
4792
4833
|
}
|
|
4834
|
+
const microsoftAccounts = cfg.microsoft_accounts;
|
|
4835
|
+
for (const [name, a] of Object.entries(cfg.agents ?? {})) {
|
|
4836
|
+
const mw = a.microsoft_workspace;
|
|
4837
|
+
const accounts = mw?.accounts;
|
|
4838
|
+
if (!accounts)
|
|
4839
|
+
continue;
|
|
4840
|
+
accounts.forEach((b, i) => {
|
|
4841
|
+
const acct = b?.account?.trim().toLowerCase();
|
|
4842
|
+
if (!acct)
|
|
4843
|
+
return;
|
|
4844
|
+
const enabledFor = microsoftAccounts?.[acct]?.enabled_for ?? [];
|
|
4845
|
+
if (!enabledFor.includes(name)) {
|
|
4846
|
+
ctx.addIssue({
|
|
4847
|
+
code: exports_external.ZodIssueCode.custom,
|
|
4848
|
+
message: `agent '${name}' binds Microsoft account '${acct}' but is not in ` + `microsoft_accounts['${acct}'].enabled_for[] — operator must run ` + `\`switchroom auth microsoft enable ${acct} ${name}\``,
|
|
4849
|
+
path: ["agents", name, "microsoft_workspace", "accounts", i, "account"]
|
|
4850
|
+
});
|
|
4851
|
+
}
|
|
4852
|
+
});
|
|
4853
|
+
}
|
|
4793
4854
|
});
|
|
4794
4855
|
});
|
|
4795
4856
|
|
|
@@ -5184,6 +5245,25 @@ var init_merge = __esm(() => {
|
|
|
5184
5245
|
})(mergeAgentConfig ||= {});
|
|
5185
5246
|
});
|
|
5186
5247
|
|
|
5248
|
+
// src/config/timezone.ts
|
|
5249
|
+
function isResolvableTimezone(zone) {
|
|
5250
|
+
try {
|
|
5251
|
+
new Intl.DateTimeFormat("en-US", { timeZone: zone });
|
|
5252
|
+
return true;
|
|
5253
|
+
} catch {
|
|
5254
|
+
return false;
|
|
5255
|
+
}
|
|
5256
|
+
}
|
|
5257
|
+
var CONTAINER_DEFAULT_UTC_ZONES;
|
|
5258
|
+
var init_timezone = __esm(() => {
|
|
5259
|
+
CONTAINER_DEFAULT_UTC_ZONES = new Set([
|
|
5260
|
+
"UTC",
|
|
5261
|
+
"Etc/UTC",
|
|
5262
|
+
"Etc/Universal",
|
|
5263
|
+
"Universal"
|
|
5264
|
+
]);
|
|
5265
|
+
});
|
|
5266
|
+
|
|
5187
5267
|
// node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/identity.js
|
|
5188
5268
|
var require_identity = __commonJS((exports) => {
|
|
5189
5269
|
var ALIAS = Symbol.for("yaml.alias");
|
|
@@ -12492,8 +12572,30 @@ function loadConfig(configPath) {
|
|
|
12492
12572
|
if (notionIssues.length > 0) {
|
|
12493
12573
|
throw new ConfigError(`Invalid notion_workspace configuration in ${filePath}`, notionIssues);
|
|
12494
12574
|
}
|
|
12575
|
+
validateAllTimezones(config, filePath);
|
|
12495
12576
|
return config;
|
|
12496
12577
|
}
|
|
12578
|
+
function validateAllTimezones(config, filePath) {
|
|
12579
|
+
const issues = [];
|
|
12580
|
+
const check = (zone, where) => {
|
|
12581
|
+
if (zone == null)
|
|
12582
|
+
return;
|
|
12583
|
+
if (!isResolvableTimezone(zone)) {
|
|
12584
|
+
issues.push(` ${where}: "${zone}" is not a resolvable IANA timezone ` + `(shape is valid but no such zone exists — check for a typo, ` + `e.g. "Australia/Melbourne", "America/New_York", "UTC").`);
|
|
12585
|
+
}
|
|
12586
|
+
};
|
|
12587
|
+
check(config.switchroom?.timezone, "switchroom.timezone");
|
|
12588
|
+
check(config.defaults?.timezone, "defaults.timezone");
|
|
12589
|
+
for (const [profileName, profile] of Object.entries(config.profiles ?? {})) {
|
|
12590
|
+
check(profile?.timezone, `profiles.${profileName}.timezone`);
|
|
12591
|
+
}
|
|
12592
|
+
for (const [agentName, agentRaw] of Object.entries(config.agents)) {
|
|
12593
|
+
check(agentRaw?.timezone, `agents.${agentName}.timezone`);
|
|
12594
|
+
}
|
|
12595
|
+
if (issues.length > 0) {
|
|
12596
|
+
throw new ConfigError(`Invalid timezone configuration in ${filePath}`, issues);
|
|
12597
|
+
}
|
|
12598
|
+
}
|
|
12497
12599
|
function validateAllCronTopicAliases(config, filePath) {
|
|
12498
12600
|
const issues = [];
|
|
12499
12601
|
for (const [agentName, agentRaw] of Object.entries(config.agents)) {
|
|
@@ -12536,6 +12638,7 @@ var init_loader = __esm(() => {
|
|
|
12536
12638
|
init_paths();
|
|
12537
12639
|
init_overlay_loader();
|
|
12538
12640
|
init_merge();
|
|
12641
|
+
init_timezone();
|
|
12539
12642
|
import_yaml2 = __toESM(require_dist(), 1);
|
|
12540
12643
|
ConfigError = class ConfigError extends Error {
|
|
12541
12644
|
details;
|
|
@@ -18937,14 +19040,7 @@ init_merge();
|
|
|
18937
19040
|
|
|
18938
19041
|
// src/agents/scaffold.ts
|
|
18939
19042
|
init_merge();
|
|
18940
|
-
|
|
18941
|
-
// src/config/timezone.ts
|
|
18942
|
-
var CONTAINER_DEFAULT_UTC_ZONES = new Set([
|
|
18943
|
-
"UTC",
|
|
18944
|
-
"Etc/UTC",
|
|
18945
|
-
"Etc/Universal",
|
|
18946
|
-
"Universal"
|
|
18947
|
-
]);
|
|
19043
|
+
init_timezone();
|
|
18948
19044
|
|
|
18949
19045
|
// src/cli/agent-config.ts
|
|
18950
19046
|
import { join } from "node:path";
|
|
@@ -18990,63 +19086,6 @@ for (const name of SHARED_FRAGMENTS) {
|
|
|
18990
19086
|
}
|
|
18991
19087
|
}
|
|
18992
19088
|
|
|
18993
|
-
// src/agents/pane-lock.ts
|
|
18994
|
-
var tails = new Map;
|
|
18995
|
-
|
|
18996
|
-
// src/agents/inject.ts
|
|
18997
|
-
var INJECT_COMMANDS = new Map([
|
|
18998
|
-
["/cost", { description: "Show session cost", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
18999
|
-
["/status", { description: "Show session status", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
19000
|
-
["/usage", { description: "Show plan quota", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
19001
|
-
["/hooks", { description: "List configured hooks", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
19002
|
-
["/memory", { description: "Open memory picker", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
19003
|
-
["/help", { description: "Show help / command discovery", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
19004
|
-
["/context", { description: "Show context window usage", expectsOutput: true, argsAllowed: false }],
|
|
19005
|
-
["/release-notes", { description: "Show release-notes version list", expectsOutput: true, dialog: true, argsAllowed: false }],
|
|
19006
|
-
["/model", { description: "Open model picker", expectsOutput: true, argsAllowed: true }],
|
|
19007
|
-
[
|
|
19008
|
-
"/clear",
|
|
19009
|
-
{
|
|
19010
|
-
description: "Clear session screen",
|
|
19011
|
-
expectsOutput: false,
|
|
19012
|
-
silentNote: "context cleared — fresh slate",
|
|
19013
|
-
argsAllowed: false
|
|
19014
|
-
}
|
|
19015
|
-
],
|
|
19016
|
-
[
|
|
19017
|
-
"/compact",
|
|
19018
|
-
{
|
|
19019
|
-
description: "Compact conversation history",
|
|
19020
|
-
expectsOutput: false,
|
|
19021
|
-
silentNote: "compaction runs silently",
|
|
19022
|
-
argsAllowed: false
|
|
19023
|
-
}
|
|
19024
|
-
]
|
|
19025
|
-
]);
|
|
19026
|
-
var INJECT_ALLOWLIST = new Set(INJECT_COMMANDS.keys());
|
|
19027
|
-
var INJECT_BLOCKED = new Map([
|
|
19028
|
-
["/login", { reason: "would mutate auth state" }],
|
|
19029
|
-
["/logout", { reason: "would terminate the agent's auth session" }],
|
|
19030
|
-
["/exit", { reason: "would kill the agent process" }],
|
|
19031
|
-
["/quit", { reason: "would kill the agent process" }],
|
|
19032
|
-
["/upgrade", { reason: "mutates the Claude Code installation" }],
|
|
19033
|
-
["/init", { reason: "generates/overwrites CLAUDE.md and runs a model turn" }],
|
|
19034
|
-
["/mcp", { reason: "opens an interactive MCP server management dialog" }],
|
|
19035
|
-
["/permissions", { reason: "opens an interactive permissions editor that mutates tool policy" }],
|
|
19036
|
-
["/install-github-app", { reason: "runs a network/OAuth install flow" }],
|
|
19037
|
-
["/add-dir", { reason: "mutates the session's working-directory set" }],
|
|
19038
|
-
["/terminal-setup", { reason: "mutates terminal keybinding configuration" }],
|
|
19039
|
-
["/privacy-settings", { reason: "opens an interactive privacy-settings dialog" }],
|
|
19040
|
-
["/bug", { reason: "submits a bug report over the network" }],
|
|
19041
|
-
[
|
|
19042
|
-
"/effort",
|
|
19043
|
-
{
|
|
19044
|
-
reason: "leaves a blocking confirmation modal open and wedges the pane; use the /effort command (it drives the modal), not raw inject"
|
|
19045
|
-
}
|
|
19046
|
-
]
|
|
19047
|
-
]);
|
|
19048
|
-
var INJECT_BLOCKLIST = new Set(INJECT_BLOCKED.keys());
|
|
19049
|
-
|
|
19050
19089
|
// src/setup/hindsight.ts
|
|
19051
19090
|
var HINDSIGHT_DEFAULT_API_PORT = 18888;
|
|
19052
19091
|
var HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
|
|
@@ -19126,10 +19165,17 @@ var DOCKER_HOOKS_PATH = `${DOCKER_TELEGRAM_PLUGIN_PATH}/hooks`;
|
|
|
19126
19165
|
|
|
19127
19166
|
// src/agents/compose.ts
|
|
19128
19167
|
init_merge();
|
|
19168
|
+
init_timezone();
|
|
19129
19169
|
|
|
19130
19170
|
// src/setup/host-capabilities.ts
|
|
19131
19171
|
init_paths();
|
|
19132
19172
|
|
|
19173
|
+
// src/vault/grants-db-path.ts
|
|
19174
|
+
var GRANTS_DB_DIRNAME = "vault-broker";
|
|
19175
|
+
var GRANTS_DB_FILENAME = "vault-grants.db";
|
|
19176
|
+
var GRANTS_DB_CONTAINER_DIR = `/root/.switchroom/${GRANTS_DB_DIRNAME}`;
|
|
19177
|
+
var GRANTS_DB_CONTAINER_PATH = `${GRANTS_DB_CONTAINER_DIR}/${GRANTS_DB_FILENAME}`;
|
|
19178
|
+
|
|
19133
19179
|
// src/agents/compose.ts
|
|
19134
19180
|
var BIND_MOUNT_EXACT_SOURCE_DENY = new Set(["/var/run/docker.sock"]);
|
|
19135
19181
|
|