switchroom 0.17.10 → 0.18.6
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/workspace-dynamic-hook.sh +12 -13
- package/dist/agent-scheduler/index.js +29 -2
- package/dist/auth-broker/index.js +6163 -152
- package/dist/cli/notion-write-pretool.mjs +31 -3
- package/dist/cli/switchroom.js +695 -526
- package/dist/host-control/main.js +6184 -173
- package/dist/vault/approvals/kernel-server.js +5893 -165
- package/dist/vault/broker/server.js +6666 -921
- package/package.json +1 -1
- package/profiles/_base/settings.json.hbs +2 -2
- package/profiles/_base/start.sh.hbs +170 -21
- package/profiles/coding/CLAUDE.md.hbs +1 -1
- package/profiles/default/CLAUDE.md.hbs +2 -2
- package/profiles/executive-assistant/CLAUDE.md.hbs +1 -1
- package/profiles/health-coach/CLAUDE.md.hbs +1 -1
- package/skills/switchroom-release/SKILL.md +78 -0
- package/telegram-plugin/auth-snapshot-format.ts +37 -25
- package/telegram-plugin/context-exhaustion.ts +124 -0
- package/telegram-plugin/dist/gateway/gateway.js +25025 -9203
- package/telegram-plugin/gateway/activity-card-store.ts +76 -0
- package/telegram-plugin/gateway/gateway.ts +740 -106
- package/telegram-plugin/gateway/inbound-delivery-gate.ts +26 -0
- package/telegram-plugin/gateway/model-command.ts +70 -10
- package/telegram-plugin/gateway/resolve-person.ts +304 -0
- package/telegram-plugin/gateway/unhandled-rejection-policy.ts +21 -1
- package/telegram-plugin/hooks/silent-end-scan.mjs +164 -40
- package/telegram-plugin/operator-events.ts +21 -0
- package/telegram-plugin/package.json +6 -0
- package/telegram-plugin/pending-work-progress.ts +42 -7
- package/telegram-plugin/quota-bar-format.ts +360 -0
- package/telegram-plugin/quota-watch.ts +4 -6
- package/telegram-plugin/registry/turns-schema.test.ts +97 -0
- package/telegram-plugin/registry/turns-schema.ts +78 -0
- package/telegram-plugin/render/ir.ts +209 -0
- package/telegram-plugin/render/parse.ts +363 -0
- package/telegram-plugin/render/render.ts +440 -0
- package/telegram-plugin/render/rich-render.ts +72 -0
- package/telegram-plugin/stream-controller.ts +14 -3
- package/telegram-plugin/subagent-watcher.ts +27 -9
- package/telegram-plugin/tests/activity-card-store.test.ts +94 -0
- package/telegram-plugin/tests/auth-command-format2.test.ts +1 -1
- package/telegram-plugin/tests/auth-snapshot-format.test.ts +51 -16
- package/telegram-plugin/tests/claude-code-event-contract.test.ts +48 -0
- package/telegram-plugin/tests/feed-heartbeat-liveness-open.test.ts +11 -0
- package/telegram-plugin/tests/feed-survival.test.ts +39 -0
- package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +3 -3
- package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +81 -0
- package/telegram-plugin/tests/inbound-emit-after-intercepts.test.ts +82 -0
- package/telegram-plugin/tests/liveness-tracker.test.ts +228 -0
- package/telegram-plugin/tests/model-command.test.ts +193 -16
- package/telegram-plugin/tests/narrative-render.test.ts +125 -0
- package/telegram-plugin/tests/operator-events.test.ts +16 -0
- package/telegram-plugin/tests/orphaned-reply-rearm.test.ts +123 -163
- package/telegram-plugin/tests/pending-work-progress.test.ts +116 -3
- package/telegram-plugin/tests/quota-bar-format.test.ts +444 -0
- package/telegram-plugin/tests/quota-watch.test.ts +1 -4
- package/telegram-plugin/tests/rapid-fire-delivery-ordering.test.ts +149 -0
- package/telegram-plugin/tests/render/parse-torture.test.ts +136 -0
- package/telegram-plugin/tests/render/parse.test.ts +393 -0
- package/telegram-plugin/tests/render/render.test.ts +436 -0
- package/telegram-plugin/tests/render/rich-render.test.ts +85 -0
- package/telegram-plugin/tests/resolve-person.test.ts +290 -0
- package/telegram-plugin/tests/silent-end-interrupt-stop-integration.test.ts +53 -0
- package/telegram-plugin/tests/silent-end-interrupt-stop-scan.test.ts +138 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +61 -0
- package/telegram-plugin/tests/telegram-activity-visibility-integration.test.ts +155 -1
- package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +19 -0
- package/telegram-plugin/tests/worker-activity-feed.test.ts +97 -0
- package/telegram-plugin/tests/worktree-watch-cwds.test.ts +98 -3
- package/telegram-plugin/turn-liveness-floor.ts +35 -1
- package/telegram-plugin/uat/scenarios/jtbd-rich-formatting-render-dm.test.ts +99 -7
- package/telegram-plugin/worker-activity-feed.ts +220 -15
- package/telegram-plugin/worktree-watch-cwds.ts +92 -17
- package/vendor/hindsight-memory/scripts/lib/client.py +11 -1
- package/vendor/hindsight-memory/scripts/lib/config.py +9 -2
- package/vendor/hindsight-memory/scripts/recall.py +64 -6
- package/vendor/hindsight-memory/scripts/tests/test_recall_integration.py +1 -0
- package/vendor/hindsight-memory/tests/test_client.py +43 -0
- package/vendor/hindsight-memory/tests/test_recall_precision.py +114 -0
- package/profiles/default/CLAUDE.md +0 -116
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- 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/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/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/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/dist/cli/switchroom.js
CHANGED
|
@@ -13542,7 +13542,7 @@ var init_zod = __esm(() => {
|
|
|
13542
13542
|
function isValidTimezone(value) {
|
|
13543
13543
|
return TIMEZONE_REGEX.test(value);
|
|
13544
13544
|
}
|
|
13545
|
-
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, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, ReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, DEFAULT_PROFILE = "default", AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
13545
|
+
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, DEFAULT_PROFILE = "default", AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
13546
13546
|
var init_schema = __esm(() => {
|
|
13547
13547
|
init_zod();
|
|
13548
13548
|
CodeRepoEntrySchema = exports_external.object({
|
|
@@ -13694,7 +13694,11 @@ var init_schema = __esm(() => {
|
|
|
13694
13694
|
sender_banks: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Per-speaker recall routing: a map of Telegram sender \u2192 extra " + "recall bank. When a message arrives, the agent also recalls the " + "speaker's bank (matched by Telegram username \u2014 a leading @ is " + "optional \u2014 or numeric user_id), merged " + "into its own results \u2014 so each trusted user gets their own " + "profile context. Additive recall scoping within the single " + "tenant: never an access boundary (who may drive an agent stays " + "the per-agent user assignment in `access.allowFrom`). Author the " + "banks via `switchroom memory profile`."),
|
|
13695
13695
|
skip_trivial: exports_external.boolean().optional().describe("Skip recall on plausibly-stateless trivial turns (time/date/" + "greeting). Switchroom default true \u2014 saves the recall arm + " + "injected tokens on turns that never need memory, guarded so it " + "never skips a turn that references user/project/session state. " + "Set false to always run recall."),
|
|
13696
13696
|
topic_filter_mode: exports_external.enum(["soft-preamble", "hard-filter"]).optional().describe("Supergroup-mode cross-topic memory behaviour. Default " + "(unset) \u2192 soft-preamble: recall returns memories from all " + "topics, and a 'Current topic: \u2026' preamble tells the model " + "to self-scope. hard-filter: drop any recalled memory whose " + "metadata.thread_id differs from the active inbound's topic. " + "Flip to hard-filter when the recall_log shows binding " + "failures (model surfacing the right memory but applying " + "it to the wrong topic).")
|
|
13697
|
-
}).optional().describe("Auto-recall tuning knobs")
|
|
13697
|
+
}).optional().describe("Auto-recall tuning knobs"),
|
|
13698
|
+
retain: exports_external.object({
|
|
13699
|
+
every_n_turns: exports_external.number().int().min(1).optional().describe("How often the Stop hook fires auto-retention, in turns. The " + "vendor plugin default is 10 (a short session can end before " + "retention ever fires); switchroom's scaffold default is 3. " + "Lower = more frequent, smaller retains + tighter crash " + "durability (at 1, every turn is retained before a restart can " + "lose it); higher = fewer, larger retains + less LLM churn. " + "Raised from the historical 1 to 3 because the local reasoning " + "consolidation model (Ollama gpt-oss-20b) ran away on the large " + "overlapping every-turn payloads. Set to 1 for the old " + "every-turn crash-durability guarantee. Min 1. Cascade: " + "per-field merge (agent wins over default)."),
|
|
13700
|
+
overlap_turns: exports_external.number().int().min(0).optional().describe("Extra recent turns included in each chunked retain window on " + "top of `every_n_turns`, so window = overlap_turns + " + "every_n_turns recent turns. Vendor default is 2; switchroom's " + "scaffold default is 1 (smaller payloads for the local " + "reasoning consolidation model). Higher = more redundant " + "context re-sent per fire. Min 0. Cascade: per-field merge.")
|
|
13701
|
+
}).optional().describe("Auto-retain (Stop-hook consolidation) cadence knobs")
|
|
13698
13702
|
}).optional();
|
|
13699
13703
|
HookEntrySchema = exports_external.object({
|
|
13700
13704
|
command: exports_external.string().describe("Shell command to run. Supports ${CLAUDE_CONFIG_DIR} and ${CLAUDE_PLUGIN_ROOT} substitution."),
|
|
@@ -13872,6 +13876,21 @@ var init_schema = __esm(() => {
|
|
|
13872
13876
|
small_fast_model: exports_external.string().optional().describe("Model id exported as ANTHROPIC_SMALL_FAST_MODEL for the claude CLI's " + "background/fast lane, e.g. 'claude-haiku-4-5-20251001'."),
|
|
13873
13877
|
tags: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Extra key/value metadata tags attached to the provisioned LiteLLM " + "virtual key. Merged per-key across cascade layers (agent wins).")
|
|
13874
13878
|
}).optional().describe("LiteLLM routing config \u2014 opt-in per-agent virtual-key auto-provisioning " + "+ routing env. Default OFF. See LiteLLMConfigSchema doc for the full flow.");
|
|
13879
|
+
HindsightPerOpLlmSchema = exports_external.object({
|
|
13880
|
+
model: exports_external.string().min(1).optional().describe("Per-op model (upstream `HINDSIGHT_API_<OP>_LLM_MODEL`). Absent \u2192 " + "inherit the global `hindsight.llm.model`."),
|
|
13881
|
+
provider: exports_external.string().min(1).optional().describe("Per-op provider (upstream `HINDSIGHT_API_<OP>_LLM_PROVIDER`). " + "Absent \u2192 inherit the global `hindsight.llm.provider`."),
|
|
13882
|
+
base_url: exports_external.string().min(1).optional().describe("Per-op base URL (upstream `HINDSIGHT_API_<OP>_LLM_BASE_URL`). " + "Optional passthrough; absent \u2192 inherit the global."),
|
|
13883
|
+
api_key: exports_external.string().min(1).optional().describe("Per-op API key (upstream `HINDSIGHT_API_<OP>_LLM_API_KEY`). Literal " + "or `vault:` reference. Optional passthrough; absent \u2192 inherit global.")
|
|
13884
|
+
}).describe("Per-operation LLM override. Every field optional; an unset field (or " + "an omitted op block) inherits the global `hindsight.llm.*`, which is " + "already the engine's fallback \u2014 switchroom emits only the vars set.");
|
|
13885
|
+
HindsightConfigSchema = exports_external.object({
|
|
13886
|
+
llm: exports_external.object({
|
|
13887
|
+
provider: exports_external.string().min(1).optional().describe("Hindsight LLM provider (upstream `HINDSIGHT_API_LLM_PROVIDER`). " + "Defaults to `claude-code` (subscription-honest, broker-fed OAuth). " + "Any litellm-routable provider the upstream image supports is valid. " + "Serves as the GLOBAL default for every op absent a per-op override."),
|
|
13888
|
+
model: exports_external.string().min(1).optional().describe("Hindsight LLM model (upstream `HINDSIGHT_API_LLM_MODEL`). Defaults " + "to HINDSIGHT_DEFAULT_MODEL. Any model your LiteLLM proxy can route " + "is valid, e.g. `openrouter/z-ai/glm-5.2` when routing through the " + "fleet proxy. With provider=claude-code this value is ALSO exported " + "as `ANTHROPIC_MODEL` to the claude subprocess. Serves as the GLOBAL " + "default for every op absent a per-op override."),
|
|
13889
|
+
retain: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `retain` LLM op (memory ingestion). Emits " + "`HINDSIGHT_API_RETAIN_LLM_*`. Absent \u2192 uses the global model/provider."),
|
|
13890
|
+
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent \u2192 uses global."),
|
|
13891
|
+
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent \u2192 global.")
|
|
13892
|
+
}).optional().describe("LLM knob for the hindsight container. The flat `provider`/`model` set " + "the global default (backward-compatible); optional `retain`/`reflect`/" + "`consolidation` blocks override individual ops. All fields optional; " + "unset fields fall back to the hard-coded defaults.")
|
|
13893
|
+
});
|
|
13875
13894
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
13876
13895
|
microsoft_client_id: exports_external.string().min(1).optional().describe("Microsoft OAuth application (client) ID from Entra portal " + "(literal string or vault reference e.g. " + "'vault:microsoft-oauth-client-id'). OPTIONAL \u2014 omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
|
|
13877
13896
|
microsoft_client_secret: exports_external.string().min(1).optional().describe("Microsoft OAuth client secret. Optional \u2014 public-client apps " + "(Mobile + Desktop platform with 'Allow public client flows' " + "enabled) work without a secret; confidential clients pass " + "one. Either literal or vault reference e.g. " + "'vault:microsoft-oauth-client-secret'."),
|
|
@@ -14120,6 +14139,7 @@ var init_schema = __esm(() => {
|
|
|
14120
14139
|
config: exports_external.object({
|
|
14121
14140
|
provider: exports_external.string().default("ollama").describe("Embedding provider (ollama, openai, anthropic)"),
|
|
14122
14141
|
model: exports_external.string().optional().describe("Embedding model (e.g., 'nomic-embed-text')"),
|
|
14142
|
+
llm_model: exports_external.string().optional().describe("LiteLLM model name for Hindsight's LLM ops (retain/reflect/" + "consolidation) when the top-level `litellm.enabled` carve-out is " + "on. Defaults to a cheap OpenRouter model (routed via LiteLLM's " + "model-mapped path, not the Anthropic OAuth pass-through) to keep " + "background memory-op cost off the Claude subscription quota. Set " + "to a `claude-*` model name to route it back through the OAuth " + "pass-through instead. Has no effect when litellm is disabled."),
|
|
14123
14143
|
api_key: exports_external.string().optional().describe("API key or vault reference for embedding provider"),
|
|
14124
14144
|
docker_service: exports_external.boolean().default(true).describe("Whether to include Hindsight in docker-compose"),
|
|
14125
14145
|
url: exports_external.string().url("Hindsight URL must be a valid URL (no shell-special characters)").optional().describe("Hindsight MCP endpoint URL (e.g., http://localhost:18888/mcp/). Defaults to http://localhost:8888/mcp/.")
|
|
@@ -14185,7 +14205,8 @@ var init_schema = __esm(() => {
|
|
|
14185
14205
|
UserSchema = exports_external.object({
|
|
14186
14206
|
name: exports_external.string().optional().describe("Display name for the user."),
|
|
14187
14207
|
telegram_ids: exports_external.array(exports_external.string()).min(1).describe("Telegram username(s) and/or numeric user id(s) identifying this user " + "(a leading @ is optional). Matched against the message sender for " + "per-speaker memory routing."),
|
|
14188
|
-
profile_bank: exports_external.string().describe("Hindsight bank holding this user's memory profile (author via " + "`switchroom memory profile add <bank> ...`).")
|
|
14208
|
+
profile_bank: exports_external.string().describe("Hindsight bank holding this user's memory profile (author via " + "`switchroom memory profile add <bank> ...`)."),
|
|
14209
|
+
person_id: exports_external.string().optional().describe("Free-text display name projected into the inbound `<channel>` " + 'tag\'s `user` attribute (e.g. "Lisa") so an agent can greet by ' + "name instead of only seeing the raw Telegram id/username. NOT a " + "stable identity system \u2014 just a label. Resolution is boot-time-only " + "(no hot-reload; a config change needs an agent restart) and " + "chat-scoped (a resolved name is only shown in a chat/group the " + "person is actually a member of, per that chat's access.json " + "membership \u2014 never broadcast into every chat the agent operates " + "in). Keep this broadly safe to show, the same discipline as " + "picking a `profile_bank` name: there is no automated enforcement " + "that a `person_id` stays safe if a group's membership changes " + "later \u2014 see docs/configuration.md.")
|
|
14189
14210
|
});
|
|
14190
14211
|
SwitchroomConfigSchema = exports_external.object({
|
|
14191
14212
|
switchroom: exports_external.object({
|
|
@@ -14197,6 +14218,7 @@ var init_schema = __esm(() => {
|
|
|
14197
14218
|
telegram: TelegramConfigSchema,
|
|
14198
14219
|
release: ReleaseBlock.optional().describe("Fleet-wide default release-channel pin / pointer for the update " + "flow. Either `channel` (dev|rc|latest) or `pin` (sha-<hex>|v<semver>) " + "\u2014 mutually exclusive. Per-agent `release` REPLACES this entirely."),
|
|
14199
14220
|
memory: MemoryBackendConfigSchema.optional(),
|
|
14221
|
+
hindsight: HindsightConfigSchema.optional().describe("Fleet-singleton Hindsight (memory backend) configuration. Currently " + "just the LLM knob (provider + model) used for retain/reflect/" + "consolidation. Both fields optional; when unset the container falls " + "back to provider=claude-code + the hard-coded HINDSIGHT_DEFAULT_MODEL " + "so nothing changes for operators who don't set it. Read at container " + "launch by startHindsight() (src/setup/hindsight.ts) \u2014 takes effect on " + "the next `switchroom apply` / `memory setup --recreate`."),
|
|
14200
14222
|
vault: VaultConfigSchema.optional(),
|
|
14201
14223
|
auth: exports_external.object({
|
|
14202
14224
|
active: exports_external.string().min(1).optional().describe("Fleet-wide active Anthropic account label. Every agent without " + "an explicit `agent.auth.override` uses this account. See " + "docs/auth.md for the full model. Set by `switchroom auth use <label>`."),
|
|
@@ -14645,6 +14667,11 @@ function mergeAgentConfig(defaultsIn, agentIn) {
|
|
|
14645
14667
|
continue;
|
|
14646
14668
|
if (k === "recall" && base.recall && typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
14647
14669
|
combined[k] = { ...base.recall, ...v };
|
|
14670
|
+
} else if (k === "retain" && base.retain && typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
14671
|
+
combined[k] = {
|
|
14672
|
+
...base.retain,
|
|
14673
|
+
...v
|
|
14674
|
+
};
|
|
14648
14675
|
} else if (k === "disposition" && base.disposition && typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
14649
14676
|
combined[k] = {
|
|
14650
14677
|
...base.disposition,
|
|
@@ -15098,7 +15125,7 @@ function resolveAgentsDir(config) {
|
|
|
15098
15125
|
if (override && override.length > 0 && override.startsWith("/")) {
|
|
15099
15126
|
return override;
|
|
15100
15127
|
}
|
|
15101
|
-
return resolveDualPath(config.switchroom.agents_dir);
|
|
15128
|
+
return resolveDualPath(config.switchroom.agents_dir || "~/.switchroom/agents");
|
|
15102
15129
|
}
|
|
15103
15130
|
function resolvePath(pathStr) {
|
|
15104
15131
|
return resolveDualPath(pathStr);
|
|
@@ -15151,10 +15178,388 @@ function resolveUsers(config, agentName) {
|
|
|
15151
15178
|
]);
|
|
15152
15179
|
return { senderBanks, additionalBanks };
|
|
15153
15180
|
}
|
|
15181
|
+
function resolvePersonEntries(config) {
|
|
15182
|
+
const users = config.users ?? {};
|
|
15183
|
+
const entries = [];
|
|
15184
|
+
for (const [key, u] of Object.entries(users)) {
|
|
15185
|
+
if (!u.person_id)
|
|
15186
|
+
continue;
|
|
15187
|
+
entries.push({ key, person_id: u.person_id, telegram_ids: u.telegram_ids });
|
|
15188
|
+
}
|
|
15189
|
+
return entries;
|
|
15190
|
+
}
|
|
15154
15191
|
var init_users = __esm(() => {
|
|
15155
15192
|
init_merge();
|
|
15156
15193
|
});
|
|
15157
15194
|
|
|
15195
|
+
// src/agents/pane-lock.ts
|
|
15196
|
+
function withPaneLock(key, fn) {
|
|
15197
|
+
const tail = tails.get(key) ?? Promise.resolve();
|
|
15198
|
+
const run = tail.then(() => fn());
|
|
15199
|
+
const next = run.then(() => {}, () => {});
|
|
15200
|
+
tails.set(key, next);
|
|
15201
|
+
next.then(() => {
|
|
15202
|
+
if (tails.get(key) === next)
|
|
15203
|
+
tails.delete(key);
|
|
15204
|
+
});
|
|
15205
|
+
return run;
|
|
15206
|
+
}
|
|
15207
|
+
var tails;
|
|
15208
|
+
var init_pane_lock = __esm(() => {
|
|
15209
|
+
tails = new Map;
|
|
15210
|
+
});
|
|
15211
|
+
|
|
15212
|
+
// src/agents/inject.ts
|
|
15213
|
+
var exports_inject = {};
|
|
15214
|
+
__export(exports_inject, {
|
|
15215
|
+
validateInjectCommand: () => validateInjectCommand,
|
|
15216
|
+
normalizeInjectCommand: () => normalizeInjectCommand,
|
|
15217
|
+
makeTmuxRunner: () => makeTmuxRunner,
|
|
15218
|
+
isTuiChromeLine: () => isTuiChromeLine,
|
|
15219
|
+
injectSlashCommandWith: () => injectSlashCommandWith,
|
|
15220
|
+
injectSlashCommand: () => injectSlashCommand,
|
|
15221
|
+
diffPane: () => diffPane,
|
|
15222
|
+
dedupeInjectQueue: () => dedupeInjectQueue,
|
|
15223
|
+
InjectError: () => InjectError,
|
|
15224
|
+
INJECT_COMMANDS: () => INJECT_COMMANDS,
|
|
15225
|
+
INJECT_BLOCKLIST: () => INJECT_BLOCKLIST,
|
|
15226
|
+
INJECT_BLOCKED: () => INJECT_BLOCKED,
|
|
15227
|
+
INJECT_ALLOWLIST: () => INJECT_ALLOWLIST
|
|
15228
|
+
});
|
|
15229
|
+
import { execFileSync } from "node:child_process";
|
|
15230
|
+
function validateInjectCommand(command) {
|
|
15231
|
+
if (typeof command !== "string" || command.trim().length === 0) {
|
|
15232
|
+
throw new InjectError("invalid", "command is empty");
|
|
15233
|
+
}
|
|
15234
|
+
const trimmed = command.trim();
|
|
15235
|
+
if (!trimmed.startsWith("/")) {
|
|
15236
|
+
throw new InjectError("invalid", `command must start with "/": got ${trimmed}`);
|
|
15237
|
+
}
|
|
15238
|
+
const bare = trimmed.split(/\s+/, 1)[0].toLowerCase();
|
|
15239
|
+
const blockedMeta = INJECT_BLOCKED.get(bare);
|
|
15240
|
+
if (blockedMeta) {
|
|
15241
|
+
throw new InjectError("blocked", `${bare} is explicitly blocked from inject (${blockedMeta.reason}).`);
|
|
15242
|
+
}
|
|
15243
|
+
if (!INJECT_COMMANDS.has(bare)) {
|
|
15244
|
+
const allowed = [...INJECT_COMMANDS.keys()].sort().join(", ");
|
|
15245
|
+
throw new InjectError("not_allowed", `${bare} is not in the inject allowlist. Allowed: ${allowed}`);
|
|
15246
|
+
}
|
|
15247
|
+
return bare;
|
|
15248
|
+
}
|
|
15249
|
+
function normalizeInjectCommand(command) {
|
|
15250
|
+
return command.trim().replace(/\s+/g, " ").toLowerCase();
|
|
15251
|
+
}
|
|
15252
|
+
function dedupeInjectQueue(commands) {
|
|
15253
|
+
const seen = new Set;
|
|
15254
|
+
const out = [];
|
|
15255
|
+
for (const raw of commands) {
|
|
15256
|
+
if (typeof raw !== "string")
|
|
15257
|
+
continue;
|
|
15258
|
+
const key = normalizeInjectCommand(raw);
|
|
15259
|
+
if (key.length === 0)
|
|
15260
|
+
continue;
|
|
15261
|
+
if (seen.has(key))
|
|
15262
|
+
continue;
|
|
15263
|
+
seen.add(key);
|
|
15264
|
+
out.push(raw.trim());
|
|
15265
|
+
}
|
|
15266
|
+
return out;
|
|
15267
|
+
}
|
|
15268
|
+
function defaultSocketName(agentName) {
|
|
15269
|
+
return `switchroom-${agentName}`;
|
|
15270
|
+
}
|
|
15271
|
+
function makeTmuxRunner(tmuxBin) {
|
|
15272
|
+
return {
|
|
15273
|
+
capture(socket, session) {
|
|
15274
|
+
try {
|
|
15275
|
+
return execFileSync(tmuxBin, ["-L", socket, "capture-pane", "-p", "-t", session, "-S", "-200"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
15276
|
+
} catch {
|
|
15277
|
+
return null;
|
|
15278
|
+
}
|
|
15279
|
+
},
|
|
15280
|
+
send(socket, session, args) {
|
|
15281
|
+
const [subcmd, ...rest] = args;
|
|
15282
|
+
const flagEnd = rest.findIndex((a) => !a.startsWith("-"));
|
|
15283
|
+
const flagsBeforeKeys = flagEnd === -1 ? rest : rest.slice(0, flagEnd);
|
|
15284
|
+
const keys = flagEnd === -1 ? [] : rest.slice(flagEnd);
|
|
15285
|
+
execFileSync(tmuxBin, ["-L", socket, subcmd, ...flagsBeforeKeys, "-t", session, ...keys], { stdio: ["pipe", "pipe", "pipe"] });
|
|
15286
|
+
},
|
|
15287
|
+
hasSession(socket, session) {
|
|
15288
|
+
try {
|
|
15289
|
+
execFileSync(tmuxBin, ["-L", socket, "has-session", "-t", session], {
|
|
15290
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
15291
|
+
});
|
|
15292
|
+
return true;
|
|
15293
|
+
} catch {
|
|
15294
|
+
return false;
|
|
15295
|
+
}
|
|
15296
|
+
}
|
|
15297
|
+
};
|
|
15298
|
+
}
|
|
15299
|
+
function sleep(ms) {
|
|
15300
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
15301
|
+
}
|
|
15302
|
+
function isTuiChromeLine(line) {
|
|
15303
|
+
const t = line.trim();
|
|
15304
|
+
if (t.length === 0)
|
|
15305
|
+
return false;
|
|
15306
|
+
if (/^[\s\-_\u2502\u2500\u256d\u256e\u2570\u256f\u250c\u2510\u2514\u2518|]+$/.test(t))
|
|
15307
|
+
return true;
|
|
15308
|
+
if (/^[\u256d\u256e\u2570\u256f\u2502\s]*[>)\u276f][\u256d\u256e\u2570\u256f\u2502\s]*$/.test(t))
|
|
15309
|
+
return true;
|
|
15310
|
+
if (/accept edits on/i.test(t))
|
|
15311
|
+
return true;
|
|
15312
|
+
if (/shift\+tab to cycle/i.test(t))
|
|
15313
|
+
return true;
|
|
15314
|
+
if (/for agents/i.test(t))
|
|
15315
|
+
return true;
|
|
15316
|
+
if (/\? for shortcuts/i.test(t))
|
|
15317
|
+
return true;
|
|
15318
|
+
if (/bypassing permissions/i.test(t))
|
|
15319
|
+
return true;
|
|
15320
|
+
if (t === "copy")
|
|
15321
|
+
return true;
|
|
15322
|
+
return false;
|
|
15323
|
+
}
|
|
15324
|
+
function diffPane(before, after, command) {
|
|
15325
|
+
if (command) {
|
|
15326
|
+
const escaped = command.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
15327
|
+
const lines = after.split(`
|
|
15328
|
+
`);
|
|
15329
|
+
let anchorIdx = -1;
|
|
15330
|
+
for (let i = lines.length - 1;i >= 0; i--) {
|
|
15331
|
+
if (/[\u276f>]\s+/.test(lines[i]) && lines[i].includes(escaped)) {
|
|
15332
|
+
anchorIdx = i;
|
|
15333
|
+
break;
|
|
15334
|
+
}
|
|
15335
|
+
}
|
|
15336
|
+
if (anchorIdx >= 0) {
|
|
15337
|
+
const tail = lines.slice(anchorIdx + 1);
|
|
15338
|
+
const trimmed = [];
|
|
15339
|
+
for (const raw of tail) {
|
|
15340
|
+
const line = raw.trimEnd();
|
|
15341
|
+
if (line.length === 0 && trimmed.length === 0)
|
|
15342
|
+
continue;
|
|
15343
|
+
if (isTuiChromeLine(line))
|
|
15344
|
+
continue;
|
|
15345
|
+
trimmed.push(line);
|
|
15346
|
+
}
|
|
15347
|
+
while (trimmed.length > 0 && trimmed[trimmed.length - 1].length === 0) {
|
|
15348
|
+
trimmed.pop();
|
|
15349
|
+
}
|
|
15350
|
+
const affordances = /^(esc to cancel|press any key|\u21b5 select)/i;
|
|
15351
|
+
while (trimmed.length > 0 && affordances.test(trimmed[trimmed.length - 1].trim())) {
|
|
15352
|
+
trimmed.pop();
|
|
15353
|
+
}
|
|
15354
|
+
if (trimmed.length > 0) {
|
|
15355
|
+
return { output: trimmed.join(`
|
|
15356
|
+
`), anchored: true };
|
|
15357
|
+
}
|
|
15358
|
+
}
|
|
15359
|
+
}
|
|
15360
|
+
const beforeSet = new Set(before.split(`
|
|
15361
|
+
`).map((l) => l.trimEnd()));
|
|
15362
|
+
const newLines = [];
|
|
15363
|
+
for (const raw of after.split(`
|
|
15364
|
+
`)) {
|
|
15365
|
+
const line = raw.trimEnd();
|
|
15366
|
+
if (line.length === 0)
|
|
15367
|
+
continue;
|
|
15368
|
+
if (beforeSet.has(line))
|
|
15369
|
+
continue;
|
|
15370
|
+
if (isTuiChromeLine(line))
|
|
15371
|
+
continue;
|
|
15372
|
+
newLines.push(line);
|
|
15373
|
+
}
|
|
15374
|
+
return { output: newLines.join(`
|
|
15375
|
+
`), anchored: false };
|
|
15376
|
+
}
|
|
15377
|
+
async function injectSlashCommand(agentName, command, opts = {}) {
|
|
15378
|
+
validateInjectCommand(command);
|
|
15379
|
+
const tmuxBin = opts.tmuxBin ?? "tmux";
|
|
15380
|
+
const socket = opts.socketName ?? defaultSocketName(agentName);
|
|
15381
|
+
const session = opts.sessionName ?? agentName;
|
|
15382
|
+
const settleMs = opts.settleMs ?? 2000;
|
|
15383
|
+
const timeoutMs = opts.timeoutMs ?? 5000;
|
|
15384
|
+
return withPaneLock(`${socket}:${session}`, () => injectSlashCommandWith(makeTmuxRunner(tmuxBin), {
|
|
15385
|
+
socket,
|
|
15386
|
+
session,
|
|
15387
|
+
command: command.trim(),
|
|
15388
|
+
settleMs,
|
|
15389
|
+
timeoutMs
|
|
15390
|
+
}));
|
|
15391
|
+
}
|
|
15392
|
+
async function injectSlashCommandWith(runner, args) {
|
|
15393
|
+
const { socket, session, command, settleMs, timeoutMs } = args;
|
|
15394
|
+
let bareVerb;
|
|
15395
|
+
try {
|
|
15396
|
+
bareVerb = validateInjectCommand(command);
|
|
15397
|
+
} catch (err) {
|
|
15398
|
+
if (err instanceof InjectError) {
|
|
15399
|
+
return {
|
|
15400
|
+
outcome: "failed",
|
|
15401
|
+
output: "",
|
|
15402
|
+
truncated: false,
|
|
15403
|
+
command: command.trim().split(/\s+/, 1)[0]?.toLowerCase() ?? "",
|
|
15404
|
+
meta: null,
|
|
15405
|
+
errorCode: err.code,
|
|
15406
|
+
errorMessage: err.message
|
|
15407
|
+
};
|
|
15408
|
+
}
|
|
15409
|
+
throw err;
|
|
15410
|
+
}
|
|
15411
|
+
const meta = INJECT_COMMANDS.get(bareVerb) ?? null;
|
|
15412
|
+
if (!runner.hasSession(socket, session)) {
|
|
15413
|
+
return {
|
|
15414
|
+
outcome: "failed",
|
|
15415
|
+
output: "",
|
|
15416
|
+
truncated: false,
|
|
15417
|
+
command: bareVerb,
|
|
15418
|
+
meta,
|
|
15419
|
+
errorCode: "session_missing",
|
|
15420
|
+
errorMessage: `tmux session "${session}" on socket "${socket}" not found. ` + `Is the agent running under the tmux supervisor (the default)? ` + `If experimental.legacy_pty=true is set, inject is unsupported.`
|
|
15421
|
+
};
|
|
15422
|
+
}
|
|
15423
|
+
const before = runner.capture(socket, session) ?? "";
|
|
15424
|
+
try {
|
|
15425
|
+
runner.send(socket, session, ["send-keys", "-l", command]);
|
|
15426
|
+
runner.send(socket, session, ["send-keys", "Enter"]);
|
|
15427
|
+
} catch (err) {
|
|
15428
|
+
return {
|
|
15429
|
+
outcome: "failed",
|
|
15430
|
+
output: "",
|
|
15431
|
+
truncated: false,
|
|
15432
|
+
command: bareVerb,
|
|
15433
|
+
meta,
|
|
15434
|
+
errorCode: "tmux_failed",
|
|
15435
|
+
errorMessage: `tmux send-keys failed: ${err instanceof Error ? err.message : String(err)}`
|
|
15436
|
+
};
|
|
15437
|
+
}
|
|
15438
|
+
const start = Date.now();
|
|
15439
|
+
let last = before;
|
|
15440
|
+
let stableSince = null;
|
|
15441
|
+
while (Date.now() - start < timeoutMs) {
|
|
15442
|
+
await sleep(POLL_INTERVAL_MS);
|
|
15443
|
+
const cur = runner.capture(socket, session) ?? "";
|
|
15444
|
+
if (cur === last && cur !== before) {
|
|
15445
|
+
if (stableSince === null) {
|
|
15446
|
+
stableSince = Date.now();
|
|
15447
|
+
} else if (Date.now() - stableSince >= POLL_INTERVAL_MS) {
|
|
15448
|
+
last = cur;
|
|
15449
|
+
break;
|
|
15450
|
+
}
|
|
15451
|
+
} else {
|
|
15452
|
+
stableSince = null;
|
|
15453
|
+
}
|
|
15454
|
+
last = cur;
|
|
15455
|
+
if (Date.now() - start >= settleMs && cur !== before) {
|
|
15456
|
+
break;
|
|
15457
|
+
}
|
|
15458
|
+
}
|
|
15459
|
+
const { output: rawOutput, anchored } = diffPane(before, last, command);
|
|
15460
|
+
let output = rawOutput;
|
|
15461
|
+
let truncated = false;
|
|
15462
|
+
const bytes = Buffer.byteLength(output, "utf-8");
|
|
15463
|
+
if (bytes > OUTPUT_BYTE_CAP) {
|
|
15464
|
+
while (Buffer.byteLength(output, "utf-8") > OUTPUT_BYTE_CAP) {
|
|
15465
|
+
output = output.slice(Math.floor(output.length * 0.1) + 1);
|
|
15466
|
+
}
|
|
15467
|
+
truncated = true;
|
|
15468
|
+
}
|
|
15469
|
+
if (meta?.dialog) {
|
|
15470
|
+
try {
|
|
15471
|
+
runner.send(socket, session, ["send-keys", "Escape"]);
|
|
15472
|
+
} catch {}
|
|
15473
|
+
}
|
|
15474
|
+
if (output.trim().length === 0) {
|
|
15475
|
+
return {
|
|
15476
|
+
outcome: "ok_no_output",
|
|
15477
|
+
output: "",
|
|
15478
|
+
truncated: false,
|
|
15479
|
+
command: bareVerb,
|
|
15480
|
+
meta,
|
|
15481
|
+
...anchored ? {} : { diagnostic: "anchor_missing" }
|
|
15482
|
+
};
|
|
15483
|
+
}
|
|
15484
|
+
return {
|
|
15485
|
+
outcome: "ok",
|
|
15486
|
+
output,
|
|
15487
|
+
truncated,
|
|
15488
|
+
command: bareVerb,
|
|
15489
|
+
meta,
|
|
15490
|
+
...truncated ? { diagnostic: "truncated_output" } : {}
|
|
15491
|
+
};
|
|
15492
|
+
}
|
|
15493
|
+
var INJECT_COMMANDS, INJECT_ALLOWLIST, INJECT_BLOCKED, INJECT_BLOCKLIST, InjectError, POLL_INTERVAL_MS = 150, OUTPUT_BYTE_CAP = 3000;
|
|
15494
|
+
var init_inject = __esm(() => {
|
|
15495
|
+
init_pane_lock();
|
|
15496
|
+
INJECT_COMMANDS = new Map([
|
|
15497
|
+
["/cost", { description: "Show session cost", expectsOutput: true, dialog: true }],
|
|
15498
|
+
["/status", { description: "Show session status", expectsOutput: true, dialog: true }],
|
|
15499
|
+
["/usage", { description: "Show plan quota", expectsOutput: true, dialog: true }],
|
|
15500
|
+
["/hooks", { description: "List configured hooks", expectsOutput: true, dialog: true }],
|
|
15501
|
+
["/memory", { description: "Open memory picker", expectsOutput: true, dialog: true }],
|
|
15502
|
+
["/model", { description: "Open model picker", expectsOutput: true }],
|
|
15503
|
+
[
|
|
15504
|
+
"/clear",
|
|
15505
|
+
{
|
|
15506
|
+
description: "Clear session screen",
|
|
15507
|
+
expectsOutput: false,
|
|
15508
|
+
silentNote: "context cleared \u2014 fresh slate"
|
|
15509
|
+
}
|
|
15510
|
+
],
|
|
15511
|
+
[
|
|
15512
|
+
"/compact",
|
|
15513
|
+
{
|
|
15514
|
+
description: "Compact conversation history",
|
|
15515
|
+
expectsOutput: false,
|
|
15516
|
+
silentNote: "compaction runs silently"
|
|
15517
|
+
}
|
|
15518
|
+
]
|
|
15519
|
+
]);
|
|
15520
|
+
INJECT_ALLOWLIST = new Set(INJECT_COMMANDS.keys());
|
|
15521
|
+
INJECT_BLOCKED = new Map([
|
|
15522
|
+
["/login", { reason: "would mutate auth state" }],
|
|
15523
|
+
["/logout", { reason: "would terminate the agent's auth session" }],
|
|
15524
|
+
["/exit", { reason: "would kill the agent process" }],
|
|
15525
|
+
["/quit", { reason: "would kill the agent process" }],
|
|
15526
|
+
[
|
|
15527
|
+
"/effort",
|
|
15528
|
+
{
|
|
15529
|
+
reason: "leaves a blocking confirmation modal open and wedges the pane; use the /effort command (it drives the modal), not raw inject"
|
|
15530
|
+
}
|
|
15531
|
+
]
|
|
15532
|
+
]);
|
|
15533
|
+
INJECT_BLOCKLIST = new Set(INJECT_BLOCKED.keys());
|
|
15534
|
+
InjectError = class InjectError extends Error {
|
|
15535
|
+
code;
|
|
15536
|
+
constructor(code, message) {
|
|
15537
|
+
super(message);
|
|
15538
|
+
this.name = "InjectError";
|
|
15539
|
+
this.code = code;
|
|
15540
|
+
}
|
|
15541
|
+
};
|
|
15542
|
+
});
|
|
15543
|
+
|
|
15544
|
+
// src/agents/model-picker.ts
|
|
15545
|
+
var init_model_picker = __esm(() => {
|
|
15546
|
+
init_inject();
|
|
15547
|
+
init_pane_lock();
|
|
15548
|
+
});
|
|
15549
|
+
|
|
15550
|
+
// telegram-plugin/gateway/model-command.ts
|
|
15551
|
+
function isClaudeModel(name) {
|
|
15552
|
+
const lower = name.toLowerCase();
|
|
15553
|
+
if (MODEL_ALIASES.includes(lower))
|
|
15554
|
+
return true;
|
|
15555
|
+
return lower.startsWith("claude-");
|
|
15556
|
+
}
|
|
15557
|
+
var MODEL_ALIASES;
|
|
15558
|
+
var init_model_command = __esm(() => {
|
|
15559
|
+
init_model_picker();
|
|
15560
|
+
MODEL_ALIASES = ["opus", "sonnet", "haiku", "fable", "default"];
|
|
15561
|
+
});
|
|
15562
|
+
|
|
15158
15563
|
// src/util/atomic.ts
|
|
15159
15564
|
var exports_atomic = {};
|
|
15160
15565
|
__export(exports_atomic, {
|
|
@@ -15223,7 +15628,7 @@ var init_atomic = __esm(() => {
|
|
|
15223
15628
|
});
|
|
15224
15629
|
|
|
15225
15630
|
// src/setup/hindsight.ts
|
|
15226
|
-
import { execFileSync } from "node:child_process";
|
|
15631
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
15227
15632
|
import { createServer } from "node:net";
|
|
15228
15633
|
function hindsightImageRef(tag) {
|
|
15229
15634
|
const t = tag?.trim();
|
|
@@ -15249,7 +15654,7 @@ function describePortHolder(port) {
|
|
|
15249
15654
|
];
|
|
15250
15655
|
for (const [cmd, args] of probes) {
|
|
15251
15656
|
try {
|
|
15252
|
-
const out =
|
|
15657
|
+
const out = execFileSync2(cmd, args, {
|
|
15253
15658
|
stdio: "pipe",
|
|
15254
15659
|
encoding: "utf-8"
|
|
15255
15660
|
}).trim();
|
|
@@ -15306,7 +15711,7 @@ async function pickHindsightPorts() {
|
|
|
15306
15711
|
}
|
|
15307
15712
|
function isDockerAvailable() {
|
|
15308
15713
|
try {
|
|
15309
|
-
|
|
15714
|
+
execFileSync2("docker", ["--version"], { stdio: "pipe" });
|
|
15310
15715
|
return true;
|
|
15311
15716
|
} catch {
|
|
15312
15717
|
return false;
|
|
@@ -15314,7 +15719,7 @@ function isDockerAvailable() {
|
|
|
15314
15719
|
}
|
|
15315
15720
|
function isHindsightRunning() {
|
|
15316
15721
|
try {
|
|
15317
|
-
const output =
|
|
15722
|
+
const output = execFileSync2("docker", ["ps", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
|
|
15318
15723
|
return output.trim().length > 0;
|
|
15319
15724
|
} catch {
|
|
15320
15725
|
return false;
|
|
@@ -15322,24 +15727,54 @@ function isHindsightRunning() {
|
|
|
15322
15727
|
}
|
|
15323
15728
|
function isHindsightContainerExists() {
|
|
15324
15729
|
try {
|
|
15325
|
-
const output =
|
|
15730
|
+
const output = execFileSync2("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Names}}"], { stdio: "pipe", encoding: "utf-8" });
|
|
15326
15731
|
return output.trim().length > 0;
|
|
15327
15732
|
} catch {
|
|
15328
15733
|
return false;
|
|
15329
15734
|
}
|
|
15330
15735
|
}
|
|
15331
|
-
function
|
|
15736
|
+
function resolveHindsightPerOpLlm(llm) {
|
|
15737
|
+
const out = [];
|
|
15738
|
+
if (!llm)
|
|
15739
|
+
return out;
|
|
15740
|
+
for (const op of HINDSIGHT_LLM_OPS) {
|
|
15741
|
+
const cfg = llm[op];
|
|
15742
|
+
if (!cfg)
|
|
15743
|
+
continue;
|
|
15744
|
+
const prefix = `HINDSIGHT_API_${op.toUpperCase()}_LLM`;
|
|
15745
|
+
const model = cfg.model?.trim();
|
|
15746
|
+
const provider = cfg.provider?.trim();
|
|
15747
|
+
const baseUrl = cfg.base_url?.trim();
|
|
15748
|
+
const apiKey = cfg.api_key?.trim();
|
|
15749
|
+
if (model)
|
|
15750
|
+
out.push([`${prefix}_MODEL`, model]);
|
|
15751
|
+
if (provider)
|
|
15752
|
+
out.push([`${prefix}_PROVIDER`, provider]);
|
|
15753
|
+
if (baseUrl)
|
|
15754
|
+
out.push([`${prefix}_BASE_URL`, baseUrl]);
|
|
15755
|
+
if (apiKey)
|
|
15756
|
+
out.push([`${prefix}_API_KEY`, apiKey]);
|
|
15757
|
+
}
|
|
15758
|
+
return out;
|
|
15759
|
+
}
|
|
15760
|
+
function resolveHindsightLlm(llm, litellm) {
|
|
15761
|
+
const provider = llm?.provider?.trim() || "claude-code";
|
|
15762
|
+
const model = llm?.model?.trim() || (litellm ? litellm.model ?? HINDSIGHT_DEFAULT_LITELLM_MODEL : HINDSIGHT_DEFAULT_MODEL);
|
|
15763
|
+
return { provider, model };
|
|
15764
|
+
}
|
|
15765
|
+
function startHindsight(ports, litellm, imageTag, llm) {
|
|
15332
15766
|
const apiPort = ports?.apiPort ?? HINDSIGHT_DEFAULT_API_PORT;
|
|
15333
15767
|
const uiPort = ports?.uiPort ?? HINDSIGHT_DEFAULT_UI_PORT;
|
|
15768
|
+
const { provider: llmProvider, model: llmModel } = resolveHindsightLlm(llm, litellm);
|
|
15769
|
+
const perOpLlm = resolveHindsightPerOpLlm(llm);
|
|
15334
15770
|
const envArgs = [
|
|
15335
15771
|
"-e",
|
|
15336
15772
|
`HINDSIGHT_API_MAX_OBSERVATIONS_PER_SCOPE=${HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE}`,
|
|
15337
15773
|
"-e",
|
|
15338
|
-
|
|
15774
|
+
`HINDSIGHT_API_LLM_PROVIDER=${llmProvider}`,
|
|
15339
15775
|
"-e",
|
|
15340
|
-
`HINDSIGHT_API_LLM_MODEL=${
|
|
15341
|
-
"-e",
|
|
15342
|
-
`ANTHROPIC_MODEL=${HINDSIGHT_DEFAULT_MODEL}`,
|
|
15776
|
+
`HINDSIGHT_API_LLM_MODEL=${llmModel}`,
|
|
15777
|
+
...perOpLlm.flatMap(([k, v]) => ["-e", `${k}=${v}`]),
|
|
15343
15778
|
"-e",
|
|
15344
15779
|
`HINDSIGHT_API_MCP_STATELESS=${HINDSIGHT_DEFAULT_MCP_STATELESS}`,
|
|
15345
15780
|
"-e",
|
|
@@ -15361,8 +15796,13 @@ function startHindsight(ports, litellm, imageTag) {
|
|
|
15361
15796
|
"-e",
|
|
15362
15797
|
`HINDSIGHT_API_CONSOLIDATION_MAX_MEMORIES_PER_ROUND=${HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND}`
|
|
15363
15798
|
];
|
|
15799
|
+
if (llmProvider === "claude-code") {
|
|
15800
|
+
envArgs.push("-e", `ANTHROPIC_MODEL=${llmModel}`);
|
|
15801
|
+
}
|
|
15364
15802
|
if (litellm) {
|
|
15365
|
-
|
|
15803
|
+
const litellmRoot = litellm.baseUrl.replace(/\/+$/, "");
|
|
15804
|
+
const anthropicBaseUrl = isClaudeModel(llmModel) ? `${litellmRoot}/anthropic` : litellmRoot;
|
|
15805
|
+
envArgs.push("-e", `HINDSIGHT_API_PORT=${apiPort}`, "-e", `HINDSIGHT_CP_DATAPLANE_API_URL=http://localhost:${apiPort}`, "-e", `ANTHROPIC_BASE_URL=${anthropicBaseUrl}`, "-e", `ANTHROPIC_CUSTOM_HEADERS=x-litellm-api-key: Bearer ${litellm.apiKey}
|
|
15366
15806
|
x-litellm-customer-id: hindsight
|
|
15367
15807
|
x-litellm-tags: service:hindsight`);
|
|
15368
15808
|
}
|
|
@@ -15394,25 +15834,25 @@ x-litellm-tags: service:hindsight`);
|
|
|
15394
15834
|
args.push("-p", `127.0.0.1:${apiPort}:8888`, "-p", `127.0.0.1:${uiPort}:9999`);
|
|
15395
15835
|
}
|
|
15396
15836
|
args.push("-v", "switchroom-hindsight-data:/home/hindsight/.pg0", "-v", "switchroom-hindsight-backups:/backups", "-v", `${HINDSIGHT_BROKER_SOCK_VOLUME}:/run/switchroom/auth-broker`, "--tmpfs", `/run/claude-creds:rw,mode=0700,uid=${HINDSIGHT_DEFAULT_UID},gid=${HINDSIGHT_DEFAULT_UID}`, ...envArgs, hindsightImageRef(imageTag));
|
|
15397
|
-
|
|
15837
|
+
execFileSync2("docker", args, { stdio: "pipe" });
|
|
15398
15838
|
}
|
|
15399
15839
|
function stopHindsight() {
|
|
15400
15840
|
try {
|
|
15401
|
-
|
|
15841
|
+
execFileSync2("docker", ["stop", "switchroom-hindsight"], { stdio: "pipe" });
|
|
15402
15842
|
} catch {}
|
|
15403
15843
|
try {
|
|
15404
|
-
|
|
15844
|
+
execFileSync2("docker", ["rm", "switchroom-hindsight"], { stdio: "pipe" });
|
|
15405
15845
|
} catch {}
|
|
15406
15846
|
}
|
|
15407
15847
|
function pullHindsightImage(imageTag) {
|
|
15408
|
-
|
|
15848
|
+
execFileSync2("docker", ["pull", hindsightImageRef(imageTag)], { stdio: "inherit" });
|
|
15409
15849
|
}
|
|
15410
15850
|
function getRunningHindsightPorts() {
|
|
15411
15851
|
const CONTAINER_API_PORT = 8888;
|
|
15412
15852
|
const CONTAINER_UI_PORT = 9999;
|
|
15413
15853
|
const readPort = (containerPort) => {
|
|
15414
15854
|
try {
|
|
15415
|
-
const out =
|
|
15855
|
+
const out = execFileSync2("docker", ["port", "switchroom-hindsight", `${containerPort}/tcp`], { stdio: "pipe", encoding: "utf-8" });
|
|
15416
15856
|
const m = out.split(`
|
|
15417
15857
|
`).map((l) => l.trim()).find((l) => l.length > 0);
|
|
15418
15858
|
if (!m)
|
|
@@ -15433,7 +15873,7 @@ function getRunningHindsightPorts() {
|
|
|
15433
15873
|
function getHindsightPortsFromEnv() {
|
|
15434
15874
|
let env2;
|
|
15435
15875
|
try {
|
|
15436
|
-
env2 =
|
|
15876
|
+
env2 = execFileSync2("docker", ["inspect", "--format", "{{.Config.Env}}", "switchroom-hindsight"], { stdio: "pipe", encoding: "utf-8" });
|
|
15437
15877
|
} catch {
|
|
15438
15878
|
return null;
|
|
15439
15879
|
}
|
|
@@ -15453,14 +15893,24 @@ function getHindsightPortsFromEnv() {
|
|
|
15453
15893
|
}
|
|
15454
15894
|
function getHindsightStatus() {
|
|
15455
15895
|
try {
|
|
15456
|
-
const output =
|
|
15896
|
+
const output = execFileSync2("docker", ["ps", "-a", "--filter", "name=switchroom-hindsight", "--format", "{{.Status}}"], { stdio: "pipe", encoding: "utf-8" });
|
|
15457
15897
|
const status = output.trim();
|
|
15458
15898
|
return status.length > 0 ? status : null;
|
|
15459
15899
|
} catch {
|
|
15460
15900
|
return null;
|
|
15461
15901
|
}
|
|
15462
15902
|
}
|
|
15463
|
-
function generateHindsightComposeSnippet() {
|
|
15903
|
+
function generateHindsightComposeSnippet(llm) {
|
|
15904
|
+
const { provider: llmProvider, model: llmModel } = resolveHindsightLlm(llm);
|
|
15905
|
+
const perOpLlm = resolveHindsightPerOpLlm(llm);
|
|
15906
|
+
const environment = [
|
|
15907
|
+
` - HINDSIGHT_API_MAX_OBSERVATIONS_PER_SCOPE=${HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE}`,
|
|
15908
|
+
` - HINDSIGHT_API_LLM_PROVIDER=${llmProvider}`,
|
|
15909
|
+
` - HINDSIGHT_API_LLM_MODEL=${llmModel}`,
|
|
15910
|
+
...perOpLlm.map(([k, v]) => ` - ${k}=${v}`),
|
|
15911
|
+
...llmProvider === "claude-code" ? [` - ANTHROPIC_MODEL=${llmModel}`] : [],
|
|
15912
|
+
` - HINDSIGHT_CP_DATAPLANE_API_URL=http://localhost:8888`
|
|
15913
|
+
];
|
|
15464
15914
|
return [
|
|
15465
15915
|
"services:",
|
|
15466
15916
|
" switchroom-hindsight:",
|
|
@@ -15470,10 +15920,7 @@ function generateHindsightComposeSnippet() {
|
|
|
15470
15920
|
` - "127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}:8888"`,
|
|
15471
15921
|
' - "127.0.0.1:19999:9999"',
|
|
15472
15922
|
" environment:",
|
|
15473
|
-
|
|
15474
|
-
" - HINDSIGHT_API_LLM_PROVIDER=claude-code",
|
|
15475
|
-
` - HINDSIGHT_API_LLM_MODEL=${HINDSIGHT_DEFAULT_MODEL}`,
|
|
15476
|
-
` - ANTHROPIC_MODEL=${HINDSIGHT_DEFAULT_MODEL}`,
|
|
15923
|
+
...environment,
|
|
15477
15924
|
` - HINDSIGHT_API_MCP_STATELESS=${HINDSIGHT_DEFAULT_MCP_STATELESS}`,
|
|
15478
15925
|
` - HINDSIGHT_API_RERANKER_LOCAL_BUCKET_BATCHING=${HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING}`,
|
|
15479
15926
|
` - HINDSIGHT_API_RERANKER_MAX_CANDIDATES=${HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES}`,
|
|
@@ -15564,13 +16011,15 @@ async function ensureHindsightConsumer(configPath, account, uid = HINDSIGHT_DEFA
|
|
|
15564
16011
|
atomicWriteFileSync2(configPath, tail, mode);
|
|
15565
16012
|
return { added: true, reason: "added" };
|
|
15566
16013
|
}
|
|
15567
|
-
var HINDSIGHT_DEFAULT_API_PORT = 18888, HINDSIGHT_DEFAULT_UI_PORT = 9999, HINDSIGHT_DEFAULT_MCP_URL, HINDSIGHT_DEFAULT_API_BASE_URL, HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE = 1000, HINDSIGHT_CONSUMER_NAME = "hindsight", HINDSIGHT_DEFAULT_UID = 11000, HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight", HINDSIGHT_IMAGE, HINDSIGHT_DEFAULT_MODEL = "claude-sonnet-5", HINDSIGHT_DEFAULT_MCP_STATELESS = true, HINDSIGHT_BROKER_SOCK_VOLUME, HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING = "true", HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150, HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4, HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8, HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE = 12, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM = 2, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 100, HINDSIGHT_DEFAULT_MEM_LIMIT = "8g", HINDSIGHT_DEFAULT_MEM_RESERVATION = "4g", HINDSIGHT_DEFAULT_PIDS_LIMIT = 1000, HINDSIGHT_DEFAULT_SHM_SIZE = "2g", HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)', HINDSIGHT_HEALTHCHECK_CMD;
|
|
16014
|
+
var HINDSIGHT_DEFAULT_API_PORT = 18888, HINDSIGHT_DEFAULT_UI_PORT = 9999, HINDSIGHT_DEFAULT_MCP_URL, HINDSIGHT_DEFAULT_API_BASE_URL, HINDSIGHT_DEFAULT_MAX_OBSERVATIONS_PER_SCOPE = 1000, HINDSIGHT_CONSUMER_NAME = "hindsight", HINDSIGHT_DEFAULT_UID = 11000, HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight", HINDSIGHT_IMAGE, HINDSIGHT_DEFAULT_MODEL = "claude-sonnet-5", HINDSIGHT_DEFAULT_LITELLM_MODEL = "openrouter/google/gemini-3.1-flash-lite", HINDSIGHT_DEFAULT_MCP_STATELESS = true, HINDSIGHT_BROKER_SOCK_VOLUME, HINDSIGHT_DEFAULT_RERANKER_BUCKET_BATCHING = "true", HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150, HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4, HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8, HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_BATCH_SIZE = 12, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1, HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM = 2, HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 100, HINDSIGHT_DEFAULT_MEM_LIMIT = "8g", HINDSIGHT_DEFAULT_MEM_RESERVATION = "4g", HINDSIGHT_DEFAULT_PIDS_LIMIT = 1000, HINDSIGHT_DEFAULT_SHM_SIZE = "2g", HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)', HINDSIGHT_HEALTHCHECK_CMD, HINDSIGHT_LLM_OPS;
|
|
15568
16015
|
var init_hindsight = __esm(() => {
|
|
16016
|
+
init_model_command();
|
|
15569
16017
|
HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
|
|
15570
16018
|
HINDSIGHT_DEFAULT_API_BASE_URL = HINDSIGHT_DEFAULT_MCP_URL.replace(/\/mcp\/?$/, "");
|
|
15571
16019
|
HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
|
|
15572
16020
|
HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`;
|
|
15573
16021
|
HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
|
|
16022
|
+
HINDSIGHT_LLM_OPS = ["retain", "reflect", "consolidation"];
|
|
15574
16023
|
});
|
|
15575
16024
|
|
|
15576
16025
|
// src/memory/hindsight.ts
|
|
@@ -23797,7 +24246,7 @@ function parseSocketIdentity(socketPath, spec) {
|
|
|
23797
24246
|
}
|
|
23798
24247
|
|
|
23799
24248
|
// src/vault/broker/peercred.ts
|
|
23800
|
-
import { execFileSync as
|
|
24249
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
23801
24250
|
import { readFileSync as readFileSync10, readlinkSync as readlinkSync3, fstatSync } from "node:fs";
|
|
23802
24251
|
function socketPathToIdentity(socketPath) {
|
|
23803
24252
|
return parseSocketIdentity(socketPath, {
|
|
@@ -23970,7 +24419,7 @@ function identify(socketPath, socket, execFileSyncOverride) {
|
|
|
23970
24419
|
if (process.platform !== "linux") {
|
|
23971
24420
|
return null;
|
|
23972
24421
|
}
|
|
23973
|
-
const runner = execFileSyncOverride ??
|
|
24422
|
+
const runner = execFileSyncOverride ?? execFileSync3;
|
|
23974
24423
|
let pid = null;
|
|
23975
24424
|
if (socket !== undefined) {
|
|
23976
24425
|
const fd = fdFromSocket(socket);
|
|
@@ -24454,7 +24903,8 @@ function brokerIsComposeManaged() {
|
|
|
24454
24903
|
return fs.existsSync(OPERATOR_SOCKET_PATH);
|
|
24455
24904
|
}
|
|
24456
24905
|
function vaultTokenFilePath(agentSlug) {
|
|
24457
|
-
|
|
24906
|
+
const base = process.env.SWITCHROOM_AGENTS_DIR || join6(homedir4(), ".switchroom", "agents");
|
|
24907
|
+
return join6(base, agentSlug, ".vault-token");
|
|
24458
24908
|
}
|
|
24459
24909
|
function readVaultTokenFile(agentSlug) {
|
|
24460
24910
|
const filePath = vaultTokenFilePath(agentSlug);
|
|
@@ -25263,7 +25713,7 @@ var init_onboarding = __esm(() => {
|
|
|
25263
25713
|
});
|
|
25264
25714
|
|
|
25265
25715
|
// src/repos/bare-clone.ts
|
|
25266
|
-
import { execFileSync as
|
|
25716
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
25267
25717
|
import { existsSync as existsSync13, mkdirSync as mkdirSync8 } from "node:fs";
|
|
25268
25718
|
import { resolve as resolve9 } from "node:path";
|
|
25269
25719
|
function bareClonePath(slug) {
|
|
@@ -25276,7 +25726,7 @@ function ensureBareClone(slug, url) {
|
|
|
25276
25726
|
if (!existsSync13(clonePath)) {
|
|
25277
25727
|
process.stderr.write(`[switchroom] repo "${slug}": cloning ${url} \u2026
|
|
25278
25728
|
`);
|
|
25279
|
-
|
|
25729
|
+
execFileSync4("git", ["clone", "--bare", url, clonePath], {
|
|
25280
25730
|
stdio: ["ignore", "pipe", "pipe"]
|
|
25281
25731
|
});
|
|
25282
25732
|
process.stderr.write(`[switchroom] repo "${slug}": bare clone ready at ${clonePath}
|
|
@@ -25285,7 +25735,7 @@ function ensureBareClone(slug, url) {
|
|
|
25285
25735
|
process.stderr.write(`[switchroom] repo "${slug}": fetching ${clonePath} \u2026
|
|
25286
25736
|
`);
|
|
25287
25737
|
try {
|
|
25288
|
-
|
|
25738
|
+
execFileSync4("git", ["fetch", "--all"], {
|
|
25289
25739
|
cwd: clonePath,
|
|
25290
25740
|
stdio: ["ignore", "pipe", "pipe"]
|
|
25291
25741
|
});
|
|
@@ -25302,7 +25752,7 @@ var init_bare_clone = __esm(() => {
|
|
|
25302
25752
|
});
|
|
25303
25753
|
|
|
25304
25754
|
// src/repos/agent-worktree.ts
|
|
25305
|
-
import { execFileSync as
|
|
25755
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
25306
25756
|
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync5 } from "node:fs";
|
|
25307
25757
|
import { join as join9, resolve as resolve10 } from "node:path";
|
|
25308
25758
|
function agentBranchName(agentName) {
|
|
@@ -25313,7 +25763,7 @@ function agentWorktreePath(agentDir, slug) {
|
|
|
25313
25763
|
}
|
|
25314
25764
|
function isWorktreeDirty(worktreePath) {
|
|
25315
25765
|
try {
|
|
25316
|
-
const out =
|
|
25766
|
+
const out = execFileSync5("git", ["status", "--porcelain"], {
|
|
25317
25767
|
cwd: worktreePath,
|
|
25318
25768
|
stdio: ["ignore", "pipe", "pipe"],
|
|
25319
25769
|
encoding: "utf-8"
|
|
@@ -25325,7 +25775,7 @@ function isWorktreeDirty(worktreePath) {
|
|
|
25325
25775
|
}
|
|
25326
25776
|
function headShortSha(worktreePath) {
|
|
25327
25777
|
try {
|
|
25328
|
-
return
|
|
25778
|
+
return execFileSync5("git", ["rev-parse", "--short", "HEAD"], {
|
|
25329
25779
|
cwd: worktreePath,
|
|
25330
25780
|
stdio: ["ignore", "pipe", "pipe"],
|
|
25331
25781
|
encoding: "utf-8"
|
|
@@ -25336,7 +25786,7 @@ function headShortSha(worktreePath) {
|
|
|
25336
25786
|
}
|
|
25337
25787
|
function branchExistsInBare(bareClonePath2, branchName) {
|
|
25338
25788
|
try {
|
|
25339
|
-
|
|
25789
|
+
execFileSync5("git", ["rev-parse", "--verify", `refs/heads/${branchName}`], {
|
|
25340
25790
|
cwd: bareClonePath2,
|
|
25341
25791
|
stdio: ["ignore", "pipe", "pipe"]
|
|
25342
25792
|
});
|
|
@@ -25347,7 +25797,7 @@ function branchExistsInBare(bareClonePath2, branchName) {
|
|
|
25347
25797
|
}
|
|
25348
25798
|
function resolveDefaultBranch(bareClonePath2) {
|
|
25349
25799
|
try {
|
|
25350
|
-
const out =
|
|
25800
|
+
const out = execFileSync5("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
25351
25801
|
cwd: bareClonePath2,
|
|
25352
25802
|
stdio: ["ignore", "pipe", "pipe"],
|
|
25353
25803
|
encoding: "utf-8"
|
|
@@ -25365,12 +25815,12 @@ function ensureAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
|
|
|
25365
25815
|
if (!existsSync14(worktreePath)) {
|
|
25366
25816
|
mkdirSync9(join9(agentDir, "work"), { recursive: true });
|
|
25367
25817
|
if (branchExistsInBare(bareClonePath2, branch)) {
|
|
25368
|
-
|
|
25818
|
+
execFileSync5("git", ["worktree", "add", worktreePath, branch], {
|
|
25369
25819
|
cwd: bareClonePath2,
|
|
25370
25820
|
stdio: ["ignore", "pipe", "pipe"]
|
|
25371
25821
|
});
|
|
25372
25822
|
} else {
|
|
25373
|
-
|
|
25823
|
+
execFileSync5("git", [
|
|
25374
25824
|
"worktree",
|
|
25375
25825
|
"add",
|
|
25376
25826
|
worktreePath,
|
|
@@ -25398,11 +25848,11 @@ function ensureAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
|
|
|
25398
25848
|
};
|
|
25399
25849
|
}
|
|
25400
25850
|
try {
|
|
25401
|
-
|
|
25851
|
+
execFileSync5("git", ["fetch", "origin"], {
|
|
25402
25852
|
cwd: worktreePath,
|
|
25403
25853
|
stdio: ["ignore", "pipe", "pipe"]
|
|
25404
25854
|
});
|
|
25405
|
-
|
|
25855
|
+
execFileSync5("git", ["merge", "--ff-only", `origin/${defaultBranch}`], {
|
|
25406
25856
|
cwd: worktreePath,
|
|
25407
25857
|
stdio: ["ignore", "pipe", "pipe"]
|
|
25408
25858
|
});
|
|
@@ -25420,7 +25870,7 @@ function removeAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
|
|
|
25420
25870
|
const branch = agentBranchName(agentName);
|
|
25421
25871
|
if (existsSync14(worktreePath)) {
|
|
25422
25872
|
try {
|
|
25423
|
-
|
|
25873
|
+
execFileSync5("git", ["worktree", "remove", "--force", worktreePath], {
|
|
25424
25874
|
cwd: bareClonePath2,
|
|
25425
25875
|
stdio: ["ignore", "pipe", "pipe"]
|
|
25426
25876
|
});
|
|
@@ -25432,7 +25882,7 @@ function removeAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
|
|
|
25432
25882
|
}
|
|
25433
25883
|
if (branchExistsInBare(bareClonePath2, branch)) {
|
|
25434
25884
|
try {
|
|
25435
|
-
|
|
25885
|
+
execFileSync5("git", ["branch", "-D", branch], {
|
|
25436
25886
|
cwd: bareClonePath2,
|
|
25437
25887
|
stdio: ["ignore", "pipe", "pipe"]
|
|
25438
25888
|
});
|
|
@@ -25443,7 +25893,7 @@ function removeAgentWorktree(agentName, slug, bareClonePath2, agentDir) {
|
|
|
25443
25893
|
}
|
|
25444
25894
|
}
|
|
25445
25895
|
try {
|
|
25446
|
-
|
|
25896
|
+
execFileSync5("git", ["worktree", "prune"], {
|
|
25447
25897
|
cwd: bareClonePath2,
|
|
25448
25898
|
stdio: ["ignore", "pipe", "pipe"]
|
|
25449
25899
|
});
|
|
@@ -25472,7 +25922,7 @@ import {
|
|
|
25472
25922
|
unlinkSync as unlinkSync4
|
|
25473
25923
|
} from "node:fs";
|
|
25474
25924
|
import { homedir as homedir5 } from "node:os";
|
|
25475
|
-
import { execSync, execFileSync as
|
|
25925
|
+
import { execSync, execFileSync as execFileSync6 } from "node:child_process";
|
|
25476
25926
|
import { join as join10, resolve as resolve11 } from "node:path";
|
|
25477
25927
|
import { createHash as createHash2 } from "node:crypto";
|
|
25478
25928
|
function prependReplyDiscipline(rendered, context) {
|
|
@@ -25606,13 +26056,13 @@ function alignAgentUid(name, agentDir, uid, opts = {}) {
|
|
|
25606
26056
|
`));
|
|
25607
26057
|
}
|
|
25608
26058
|
try {
|
|
25609
|
-
|
|
26059
|
+
execFileSync6("chown", ["-R", `${uid}:${uid}`, ...paths], {
|
|
25610
26060
|
stdio: ["ignore", "ignore", "pipe"]
|
|
25611
26061
|
});
|
|
25612
26062
|
return { chowned: true, paths };
|
|
25613
26063
|
} catch {}
|
|
25614
26064
|
try {
|
|
25615
|
-
|
|
26065
|
+
execFileSync6("sudo", ["chown", "-R", `${uid}:${uid}`, ...paths], {
|
|
25616
26066
|
stdio: "inherit"
|
|
25617
26067
|
});
|
|
25618
26068
|
return { chowned: true, paths };
|
|
@@ -26260,7 +26710,7 @@ Thumbs.db
|
|
|
26260
26710
|
execSync("git add -A", { cwd: workspaceDir, stdio: "pipe" });
|
|
26261
26711
|
const userEmail = process.env.GIT_AUTHOR_EMAIL || "switchroom@localhost";
|
|
26262
26712
|
const userName = process.env.GIT_AUTHOR_NAME || "Switchroom Agent";
|
|
26263
|
-
|
|
26713
|
+
execFileSync6("git", [
|
|
26264
26714
|
"-c",
|
|
26265
26715
|
`user.email=${userEmail}`,
|
|
26266
26716
|
"-c",
|
|
@@ -26298,11 +26748,12 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig) {
|
|
|
26298
26748
|
}
|
|
26299
26749
|
const destPath = join10(agentDir, ".claude", "plugins", "hindsight-memory");
|
|
26300
26750
|
const additionalBanks = resolveUsers(switchroomConfig, agentName).additionalBanks;
|
|
26751
|
+
const retainConfig = resolveHindsightRetainConfig(switchroomConfig, agentName);
|
|
26301
26752
|
let expectedSettings = null;
|
|
26302
26753
|
try {
|
|
26303
26754
|
const vendorSettingsPath = join10(sourcePath, "settings.json");
|
|
26304
26755
|
if (existsSync15(vendorSettingsPath)) {
|
|
26305
|
-
expectedSettings = renderHindsightSettingsOverrides(readFileSync13(vendorSettingsPath, "utf-8"), additionalBanks);
|
|
26756
|
+
expectedSettings = renderHindsightSettingsOverrides(readFileSync13(vendorSettingsPath, "utf-8"), additionalBanks, retainConfig);
|
|
26306
26757
|
}
|
|
26307
26758
|
} catch {}
|
|
26308
26759
|
const upToDate = dirContentEquals(sourcePath, destPath, expectedSettings != null ? { "settings.json": expectedSettings } : undefined);
|
|
@@ -26312,13 +26763,21 @@ function installHindsightPlugin(agentName, agentDir, switchroomConfig) {
|
|
|
26312
26763
|
}
|
|
26313
26764
|
copyDirRecursive2(sourcePath, destPath);
|
|
26314
26765
|
}
|
|
26315
|
-
applyHindsightSettingsOverrides(destPath, additionalBanks);
|
|
26766
|
+
applyHindsightSettingsOverrides(destPath, additionalBanks, retainConfig);
|
|
26316
26767
|
const bankId = agentMemory?.collection ?? agentName;
|
|
26317
26768
|
const mcpUrl = memory.config?.url ?? HINDSIGHT_DEFAULT_MCP_URL;
|
|
26318
26769
|
const apiBaseUrl = mcpUrl.replace(/\/mcp\/?$/, "").replace(/\/$/, "");
|
|
26319
26770
|
return { pluginDir: destPath, apiBaseUrl, bankId };
|
|
26320
26771
|
}
|
|
26321
|
-
function
|
|
26772
|
+
function resolveHindsightRetainConfig(switchroomConfig, agentName) {
|
|
26773
|
+
const resolved = switchroomConfig ? resolveAgentConfig(switchroomConfig.defaults, switchroomConfig.profiles, switchroomConfig.agents[agentName] ?? {}) : undefined;
|
|
26774
|
+
const retain = resolved?.memory?.retain;
|
|
26775
|
+
return {
|
|
26776
|
+
everyNTurns: retain?.every_n_turns ?? HINDSIGHT_DEFAULT_RETAIN_EVERY_N_TURNS,
|
|
26777
|
+
overlapTurns: retain?.overlap_turns ?? HINDSIGHT_DEFAULT_RETAIN_OVERLAP_TURNS
|
|
26778
|
+
};
|
|
26779
|
+
}
|
|
26780
|
+
function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks, retainConfig) {
|
|
26322
26781
|
const settingsPath = join10(pluginDestPath, "settings.json");
|
|
26323
26782
|
if (!existsSync15(settingsPath))
|
|
26324
26783
|
return;
|
|
@@ -26328,20 +26787,21 @@ function applyHindsightSettingsOverrides(pluginDestPath, additionalBanks) {
|
|
|
26328
26787
|
} catch {
|
|
26329
26788
|
return;
|
|
26330
26789
|
}
|
|
26331
|
-
const next = renderHindsightSettingsOverrides(raw, additionalBanks);
|
|
26790
|
+
const next = renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig);
|
|
26332
26791
|
if (next == null)
|
|
26333
26792
|
return;
|
|
26334
26793
|
writeFileSyncIfChanged(settingsPath, next);
|
|
26335
26794
|
}
|
|
26336
|
-
function renderHindsightSettingsOverrides(raw, additionalBanks) {
|
|
26795
|
+
function renderHindsightSettingsOverrides(raw, additionalBanks, retainConfig) {
|
|
26337
26796
|
let settings;
|
|
26338
26797
|
try {
|
|
26339
26798
|
settings = JSON.parse(raw);
|
|
26340
26799
|
} catch {
|
|
26341
26800
|
return null;
|
|
26342
26801
|
}
|
|
26343
|
-
settings.retainEveryNTurns =
|
|
26802
|
+
settings.retainEveryNTurns = retainConfig.everyNTurns;
|
|
26344
26803
|
settings.retainMode = "chunked";
|
|
26804
|
+
settings.retainOverlapTurns = retainConfig.overlapTurns;
|
|
26345
26805
|
settings.recallMaxMemories = 8;
|
|
26346
26806
|
settings.recallMinOverlap = 0.1;
|
|
26347
26807
|
settings.recallTypes = ["world", "experience", "observation"];
|
|
@@ -26353,17 +26813,25 @@ function renderHindsightSettingsOverrides(raw, additionalBanks) {
|
|
|
26353
26813
|
return JSON.stringify(settings, null, 2) + `
|
|
26354
26814
|
`;
|
|
26355
26815
|
}
|
|
26816
|
+
function resolvePermissionDefaultMode(agentConfig, switchroomConfig) {
|
|
26817
|
+
const raw = agentConfig.permission_mode ?? switchroomConfig?.defaults?.permission_mode ?? SWITCHROOM_DEFAULT_PERMISSION_MODE;
|
|
26818
|
+
if (VALID_SETTINGS_DEFAULT_MODES.includes(raw)) {
|
|
26819
|
+
return raw;
|
|
26820
|
+
}
|
|
26821
|
+
console.warn(source_default.yellow(`[switchroom] permission_mode "${raw}" has no settings.json defaultMode ` + `equivalent \u2014 using "${SWITCHROOM_DEFAULT_PERMISSION_MODE}" for ` + `permissions.defaultMode (the --permission-mode CLI flag still carries "${raw}").`));
|
|
26822
|
+
return SWITCHROOM_DEFAULT_PERMISSION_MODE;
|
|
26823
|
+
}
|
|
26356
26824
|
function buildWorkspaceContext(args) {
|
|
26357
26825
|
const {
|
|
26358
26826
|
name,
|
|
26359
26827
|
agentDir,
|
|
26360
26828
|
agentConfig,
|
|
26361
26829
|
telegramConfig,
|
|
26830
|
+
switchroomConfig,
|
|
26362
26831
|
switchroomConfigPath,
|
|
26363
26832
|
topicId,
|
|
26364
26833
|
tools,
|
|
26365
26834
|
permissionAllow,
|
|
26366
|
-
hasAllWildcard,
|
|
26367
26835
|
resolvedBotToken,
|
|
26368
26836
|
rawBotToken,
|
|
26369
26837
|
hindsightAutoRecallEnabled,
|
|
@@ -26396,7 +26864,7 @@ function buildWorkspaceContext(args) {
|
|
|
26396
26864
|
...INTERACTIVE_TUI_FLEET_DENY_TOOLS
|
|
26397
26865
|
]),
|
|
26398
26866
|
permissionAllow,
|
|
26399
|
-
|
|
26867
|
+
defaultMode: resolvePermissionDefaultMode(agentConfig, switchroomConfig),
|
|
26400
26868
|
memory: agentConfig.memory,
|
|
26401
26869
|
model: agentConfig.model,
|
|
26402
26870
|
mcpServers: agentConfig.mcp_servers ? filterMcpServers(agentConfig.mcp_servers) : agentConfig.mcp_servers,
|
|
@@ -26903,6 +27371,7 @@ This file is auto-maintained. Do not edit manually.
|
|
|
26903
27371
|
}, created, skipped, 384);
|
|
26904
27372
|
writeIfMissing(join10(agentDir, "telegram", "access.json"), () => buildAccessJson2(agentConfig, telegramConfig, topicId, userId), created, skipped, 384);
|
|
26905
27373
|
reconcileConfiguredGroup(join10(agentDir, "telegram", "access.json"), agentConfig, telegramConfig);
|
|
27374
|
+
writeFileSyncIfChanged(join10(agentDir, "telegram", "people.json"), buildPeopleJson(switchroomConfig), 384);
|
|
26906
27375
|
if (agentConfig.subagents) {
|
|
26907
27376
|
const agentsDir2 = join10(agentDir, ".claude", "agents");
|
|
26908
27377
|
mkdirSync10(agentsDir2, { recursive: true });
|
|
@@ -27312,7 +27781,7 @@ function buildSettingsHooksBlock(p) {
|
|
|
27312
27781
|
hooks: [
|
|
27313
27782
|
{
|
|
27314
27783
|
type: "command",
|
|
27315
|
-
command: wrap("hook:workspace-dynamic", `${useInjectOnChange ? `env SWITCHROOM_INJECT_ON_CHANGE=
|
|
27784
|
+
command: wrap("hook:workspace-dynamic", `${useInjectOnChange ? "" : `env SWITCHROOM_INJECT_ON_CHANGE=0 `}bash "${join10(DOCKER_BIN_PATH, "workspace-dynamic-hook.sh")}"`),
|
|
27316
27785
|
timeout: 5
|
|
27317
27786
|
}
|
|
27318
27787
|
]
|
|
@@ -27662,11 +28131,7 @@ ${baseAppend}` : TELEGRAM_FORMATTING_FLOOR_CARD;
|
|
|
27662
28131
|
settings.permissions = settings.permissions ?? {};
|
|
27663
28132
|
settings.permissions.allow = desiredAllow;
|
|
27664
28133
|
settings.permissions.deny = desiredDeny;
|
|
27665
|
-
|
|
27666
|
-
settings.permissions.defaultMode = "acceptEdits";
|
|
27667
|
-
} else {
|
|
27668
|
-
delete settings.permissions.defaultMode;
|
|
27669
|
-
}
|
|
28134
|
+
settings.permissions.defaultMode = resolvePermissionDefaultMode(agentConfig, switchroomConfig);
|
|
27670
28135
|
const mcpServers = {};
|
|
27671
28136
|
const hindsightEntry = getHindsightSettingsEntry(name, switchroomConfig);
|
|
27672
28137
|
if (hindsightEntry) {
|
|
@@ -28129,6 +28594,11 @@ function reconcileConfiguredGroup(accessPath, agentConfig, telegramConfig) {
|
|
|
28129
28594
|
renameSync4(tmp, accessPath);
|
|
28130
28595
|
console.log(source_default.green(` registered supergroup ${forumChatId} in access.json ` + `(responds to all topics; requireMention=false)`));
|
|
28131
28596
|
}
|
|
28597
|
+
function buildPeopleJson(switchroomConfig) {
|
|
28598
|
+
const entries = switchroomConfig ? resolvePersonEntries(switchroomConfig) : [];
|
|
28599
|
+
return JSON.stringify({ entries }, null, 2) + `
|
|
28600
|
+
`;
|
|
28601
|
+
}
|
|
28132
28602
|
function buildAccessJson2(agentConfig, telegramConfig, resolvedTopicId, userId) {
|
|
28133
28603
|
const allowFrom = userId ? [String(userId)] : [];
|
|
28134
28604
|
if (allowFrom.length === 0) {
|
|
@@ -28470,7 +28940,7 @@ in doubt, shorter and plainer wins.
|
|
|
28470
28940
|
|
|
28471
28941
|
Every turn that answers a user message ends with a user-visible \`reply\`
|
|
28472
28942
|
\u2014 Telegram is all the user sees; your terminal output
|
|
28473
|
-
never reaches them.`, SWITCHROOM_TELEGRAM_MCP_TOOLS, HINDSIGHT_MCP_TOOLS, AGENT_CONFIG_MCP_TOOLS, HOSTD_MCP_TOOLS, WEBKITE_MCP_TOOLS, LEGACY_SWITCHROOM_MCP_TOKENS, LEGACY_HOSTD_BLANKET_TOKENS, DEFAULT_READ_ONLY_PREAPPROVED_TOOLS, WEBKITE_FLEET_DENY_TOOLS, INTERACTIVE_TUI_FLEET_DENY_TOOLS, WEBKITE_BINARY_CONTAINER_PATH = "/usr/local/bin/webkite", SWITCHROOM_DEFAULT_MAIN_MODEL = "claude-sonnet-5", SWITCHROOM_DEFAULT_THINKING_EFFORT = "low", SWITCHROOM_OWNED_SETTINGS_KEYS, ALL_BUILTIN_TOOLS, DOCKER_TELEGRAM_PLUGIN_PATH = "/opt/switchroom/telegram-plugin", DOCKER_HOOKS_PATH, DOCKER_BUNDLED_HOOKS_PATH = "/opt/switchroom/hooks", DOCKER_BIN_PATH = "/opt/switchroom/bin", DOCKER_SECURITY_PLUGIN_PATH = "/opt/switchroom/security-plugin", DOCKER_CONFIG_PATH = "/state/config/switchroom.yaml", DOCKER_SWITCHROOM_CLI_PATH = "/usr/local/bin/switchroom", DOCKER_AUTH_BROKER_SOCKET = "/run/switchroom/auth-broker/sock", DOCKER_VAULT_BROKER_SOCKET = "/run/switchroom/broker/sock", DOCKER_AGENT_HOME = "/state/agent/home", INTEGRATION_MCP_RESOLVERS;
|
|
28943
|
+
never reaches them.`, SWITCHROOM_TELEGRAM_MCP_TOOLS, HINDSIGHT_MCP_TOOLS, AGENT_CONFIG_MCP_TOOLS, HOSTD_MCP_TOOLS, WEBKITE_MCP_TOOLS, LEGACY_SWITCHROOM_MCP_TOKENS, LEGACY_HOSTD_BLANKET_TOKENS, DEFAULT_READ_ONLY_PREAPPROVED_TOOLS, WEBKITE_FLEET_DENY_TOOLS, INTERACTIVE_TUI_FLEET_DENY_TOOLS, WEBKITE_BINARY_CONTAINER_PATH = "/usr/local/bin/webkite", SWITCHROOM_DEFAULT_MAIN_MODEL = "claude-sonnet-5", SWITCHROOM_DEFAULT_THINKING_EFFORT = "low", SWITCHROOM_OWNED_SETTINGS_KEYS, ALL_BUILTIN_TOOLS, HINDSIGHT_DEFAULT_RETAIN_EVERY_N_TURNS = 3, HINDSIGHT_DEFAULT_RETAIN_OVERLAP_TURNS = 1, VALID_SETTINGS_DEFAULT_MODES, SWITCHROOM_DEFAULT_PERMISSION_MODE = "acceptEdits", DOCKER_TELEGRAM_PLUGIN_PATH = "/opt/switchroom/telegram-plugin", DOCKER_HOOKS_PATH, DOCKER_BUNDLED_HOOKS_PATH = "/opt/switchroom/hooks", DOCKER_BIN_PATH = "/opt/switchroom/bin", DOCKER_SECURITY_PLUGIN_PATH = "/opt/switchroom/security-plugin", DOCKER_CONFIG_PATH = "/state/config/switchroom.yaml", DOCKER_SWITCHROOM_CLI_PATH = "/usr/local/bin/switchroom", DOCKER_AUTH_BROKER_SOCKET = "/run/switchroom/auth-broker/sock", DOCKER_VAULT_BROKER_SOCKET = "/run/switchroom/broker/sock", DOCKER_AGENT_HOME = "/state/agent/home", INTEGRATION_MCP_RESOLVERS;
|
|
28474
28944
|
var init_scaffold = __esm(() => {
|
|
28475
28945
|
init_source();
|
|
28476
28946
|
init_cron_unit_name();
|
|
@@ -28585,6 +29055,12 @@ var init_scaffold = __esm(() => {
|
|
|
28585
29055
|
"Agent",
|
|
28586
29056
|
"ExitPlanMode"
|
|
28587
29057
|
];
|
|
29058
|
+
VALID_SETTINGS_DEFAULT_MODES = [
|
|
29059
|
+
"default",
|
|
29060
|
+
"acceptEdits",
|
|
29061
|
+
"plan",
|
|
29062
|
+
"bypassPermissions"
|
|
29063
|
+
];
|
|
28588
29064
|
DOCKER_HOOKS_PATH = `${DOCKER_TELEGRAM_PLUGIN_PATH}/hooks`;
|
|
28589
29065
|
INTEGRATION_MCP_RESOLVERS = [
|
|
28590
29066
|
{
|
|
@@ -28829,6 +29305,7 @@ function describeAgents(config, litellmConfirmedAgents) {
|
|
|
28829
29305
|
baseUrl: resolved.litellm?.base_url ?? config.litellm?.base_url,
|
|
28830
29306
|
smallFastModel: resolved.litellm?.small_fast_model ?? config.litellm?.small_fast_model ?? "claude-haiku-4-5-20251001"
|
|
28831
29307
|
},
|
|
29308
|
+
model: resolveMainModel(resolved.model),
|
|
28832
29309
|
timezone: resolveTimezone(config, resolved, {
|
|
28833
29310
|
onUtcFallback: () => {
|
|
28834
29311
|
console.warn(` \u26a0 timezone: no explicit timezone set and server detection resolved to UTC ` + `for agent "${name}" \u2014 cron schedules and the per-turn time hint will ` + `run in UTC. Add \`switchroom.timezone: "Region/City"\` (e.g. ` + `"Australia/Melbourne") to switchroom.yaml to silence this warning.`);
|
|
@@ -29261,7 +29738,7 @@ function emitAgentService(lines, a, imageTag, buildMode, buildContext, homePrefi
|
|
|
29261
29738
|
const root = a.litellm.baseUrl.replace(/\/+$/, "");
|
|
29262
29739
|
env2.SWITCHROOM_LITELLM = "1";
|
|
29263
29740
|
env2.SWITCHROOM_LITELLM_BASE = root;
|
|
29264
|
-
env2.ANTHROPIC_BASE_URL = `${root}/anthropic
|
|
29741
|
+
env2.ANTHROPIC_BASE_URL = isClaudeModel(a.model) ? `${root}/anthropic` : root;
|
|
29265
29742
|
env2.ANTHROPIC_SMALL_FAST_MODEL = a.litellm.smallFastModel;
|
|
29266
29743
|
}
|
|
29267
29744
|
env2.SWITCHROOM_VOICE_ENGINE = voiceEngine;
|
|
@@ -29359,6 +29836,8 @@ var init_compose = __esm(() => {
|
|
|
29359
29836
|
init_schema();
|
|
29360
29837
|
init_cron_routing();
|
|
29361
29838
|
init_tier_selector();
|
|
29839
|
+
init_scaffold();
|
|
29840
|
+
init_model_command();
|
|
29362
29841
|
init_merge();
|
|
29363
29842
|
init_timezone();
|
|
29364
29843
|
init_peercred();
|
|
@@ -29686,7 +30165,7 @@ var init_write_compose = __esm(() => {
|
|
|
29686
30165
|
});
|
|
29687
30166
|
|
|
29688
30167
|
// src/agents/tmux.ts
|
|
29689
|
-
import { execFileSync as
|
|
30168
|
+
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
29690
30169
|
function sendAgentInterrupt(opts) {
|
|
29691
30170
|
const { agentName } = opts;
|
|
29692
30171
|
const attempts = typeof opts.attempts === "number" && opts.attempts > 0 ? opts.attempts : 1;
|
|
@@ -29696,7 +30175,7 @@ function sendAgentInterrupt(opts) {
|
|
|
29696
30175
|
let lastError = null;
|
|
29697
30176
|
for (let i = 0;i < attempts; i++) {
|
|
29698
30177
|
try {
|
|
29699
|
-
|
|
30178
|
+
execFileSync7("tmux", args, {
|
|
29700
30179
|
timeout: 3000,
|
|
29701
30180
|
stdio: ["ignore", "pipe", "pipe"]
|
|
29702
30181
|
});
|
|
@@ -29737,7 +30216,7 @@ var init_compose_env = () => {};
|
|
|
29737
30216
|
import { resolve as resolve13 } from "node:path";
|
|
29738
30217
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync6 } from "node:fs";
|
|
29739
30218
|
import { homedir as homedir7 } from "node:os";
|
|
29740
|
-
import { execFileSync as
|
|
30219
|
+
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
29741
30220
|
function resolveSwitchroomHome(explicit) {
|
|
29742
30221
|
if (explicit && explicit.length > 0)
|
|
29743
30222
|
return explicit;
|
|
@@ -29768,7 +30247,7 @@ function bringUpAgentService(opts) {
|
|
|
29768
30247
|
const dockerBin = opts.dockerBin ?? "docker";
|
|
29769
30248
|
const stdio = opts.stdio ?? "inherit";
|
|
29770
30249
|
for (const svc of ["vault-broker", "approval-kernel", "switchroom-auth-broker"]) {
|
|
29771
|
-
|
|
30250
|
+
execFileSync8(dockerBin, [
|
|
29772
30251
|
"compose",
|
|
29773
30252
|
"-f",
|
|
29774
30253
|
composePath,
|
|
@@ -29780,7 +30259,7 @@ function bringUpAgentService(opts) {
|
|
|
29780
30259
|
svc
|
|
29781
30260
|
], { stdio });
|
|
29782
30261
|
}
|
|
29783
|
-
|
|
30262
|
+
execFileSync8(dockerBin, [
|
|
29784
30263
|
"compose",
|
|
29785
30264
|
"-f",
|
|
29786
30265
|
composePath,
|
|
@@ -29799,7 +30278,7 @@ var init_docker_fleet = __esm(() => {
|
|
|
29799
30278
|
});
|
|
29800
30279
|
|
|
29801
30280
|
// src/agents/singleton-reconcile.ts
|
|
29802
|
-
import { execFileSync as
|
|
30281
|
+
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
29803
30282
|
import { readFileSync as readFileSync16 } from "node:fs";
|
|
29804
30283
|
function resolveSingletonServices(voiceEngine) {
|
|
29805
30284
|
const engine = voiceEngine ?? loadHostCapabilities()?.voice.engine ?? "cloud";
|
|
@@ -29825,7 +30304,7 @@ function readPinnedSingletonImages(composeText, services = SINGLETON_SERVICES) {
|
|
|
29825
30304
|
}
|
|
29826
30305
|
function defaultInspectImage(dockerBin, container) {
|
|
29827
30306
|
try {
|
|
29828
|
-
const out =
|
|
30307
|
+
const out = execFileSync9(dockerBin, ["inspect", "-f", "{{.Config.Image}}", container], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 5000 }).trim();
|
|
29829
30308
|
return out.length > 0 ? out : null;
|
|
29830
30309
|
} catch {
|
|
29831
30310
|
return null;
|
|
@@ -29851,7 +30330,7 @@ function detectSingletonDrift(deps) {
|
|
|
29851
30330
|
});
|
|
29852
30331
|
}
|
|
29853
30332
|
function defaultRecreate(dockerBin, project, composeFile, service) {
|
|
29854
|
-
|
|
30333
|
+
execFileSync9(dockerBin, ["compose", "-p", project, "-f", composeFile, ...composeEnvFileArgs(composeFile), "up", "-d", "--no-deps", service], { stdio: ["ignore", "pipe", "pipe"], timeout: 120000 });
|
|
29855
30334
|
}
|
|
29856
30335
|
function reconcileSingletons(deps) {
|
|
29857
30336
|
const dockerBin = deps.dockerBin ?? "docker";
|
|
@@ -29894,7 +30373,7 @@ var init_singleton_reconcile = __esm(() => {
|
|
|
29894
30373
|
});
|
|
29895
30374
|
|
|
29896
30375
|
// src/agents/lifecycle.ts
|
|
29897
|
-
import { execFileSync as
|
|
30376
|
+
import { execFileSync as execFileSync10, spawn, spawnSync } from "node:child_process";
|
|
29898
30377
|
import { existsSync as existsSync20, mkdirSync as mkdirSync14, writeFileSync as writeFileSync7, renameSync as renameSync5, readFileSync as readFileSync17 } from "node:fs";
|
|
29899
30378
|
import { resolve as resolve14, join as join14 } from "node:path";
|
|
29900
30379
|
import { connect } from "node:net";
|
|
@@ -29925,7 +30404,7 @@ function buildCliRestartReason(opts) {
|
|
|
29925
30404
|
if (!buildCommit)
|
|
29926
30405
|
return "cli: restart";
|
|
29927
30406
|
try {
|
|
29928
|
-
const head =
|
|
30407
|
+
const head = execFileSync10("git", ["rev-parse", "HEAD"], {
|
|
29929
30408
|
cwd: cwd ?? process.cwd(),
|
|
29930
30409
|
encoding: "utf-8",
|
|
29931
30410
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -29936,7 +30415,7 @@ function buildCliRestartReason(opts) {
|
|
|
29936
30415
|
return "cli: restart";
|
|
29937
30416
|
let subject = "";
|
|
29938
30417
|
try {
|
|
29939
|
-
subject =
|
|
30418
|
+
subject = execFileSync10("git", ["log", "-1", "--pretty=%s", head], {
|
|
29940
30419
|
cwd: cwd ?? process.cwd(),
|
|
29941
30420
|
encoding: "utf-8",
|
|
29942
30421
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -29966,7 +30445,7 @@ function reconcileSingletonImages(log) {
|
|
|
29966
30445
|
}
|
|
29967
30446
|
function dockerSync(args) {
|
|
29968
30447
|
try {
|
|
29969
|
-
return
|
|
30448
|
+
return execFileSync10("docker", args, {
|
|
29970
30449
|
encoding: "utf-8",
|
|
29971
30450
|
stdio: ["ignore", "pipe", "pipe"]
|
|
29972
30451
|
}).trim();
|
|
@@ -30355,12 +30834,12 @@ var init_lifecycle = __esm(() => {
|
|
|
30355
30834
|
});
|
|
30356
30835
|
|
|
30357
30836
|
// src/auth/pane-ready-probe.ts
|
|
30358
|
-
import { execFileSync as
|
|
30837
|
+
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
30359
30838
|
function defaultPaneReadyDeps() {
|
|
30360
30839
|
return {
|
|
30361
30840
|
capturePane(sessionName) {
|
|
30362
30841
|
try {
|
|
30363
|
-
return
|
|
30842
|
+
return execFileSync12("tmux", ["capture-pane", "-p", "-t", sessionName, "-S", "-200"], {
|
|
30364
30843
|
encoding: "utf-8",
|
|
30365
30844
|
stdio: ["pipe", "pipe", "pipe"]
|
|
30366
30845
|
}).trim();
|
|
@@ -30641,7 +31120,7 @@ var init_accounts = __esm(() => {
|
|
|
30641
31120
|
});
|
|
30642
31121
|
|
|
30643
31122
|
// src/auth/manager.ts
|
|
30644
|
-
import { execFileSync as
|
|
31123
|
+
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
30645
31124
|
import {
|
|
30646
31125
|
readFileSync as readFileSync22,
|
|
30647
31126
|
readdirSync as readdirSync11,
|
|
@@ -30692,14 +31171,14 @@ function authSessionName(name, slot) {
|
|
|
30692
31171
|
return `${base}-${slot.replace(/[^a-zA-Z0-9_.-]/g, "-")}`;
|
|
30693
31172
|
}
|
|
30694
31173
|
function tmux(args) {
|
|
30695
|
-
return
|
|
31174
|
+
return execFileSync13("tmux", args, {
|
|
30696
31175
|
encoding: "utf-8",
|
|
30697
31176
|
stdio: ["pipe", "pipe", "pipe"]
|
|
30698
31177
|
}).trim();
|
|
30699
31178
|
}
|
|
30700
31179
|
function tmuxSessionExists(sessionName) {
|
|
30701
31180
|
try {
|
|
30702
|
-
|
|
31181
|
+
execFileSync13("tmux", ["has-session", "-t", sessionName], {
|
|
30703
31182
|
stdio: ["pipe", "pipe", "pipe"]
|
|
30704
31183
|
});
|
|
30705
31184
|
return true;
|
|
@@ -31215,355 +31694,6 @@ function readQuarantineMarkerForAgent(agentsDir, name) {
|
|
|
31215
31694
|
var QUARANTINE_FILENAME = "quarantine.json";
|
|
31216
31695
|
var init_quarantine = () => {};
|
|
31217
31696
|
|
|
31218
|
-
// src/agents/pane-lock.ts
|
|
31219
|
-
function withPaneLock(key, fn) {
|
|
31220
|
-
const tail = tails.get(key) ?? Promise.resolve();
|
|
31221
|
-
const run = tail.then(() => fn());
|
|
31222
|
-
const next = run.then(() => {}, () => {});
|
|
31223
|
-
tails.set(key, next);
|
|
31224
|
-
next.then(() => {
|
|
31225
|
-
if (tails.get(key) === next)
|
|
31226
|
-
tails.delete(key);
|
|
31227
|
-
});
|
|
31228
|
-
return run;
|
|
31229
|
-
}
|
|
31230
|
-
var tails;
|
|
31231
|
-
var init_pane_lock = __esm(() => {
|
|
31232
|
-
tails = new Map;
|
|
31233
|
-
});
|
|
31234
|
-
|
|
31235
|
-
// src/agents/inject.ts
|
|
31236
|
-
var exports_inject = {};
|
|
31237
|
-
__export(exports_inject, {
|
|
31238
|
-
validateInjectCommand: () => validateInjectCommand,
|
|
31239
|
-
normalizeInjectCommand: () => normalizeInjectCommand,
|
|
31240
|
-
makeTmuxRunner: () => makeTmuxRunner,
|
|
31241
|
-
isTuiChromeLine: () => isTuiChromeLine,
|
|
31242
|
-
injectSlashCommandWith: () => injectSlashCommandWith,
|
|
31243
|
-
injectSlashCommand: () => injectSlashCommand,
|
|
31244
|
-
diffPane: () => diffPane,
|
|
31245
|
-
dedupeInjectQueue: () => dedupeInjectQueue,
|
|
31246
|
-
InjectError: () => InjectError,
|
|
31247
|
-
INJECT_COMMANDS: () => INJECT_COMMANDS,
|
|
31248
|
-
INJECT_BLOCKLIST: () => INJECT_BLOCKLIST,
|
|
31249
|
-
INJECT_BLOCKED: () => INJECT_BLOCKED,
|
|
31250
|
-
INJECT_ALLOWLIST: () => INJECT_ALLOWLIST
|
|
31251
|
-
});
|
|
31252
|
-
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
31253
|
-
function validateInjectCommand(command) {
|
|
31254
|
-
if (typeof command !== "string" || command.trim().length === 0) {
|
|
31255
|
-
throw new InjectError("invalid", "command is empty");
|
|
31256
|
-
}
|
|
31257
|
-
const trimmed = command.trim();
|
|
31258
|
-
if (!trimmed.startsWith("/")) {
|
|
31259
|
-
throw new InjectError("invalid", `command must start with "/": got ${trimmed}`);
|
|
31260
|
-
}
|
|
31261
|
-
const bare = trimmed.split(/\s+/, 1)[0].toLowerCase();
|
|
31262
|
-
const blockedMeta = INJECT_BLOCKED.get(bare);
|
|
31263
|
-
if (blockedMeta) {
|
|
31264
|
-
throw new InjectError("blocked", `${bare} is explicitly blocked from inject (${blockedMeta.reason}).`);
|
|
31265
|
-
}
|
|
31266
|
-
if (!INJECT_COMMANDS.has(bare)) {
|
|
31267
|
-
const allowed = [...INJECT_COMMANDS.keys()].sort().join(", ");
|
|
31268
|
-
throw new InjectError("not_allowed", `${bare} is not in the inject allowlist. Allowed: ${allowed}`);
|
|
31269
|
-
}
|
|
31270
|
-
return bare;
|
|
31271
|
-
}
|
|
31272
|
-
function normalizeInjectCommand(command) {
|
|
31273
|
-
return command.trim().replace(/\s+/g, " ").toLowerCase();
|
|
31274
|
-
}
|
|
31275
|
-
function dedupeInjectQueue(commands) {
|
|
31276
|
-
const seen = new Set;
|
|
31277
|
-
const out = [];
|
|
31278
|
-
for (const raw of commands) {
|
|
31279
|
-
if (typeof raw !== "string")
|
|
31280
|
-
continue;
|
|
31281
|
-
const key = normalizeInjectCommand(raw);
|
|
31282
|
-
if (key.length === 0)
|
|
31283
|
-
continue;
|
|
31284
|
-
if (seen.has(key))
|
|
31285
|
-
continue;
|
|
31286
|
-
seen.add(key);
|
|
31287
|
-
out.push(raw.trim());
|
|
31288
|
-
}
|
|
31289
|
-
return out;
|
|
31290
|
-
}
|
|
31291
|
-
function defaultSocketName(agentName) {
|
|
31292
|
-
return `switchroom-${agentName}`;
|
|
31293
|
-
}
|
|
31294
|
-
function makeTmuxRunner(tmuxBin) {
|
|
31295
|
-
return {
|
|
31296
|
-
capture(socket, session) {
|
|
31297
|
-
try {
|
|
31298
|
-
return execFileSync14(tmuxBin, ["-L", socket, "capture-pane", "-p", "-t", session, "-S", "-200"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
31299
|
-
} catch {
|
|
31300
|
-
return null;
|
|
31301
|
-
}
|
|
31302
|
-
},
|
|
31303
|
-
send(socket, session, args) {
|
|
31304
|
-
const [subcmd, ...rest] = args;
|
|
31305
|
-
const flagEnd = rest.findIndex((a) => !a.startsWith("-"));
|
|
31306
|
-
const flagsBeforeKeys = flagEnd === -1 ? rest : rest.slice(0, flagEnd);
|
|
31307
|
-
const keys = flagEnd === -1 ? [] : rest.slice(flagEnd);
|
|
31308
|
-
execFileSync14(tmuxBin, ["-L", socket, subcmd, ...flagsBeforeKeys, "-t", session, ...keys], { stdio: ["pipe", "pipe", "pipe"] });
|
|
31309
|
-
},
|
|
31310
|
-
hasSession(socket, session) {
|
|
31311
|
-
try {
|
|
31312
|
-
execFileSync14(tmuxBin, ["-L", socket, "has-session", "-t", session], {
|
|
31313
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
31314
|
-
});
|
|
31315
|
-
return true;
|
|
31316
|
-
} catch {
|
|
31317
|
-
return false;
|
|
31318
|
-
}
|
|
31319
|
-
}
|
|
31320
|
-
};
|
|
31321
|
-
}
|
|
31322
|
-
function sleep2(ms) {
|
|
31323
|
-
return new Promise((res) => setTimeout(res, ms));
|
|
31324
|
-
}
|
|
31325
|
-
function isTuiChromeLine(line) {
|
|
31326
|
-
const t = line.trim();
|
|
31327
|
-
if (t.length === 0)
|
|
31328
|
-
return false;
|
|
31329
|
-
if (/^[\s\-_\u2502\u2500\u256d\u256e\u2570\u256f\u250c\u2510\u2514\u2518|]+$/.test(t))
|
|
31330
|
-
return true;
|
|
31331
|
-
if (/^[\u256d\u256e\u2570\u256f\u2502\s]*[>)\u276f][\u256d\u256e\u2570\u256f\u2502\s]*$/.test(t))
|
|
31332
|
-
return true;
|
|
31333
|
-
if (/accept edits on/i.test(t))
|
|
31334
|
-
return true;
|
|
31335
|
-
if (/shift\+tab to cycle/i.test(t))
|
|
31336
|
-
return true;
|
|
31337
|
-
if (/for agents/i.test(t))
|
|
31338
|
-
return true;
|
|
31339
|
-
if (/\? for shortcuts/i.test(t))
|
|
31340
|
-
return true;
|
|
31341
|
-
if (/bypassing permissions/i.test(t))
|
|
31342
|
-
return true;
|
|
31343
|
-
if (t === "copy")
|
|
31344
|
-
return true;
|
|
31345
|
-
return false;
|
|
31346
|
-
}
|
|
31347
|
-
function diffPane(before, after, command) {
|
|
31348
|
-
if (command) {
|
|
31349
|
-
const escaped = command.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
31350
|
-
const lines = after.split(`
|
|
31351
|
-
`);
|
|
31352
|
-
let anchorIdx = -1;
|
|
31353
|
-
for (let i = lines.length - 1;i >= 0; i--) {
|
|
31354
|
-
if (/[\u276f>]\s+/.test(lines[i]) && lines[i].includes(escaped)) {
|
|
31355
|
-
anchorIdx = i;
|
|
31356
|
-
break;
|
|
31357
|
-
}
|
|
31358
|
-
}
|
|
31359
|
-
if (anchorIdx >= 0) {
|
|
31360
|
-
const tail = lines.slice(anchorIdx + 1);
|
|
31361
|
-
const trimmed = [];
|
|
31362
|
-
for (const raw of tail) {
|
|
31363
|
-
const line = raw.trimEnd();
|
|
31364
|
-
if (line.length === 0 && trimmed.length === 0)
|
|
31365
|
-
continue;
|
|
31366
|
-
if (isTuiChromeLine(line))
|
|
31367
|
-
continue;
|
|
31368
|
-
trimmed.push(line);
|
|
31369
|
-
}
|
|
31370
|
-
while (trimmed.length > 0 && trimmed[trimmed.length - 1].length === 0) {
|
|
31371
|
-
trimmed.pop();
|
|
31372
|
-
}
|
|
31373
|
-
const affordances = /^(esc to cancel|press any key|\u21b5 select)/i;
|
|
31374
|
-
while (trimmed.length > 0 && affordances.test(trimmed[trimmed.length - 1].trim())) {
|
|
31375
|
-
trimmed.pop();
|
|
31376
|
-
}
|
|
31377
|
-
if (trimmed.length > 0) {
|
|
31378
|
-
return { output: trimmed.join(`
|
|
31379
|
-
`), anchored: true };
|
|
31380
|
-
}
|
|
31381
|
-
}
|
|
31382
|
-
}
|
|
31383
|
-
const beforeSet = new Set(before.split(`
|
|
31384
|
-
`).map((l) => l.trimEnd()));
|
|
31385
|
-
const newLines = [];
|
|
31386
|
-
for (const raw of after.split(`
|
|
31387
|
-
`)) {
|
|
31388
|
-
const line = raw.trimEnd();
|
|
31389
|
-
if (line.length === 0)
|
|
31390
|
-
continue;
|
|
31391
|
-
if (beforeSet.has(line))
|
|
31392
|
-
continue;
|
|
31393
|
-
if (isTuiChromeLine(line))
|
|
31394
|
-
continue;
|
|
31395
|
-
newLines.push(line);
|
|
31396
|
-
}
|
|
31397
|
-
return { output: newLines.join(`
|
|
31398
|
-
`), anchored: false };
|
|
31399
|
-
}
|
|
31400
|
-
async function injectSlashCommand(agentName, command, opts = {}) {
|
|
31401
|
-
validateInjectCommand(command);
|
|
31402
|
-
const tmuxBin = opts.tmuxBin ?? "tmux";
|
|
31403
|
-
const socket = opts.socketName ?? defaultSocketName(agentName);
|
|
31404
|
-
const session = opts.sessionName ?? agentName;
|
|
31405
|
-
const settleMs = opts.settleMs ?? 2000;
|
|
31406
|
-
const timeoutMs = opts.timeoutMs ?? 5000;
|
|
31407
|
-
return withPaneLock(`${socket}:${session}`, () => injectSlashCommandWith(makeTmuxRunner(tmuxBin), {
|
|
31408
|
-
socket,
|
|
31409
|
-
session,
|
|
31410
|
-
command: command.trim(),
|
|
31411
|
-
settleMs,
|
|
31412
|
-
timeoutMs
|
|
31413
|
-
}));
|
|
31414
|
-
}
|
|
31415
|
-
async function injectSlashCommandWith(runner, args) {
|
|
31416
|
-
const { socket, session, command, settleMs, timeoutMs } = args;
|
|
31417
|
-
let bareVerb;
|
|
31418
|
-
try {
|
|
31419
|
-
bareVerb = validateInjectCommand(command);
|
|
31420
|
-
} catch (err) {
|
|
31421
|
-
if (err instanceof InjectError) {
|
|
31422
|
-
return {
|
|
31423
|
-
outcome: "failed",
|
|
31424
|
-
output: "",
|
|
31425
|
-
truncated: false,
|
|
31426
|
-
command: command.trim().split(/\s+/, 1)[0]?.toLowerCase() ?? "",
|
|
31427
|
-
meta: null,
|
|
31428
|
-
errorCode: err.code,
|
|
31429
|
-
errorMessage: err.message
|
|
31430
|
-
};
|
|
31431
|
-
}
|
|
31432
|
-
throw err;
|
|
31433
|
-
}
|
|
31434
|
-
const meta = INJECT_COMMANDS.get(bareVerb) ?? null;
|
|
31435
|
-
if (!runner.hasSession(socket, session)) {
|
|
31436
|
-
return {
|
|
31437
|
-
outcome: "failed",
|
|
31438
|
-
output: "",
|
|
31439
|
-
truncated: false,
|
|
31440
|
-
command: bareVerb,
|
|
31441
|
-
meta,
|
|
31442
|
-
errorCode: "session_missing",
|
|
31443
|
-
errorMessage: `tmux session "${session}" on socket "${socket}" not found. ` + `Is the agent running under the tmux supervisor (the default)? ` + `If experimental.legacy_pty=true is set, inject is unsupported.`
|
|
31444
|
-
};
|
|
31445
|
-
}
|
|
31446
|
-
const before = runner.capture(socket, session) ?? "";
|
|
31447
|
-
try {
|
|
31448
|
-
runner.send(socket, session, ["send-keys", "-l", command]);
|
|
31449
|
-
runner.send(socket, session, ["send-keys", "Enter"]);
|
|
31450
|
-
} catch (err) {
|
|
31451
|
-
return {
|
|
31452
|
-
outcome: "failed",
|
|
31453
|
-
output: "",
|
|
31454
|
-
truncated: false,
|
|
31455
|
-
command: bareVerb,
|
|
31456
|
-
meta,
|
|
31457
|
-
errorCode: "tmux_failed",
|
|
31458
|
-
errorMessage: `tmux send-keys failed: ${err instanceof Error ? err.message : String(err)}`
|
|
31459
|
-
};
|
|
31460
|
-
}
|
|
31461
|
-
const start = Date.now();
|
|
31462
|
-
let last = before;
|
|
31463
|
-
let stableSince = null;
|
|
31464
|
-
while (Date.now() - start < timeoutMs) {
|
|
31465
|
-
await sleep2(POLL_INTERVAL_MS);
|
|
31466
|
-
const cur = runner.capture(socket, session) ?? "";
|
|
31467
|
-
if (cur === last && cur !== before) {
|
|
31468
|
-
if (stableSince === null) {
|
|
31469
|
-
stableSince = Date.now();
|
|
31470
|
-
} else if (Date.now() - stableSince >= POLL_INTERVAL_MS) {
|
|
31471
|
-
last = cur;
|
|
31472
|
-
break;
|
|
31473
|
-
}
|
|
31474
|
-
} else {
|
|
31475
|
-
stableSince = null;
|
|
31476
|
-
}
|
|
31477
|
-
last = cur;
|
|
31478
|
-
if (Date.now() - start >= settleMs && cur !== before) {
|
|
31479
|
-
break;
|
|
31480
|
-
}
|
|
31481
|
-
}
|
|
31482
|
-
const { output: rawOutput, anchored } = diffPane(before, last, command);
|
|
31483
|
-
let output = rawOutput;
|
|
31484
|
-
let truncated = false;
|
|
31485
|
-
const bytes = Buffer.byteLength(output, "utf-8");
|
|
31486
|
-
if (bytes > OUTPUT_BYTE_CAP) {
|
|
31487
|
-
while (Buffer.byteLength(output, "utf-8") > OUTPUT_BYTE_CAP) {
|
|
31488
|
-
output = output.slice(Math.floor(output.length * 0.1) + 1);
|
|
31489
|
-
}
|
|
31490
|
-
truncated = true;
|
|
31491
|
-
}
|
|
31492
|
-
if (meta?.dialog) {
|
|
31493
|
-
try {
|
|
31494
|
-
runner.send(socket, session, ["send-keys", "Escape"]);
|
|
31495
|
-
} catch {}
|
|
31496
|
-
}
|
|
31497
|
-
if (output.trim().length === 0) {
|
|
31498
|
-
return {
|
|
31499
|
-
outcome: "ok_no_output",
|
|
31500
|
-
output: "",
|
|
31501
|
-
truncated: false,
|
|
31502
|
-
command: bareVerb,
|
|
31503
|
-
meta,
|
|
31504
|
-
...anchored ? {} : { diagnostic: "anchor_missing" }
|
|
31505
|
-
};
|
|
31506
|
-
}
|
|
31507
|
-
return {
|
|
31508
|
-
outcome: "ok",
|
|
31509
|
-
output,
|
|
31510
|
-
truncated,
|
|
31511
|
-
command: bareVerb,
|
|
31512
|
-
meta,
|
|
31513
|
-
...truncated ? { diagnostic: "truncated_output" } : {}
|
|
31514
|
-
};
|
|
31515
|
-
}
|
|
31516
|
-
var INJECT_COMMANDS, INJECT_ALLOWLIST, INJECT_BLOCKED, INJECT_BLOCKLIST, InjectError, POLL_INTERVAL_MS = 150, OUTPUT_BYTE_CAP = 3000;
|
|
31517
|
-
var init_inject = __esm(() => {
|
|
31518
|
-
init_pane_lock();
|
|
31519
|
-
INJECT_COMMANDS = new Map([
|
|
31520
|
-
["/cost", { description: "Show session cost", expectsOutput: true, dialog: true }],
|
|
31521
|
-
["/status", { description: "Show session status", expectsOutput: true, dialog: true }],
|
|
31522
|
-
["/usage", { description: "Show plan quota", expectsOutput: true, dialog: true }],
|
|
31523
|
-
["/hooks", { description: "List configured hooks", expectsOutput: true, dialog: true }],
|
|
31524
|
-
["/memory", { description: "Open memory picker", expectsOutput: true, dialog: true }],
|
|
31525
|
-
["/model", { description: "Open model picker", expectsOutput: true }],
|
|
31526
|
-
[
|
|
31527
|
-
"/clear",
|
|
31528
|
-
{
|
|
31529
|
-
description: "Clear session screen",
|
|
31530
|
-
expectsOutput: false,
|
|
31531
|
-
silentNote: "context cleared \u2014 fresh slate"
|
|
31532
|
-
}
|
|
31533
|
-
],
|
|
31534
|
-
[
|
|
31535
|
-
"/compact",
|
|
31536
|
-
{
|
|
31537
|
-
description: "Compact conversation history",
|
|
31538
|
-
expectsOutput: false,
|
|
31539
|
-
silentNote: "compaction runs silently"
|
|
31540
|
-
}
|
|
31541
|
-
]
|
|
31542
|
-
]);
|
|
31543
|
-
INJECT_ALLOWLIST = new Set(INJECT_COMMANDS.keys());
|
|
31544
|
-
INJECT_BLOCKED = new Map([
|
|
31545
|
-
["/login", { reason: "would mutate auth state" }],
|
|
31546
|
-
["/logout", { reason: "would terminate the agent's auth session" }],
|
|
31547
|
-
["/exit", { reason: "would kill the agent process" }],
|
|
31548
|
-
["/quit", { reason: "would kill the agent process" }],
|
|
31549
|
-
[
|
|
31550
|
-
"/effort",
|
|
31551
|
-
{
|
|
31552
|
-
reason: "leaves a blocking confirmation modal open and wedges the pane; use the /effort command (it drives the modal), not raw inject"
|
|
31553
|
-
}
|
|
31554
|
-
]
|
|
31555
|
-
]);
|
|
31556
|
-
INJECT_BLOCKLIST = new Set(INJECT_BLOCKED.keys());
|
|
31557
|
-
InjectError = class InjectError extends Error {
|
|
31558
|
-
code;
|
|
31559
|
-
constructor(code, message) {
|
|
31560
|
-
super(message);
|
|
31561
|
-
this.name = "InjectError";
|
|
31562
|
-
this.code = code;
|
|
31563
|
-
}
|
|
31564
|
-
};
|
|
31565
|
-
});
|
|
31566
|
-
|
|
31567
31697
|
// src/auth/broker/protocol.ts
|
|
31568
31698
|
function encodeRequest2(req) {
|
|
31569
31699
|
const line = JSON.stringify(RequestSchema2.parse(req)) + `
|
|
@@ -35392,7 +35522,7 @@ function formatForCli(entries, opts = {}) {
|
|
|
35392
35522
|
var init_audit_reader = () => {};
|
|
35393
35523
|
|
|
35394
35524
|
// node_modules/.bun/posthog-node@5.29.2/node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
|
|
35395
|
-
import { dirname as
|
|
35525
|
+
import { dirname as dirname13, posix, sep as sep2 } from "path";
|
|
35396
35526
|
function createModulerModifier() {
|
|
35397
35527
|
const getModuleFromFileName = createGetModuleFromFilename();
|
|
35398
35528
|
return async (frames) => {
|
|
@@ -35401,7 +35531,7 @@ function createModulerModifier() {
|
|
|
35401
35531
|
return frames;
|
|
35402
35532
|
};
|
|
35403
35533
|
}
|
|
35404
|
-
function createGetModuleFromFilename(basePath = process.argv[1] ?
|
|
35534
|
+
function createGetModuleFromFilename(basePath = process.argv[1] ? dirname13(process.argv[1]) : process.cwd(), isWindows = sep2 === "\\") {
|
|
35405
35535
|
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
35406
35536
|
return (filename) => {
|
|
35407
35537
|
if (!filename)
|
|
@@ -40017,7 +40147,7 @@ import {
|
|
|
40017
40147
|
readFileSync as readFileSync43,
|
|
40018
40148
|
writeFileSync as writeFileSync23
|
|
40019
40149
|
} from "node:fs";
|
|
40020
|
-
import { dirname as
|
|
40150
|
+
import { dirname as dirname14 } from "node:path";
|
|
40021
40151
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
40022
40152
|
function telemetryDisabled() {
|
|
40023
40153
|
const v = process.env.SWITCHROOM_TELEMETRY_DISABLED;
|
|
@@ -40039,7 +40169,7 @@ function getDistinctId() {
|
|
|
40039
40169
|
const id = randomUUID3();
|
|
40040
40170
|
cachedDistinctId = id;
|
|
40041
40171
|
try {
|
|
40042
|
-
mkdirSync26(
|
|
40172
|
+
mkdirSync26(dirname14(path4), { recursive: true });
|
|
40043
40173
|
writeFileSync23(path4, id, "utf-8");
|
|
40044
40174
|
} catch {}
|
|
40045
40175
|
return id;
|
|
@@ -40286,7 +40416,7 @@ import {
|
|
|
40286
40416
|
readFileSync as readFileSync52,
|
|
40287
40417
|
readdirSync as readdirSync19
|
|
40288
40418
|
} from "node:fs";
|
|
40289
|
-
import { dirname as
|
|
40419
|
+
import { dirname as dirname17, join as join50 } from "node:path";
|
|
40290
40420
|
import { execSync as execSync2 } from "node:child_process";
|
|
40291
40421
|
function locateManifestPath() {
|
|
40292
40422
|
let dir = import.meta.dirname;
|
|
@@ -40294,7 +40424,7 @@ function locateManifestPath() {
|
|
|
40294
40424
|
const candidate = join50(dir, "dependencies.json");
|
|
40295
40425
|
if (existsSync57(candidate))
|
|
40296
40426
|
return candidate;
|
|
40297
|
-
dir =
|
|
40427
|
+
dir = dirname17(dir);
|
|
40298
40428
|
}
|
|
40299
40429
|
return null;
|
|
40300
40430
|
}
|
|
@@ -43465,7 +43595,7 @@ import {
|
|
|
43465
43595
|
readdirSync as readdirSync21,
|
|
43466
43596
|
statSync as statSync26
|
|
43467
43597
|
} from "node:fs";
|
|
43468
|
-
import { dirname as
|
|
43598
|
+
import { dirname as dirname18, join as join62, resolve as resolve37 } from "node:path";
|
|
43469
43599
|
import { createPublicKey, createPrivateKey } from "node:crypto";
|
|
43470
43600
|
function findInNvm(bin) {
|
|
43471
43601
|
const nvmRoot = join62(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
@@ -44947,7 +45077,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
|
|
|
44947
45077
|
detail: "skipped (MFF_API_URL not set)"
|
|
44948
45078
|
};
|
|
44949
45079
|
}
|
|
44950
|
-
const credDir =
|
|
45080
|
+
const credDir = dirname18(envPath);
|
|
44951
45081
|
const authScript = join62(credDir, "claude-auth.py");
|
|
44952
45082
|
if (!existsSync62(authScript)) {
|
|
44953
45083
|
return {
|
|
@@ -48870,7 +49000,7 @@ __export(exports_voice_sidecar_token, {
|
|
|
48870
49000
|
});
|
|
48871
49001
|
import { randomBytes as randomBytes14 } from "node:crypto";
|
|
48872
49002
|
import { chmodSync as chmodSync11, chownSync as chownSync7, existsSync as existsSync81, mkdirSync as mkdirSync45, readFileSync as readFileSync68, rmSync as rmSync17, writeFileSync as writeFileSync37 } from "node:fs";
|
|
48873
|
-
import { dirname as
|
|
49003
|
+
import { dirname as dirname27 } from "node:path";
|
|
48874
49004
|
async function defaultResolveOrSeedToken(home2, writeErr) {
|
|
48875
49005
|
const [{ getViaBrokerStructured: getViaBrokerStructured2, putViaBroker: putViaBroker2 }, { resolveOperatorVaultPassphrase }] = await Promise.all([
|
|
48876
49006
|
Promise.resolve().then(() => (init_client(), exports_client)),
|
|
@@ -48924,7 +49054,7 @@ async function provisionVoiceSidecarToken(composePath, home2, ctx) {
|
|
|
48924
49054
|
if (!token)
|
|
48925
49055
|
return;
|
|
48926
49056
|
try {
|
|
48927
|
-
mkdirSync45(
|
|
49057
|
+
mkdirSync45(dirname27(envPath), { recursive: true });
|
|
48928
49058
|
let body = "";
|
|
48929
49059
|
try {
|
|
48930
49060
|
if (existsSync81(envPath))
|
|
@@ -48986,11 +49116,11 @@ __export(exports_apply, {
|
|
|
48986
49116
|
DEFAULT_COMPOSE_PATH: () => DEFAULT_COMPOSE_PATH2,
|
|
48987
49117
|
COMPOSE_PROJECT: () => COMPOSE_PROJECT2
|
|
48988
49118
|
});
|
|
48989
|
-
import { accessSync as accessSync3, chownSync as chownSync8, constants as fsConstants6, copyFileSync as
|
|
49119
|
+
import { accessSync as accessSync3, chownSync as chownSync8, constants as fsConstants6, copyFileSync as copyFileSync12, existsSync as existsSync82, mkdirSync as mkdirSync46, readFileSync as readFileSync69, readdirSync as readdirSync28, renameSync as renameSync18, writeFileSync as writeFileSync38 } from "node:fs";
|
|
48990
49120
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
48991
49121
|
import { spawnSync as childSpawnSync } from "node:child_process";
|
|
48992
49122
|
import readline from "node:readline";
|
|
48993
|
-
import { dirname as
|
|
49123
|
+
import { dirname as dirname28, join as join80, resolve as resolve50 } from "node:path";
|
|
48994
49124
|
import { homedir as homedir47 } from "node:os";
|
|
48995
49125
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
48996
49126
|
function effectiveLiteLLMEnabled(config, agentResolvedLitellm) {
|
|
@@ -49235,7 +49365,7 @@ async function provisionLiteLLMKeys(config, agentNames, switchroomConfigPath, ct
|
|
|
49235
49365
|
function resolveVaultBindMountDir(homeDir, ctx) {
|
|
49236
49366
|
const isCustomPath = ctx.migrationKind === "custom-path-skipped";
|
|
49237
49367
|
if (isCustomPath && ctx.customVaultPath) {
|
|
49238
|
-
return
|
|
49368
|
+
return dirname28(ctx.customVaultPath);
|
|
49239
49369
|
}
|
|
49240
49370
|
return join80(homeDir, ".switchroom", "vault");
|
|
49241
49371
|
}
|
|
@@ -49890,7 +50020,7 @@ function copyExampleConfig2(name) {
|
|
|
49890
50020
|
if (!existsSync82(exampleFile)) {
|
|
49891
50021
|
throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
|
|
49892
50022
|
}
|
|
49893
|
-
|
|
50023
|
+
copyFileSync12(exampleFile, dest);
|
|
49894
50024
|
console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
|
|
49895
50025
|
}
|
|
49896
50026
|
function findUnwritableAgentDirs(config, opts) {
|
|
@@ -64437,7 +64567,7 @@ import {
|
|
|
64437
64567
|
mkdirSync as mkdirSync53,
|
|
64438
64568
|
writeFileSync as writeFileSync44
|
|
64439
64569
|
} from "node:fs";
|
|
64440
|
-
import { resolve as resolve55, dirname as
|
|
64570
|
+
import { resolve as resolve55, dirname as dirname31 } from "node:path";
|
|
64441
64571
|
import { homedir as homedir56 } from "node:os";
|
|
64442
64572
|
function resolveSwitchroomBase(home2 = process.env.SWITCHROOM_HOME ?? process.env.HOME ?? homedir56()) {
|
|
64443
64573
|
return resolve55(home2, ".switchroom");
|
|
@@ -64528,11 +64658,11 @@ function readLedgerIfPresent(base) {
|
|
|
64528
64658
|
}
|
|
64529
64659
|
}
|
|
64530
64660
|
function ledgerPathForBase(base) {
|
|
64531
|
-
return fleetHealthLedgerPath(
|
|
64661
|
+
return fleetHealthLedgerPath(dirname31(base));
|
|
64532
64662
|
}
|
|
64533
64663
|
function writeLedger(base, ledger) {
|
|
64534
64664
|
const path8 = ledgerPathForBase(base);
|
|
64535
|
-
mkdirSync53(
|
|
64665
|
+
mkdirSync53(dirname31(path8), { recursive: true });
|
|
64536
64666
|
writeFileSync44(path8, JSON.stringify(ledger, null, 2) + `
|
|
64537
64667
|
`, "utf-8");
|
|
64538
64668
|
return path8;
|
|
@@ -64552,8 +64682,8 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
64552
64682
|
import { dirname, join } from "node:path";
|
|
64553
64683
|
|
|
64554
64684
|
// src/build-info.ts
|
|
64555
|
-
var VERSION = "0.
|
|
64556
|
-
var COMMIT_SHA = "
|
|
64685
|
+
var VERSION = "0.18.6";
|
|
64686
|
+
var COMMIT_SHA = "b62d558a";
|
|
64557
64687
|
|
|
64558
64688
|
// src/cli/resolve-version.ts
|
|
64559
64689
|
function readPackageVersion() {
|
|
@@ -64767,11 +64897,11 @@ function detectInFlight(opts) {
|
|
|
64767
64897
|
async function waitUntilIdle(opts) {
|
|
64768
64898
|
const pollMs = opts.pollMs ?? 2000;
|
|
64769
64899
|
const clock = opts.now ?? Date.now;
|
|
64770
|
-
const
|
|
64900
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
64771
64901
|
const deadline = clock() + opts.timeoutMs;
|
|
64772
64902
|
let last = detectInFlight(opts);
|
|
64773
64903
|
while (last.busy && clock() < deadline) {
|
|
64774
|
-
await
|
|
64904
|
+
await sleep2(pollMs);
|
|
64775
64905
|
last = detectInFlight(opts);
|
|
64776
64906
|
}
|
|
64777
64907
|
return last;
|
|
@@ -64883,7 +65013,7 @@ function spinner(message) {
|
|
|
64883
65013
|
// src/agents/status.ts
|
|
64884
65014
|
import { existsSync as existsSync23, readFileSync as readFileSync20, statSync as statSync11 } from "node:fs";
|
|
64885
65015
|
import { join as join16 } from "node:path";
|
|
64886
|
-
import { execFileSync as
|
|
65016
|
+
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
64887
65017
|
|
|
64888
65018
|
// src/agents/handoff-summarizer.ts
|
|
64889
65019
|
import { readFileSync as readFileSync18, writeFileSync as writeFileSync8, renameSync as renameSync6, mkdirSync as mkdirSync15, existsSync as existsSync21, statSync as statSync10, readdirSync as readdirSync9 } from "node:fs";
|
|
@@ -65468,7 +65598,7 @@ async function buildAgentStatusReport(inputs) {
|
|
|
65468
65598
|
async function waitForAgentReady(inputs, options = {}) {
|
|
65469
65599
|
const timeoutMs = options.timeoutMs ?? 30000;
|
|
65470
65600
|
const pollIntervalMs = options.pollIntervalMs ?? 750;
|
|
65471
|
-
const
|
|
65601
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
65472
65602
|
const now = options.now ?? Date.now;
|
|
65473
65603
|
const startedAt = now();
|
|
65474
65604
|
const deadline = startedAt + timeoutMs;
|
|
@@ -65483,7 +65613,7 @@ async function waitForAgentReady(inputs, options = {}) {
|
|
|
65483
65613
|
notReady
|
|
65484
65614
|
};
|
|
65485
65615
|
}
|
|
65486
|
-
await
|
|
65616
|
+
await sleep2(pollIntervalMs);
|
|
65487
65617
|
report = await buildAgentStatusReport(inputs);
|
|
65488
65618
|
notReady = readinessGaps(report);
|
|
65489
65619
|
}
|
|
@@ -65560,14 +65690,14 @@ function readLastMessages(historyDbPath) {
|
|
|
65560
65690
|
};
|
|
65561
65691
|
}
|
|
65562
65692
|
try {
|
|
65563
|
-
const out =
|
|
65693
|
+
const out = execFileSync11("bun", [
|
|
65564
65694
|
"-e",
|
|
65565
65695
|
`const { Database } = require("bun:sqlite"); ` + `const db = new Database(${JSON.stringify(historyDbPath)}, { readonly: true }); ` + `const rows = db.prepare("SELECT role, MAX(ts) as ts FROM messages GROUP BY role").all(); ` + `for (const r of rows) console.log(r.role + "|" + r.ts); ` + `db.close();`
|
|
65566
65696
|
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
65567
65697
|
return parseSqliteRoleTsOutput(out);
|
|
65568
65698
|
} catch {}
|
|
65569
65699
|
try {
|
|
65570
|
-
const out =
|
|
65700
|
+
const out = execFileSync11("sqlite3", [
|
|
65571
65701
|
historyDbPath,
|
|
65572
65702
|
"SELECT role, MAX(ts) FROM messages GROUP BY role;"
|
|
65573
65703
|
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
@@ -65679,7 +65809,7 @@ function escapeRegex(s) {
|
|
|
65679
65809
|
}
|
|
65680
65810
|
function readDockerContainer(containerName2) {
|
|
65681
65811
|
try {
|
|
65682
|
-
const out =
|
|
65812
|
+
const out = execFileSync11("docker", ["inspect", "--format", "{{json .State}}", containerName2], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
|
|
65683
65813
|
const state = JSON.parse(out.trim());
|
|
65684
65814
|
const active = state.Status === "running" ? "active" : state.Status === "restarting" ? "restarting" : "inactive";
|
|
65685
65815
|
const pid = typeof state.Pid === "number" && state.Pid > 0 ? state.Pid : null;
|
|
@@ -65794,7 +65924,7 @@ async function pollForDmStart(token, timeoutMs = 120000) {
|
|
|
65794
65924
|
const response = await fetch(url);
|
|
65795
65925
|
data = await response.json();
|
|
65796
65926
|
} catch {
|
|
65797
|
-
await
|
|
65927
|
+
await sleep2(2000);
|
|
65798
65928
|
continue;
|
|
65799
65929
|
}
|
|
65800
65930
|
if (data.ok && Array.isArray(data.result)) {
|
|
@@ -65810,11 +65940,11 @@ async function pollForDmStart(token, timeoutMs = 120000) {
|
|
|
65810
65940
|
}
|
|
65811
65941
|
}
|
|
65812
65942
|
}
|
|
65813
|
-
await
|
|
65943
|
+
await sleep2(2000);
|
|
65814
65944
|
}
|
|
65815
65945
|
throw new Error("Timed out waiting for /start DM");
|
|
65816
65946
|
}
|
|
65817
|
-
function
|
|
65947
|
+
function sleep2(ms) {
|
|
65818
65948
|
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
65819
65949
|
}
|
|
65820
65950
|
|
|
@@ -65958,7 +66088,7 @@ async function completeCreation(name, code, opts = {}) {
|
|
|
65958
66088
|
// src/agents/add-orchestrator.ts
|
|
65959
66089
|
import { resolve as resolve19, join as join19 } from "node:path";
|
|
65960
66090
|
import { existsSync as existsSync28, rmSync as rmSync8, statSync as statSync15 } from "node:fs";
|
|
65961
|
-
import { execFileSync as
|
|
66091
|
+
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
65962
66092
|
init_onboarding();
|
|
65963
66093
|
|
|
65964
66094
|
// src/setup/botfather-walkthrough.ts
|
|
@@ -66532,7 +66662,7 @@ function pruneBundledSkills(agentDir, keep, scope) {
|
|
|
66532
66662
|
}
|
|
66533
66663
|
function defaultIsUnitActive(unitName) {
|
|
66534
66664
|
try {
|
|
66535
|
-
const out =
|
|
66665
|
+
const out = execFileSync14("systemctl", ["--user", "is-active", unitName], {
|
|
66536
66666
|
encoding: "utf-8",
|
|
66537
66667
|
stdio: ["ignore", "pipe", "pipe"]
|
|
66538
66668
|
}).trim();
|
|
@@ -71386,7 +71516,7 @@ init_acl();
|
|
|
71386
71516
|
init_protocol();
|
|
71387
71517
|
import * as net3 from "node:net";
|
|
71388
71518
|
import { mkdirSync as mkdirSync24, chmodSync as chmodSync7, chownSync as chownSync4, existsSync as existsSync41, readFileSync as readFileSync34, readdirSync as readdirSync17, statSync as statSync21, unlinkSync as unlinkSync10, writeFileSync as writeFileSync18, renameSync as renameSync13 } from "node:fs";
|
|
71389
|
-
import { dirname as
|
|
71519
|
+
import { dirname as dirname12, resolve as resolve28, basename as basename7 } from "node:path";
|
|
71390
71520
|
import * as os4 from "node:os";
|
|
71391
71521
|
import * as path3 from "node:path";
|
|
71392
71522
|
|
|
@@ -71479,10 +71609,38 @@ function rotateAuditLog(logPath, maxFiles) {
|
|
|
71479
71609
|
return;
|
|
71480
71610
|
}
|
|
71481
71611
|
}
|
|
71612
|
+
const snapshotPath = `${logPath}.1`;
|
|
71482
71613
|
try {
|
|
71483
|
-
fs2.
|
|
71614
|
+
fs2.copyFileSync(logPath, snapshotPath);
|
|
71484
71615
|
} catch (err) {
|
|
71485
|
-
process.stderr.write(`[vault-audit] ERROR: could not
|
|
71616
|
+
process.stderr.write(`[vault-audit] ERROR: could not snapshot active audit log ${logPath} \u2192 ${snapshotPath}: ${err.message}
|
|
71617
|
+
`);
|
|
71618
|
+
return;
|
|
71619
|
+
}
|
|
71620
|
+
try {
|
|
71621
|
+
const fd = fs2.openSync(snapshotPath, "r");
|
|
71622
|
+
try {
|
|
71623
|
+
fs2.fsyncSync(fd);
|
|
71624
|
+
} finally {
|
|
71625
|
+
fs2.closeSync(fd);
|
|
71626
|
+
}
|
|
71627
|
+
} catch (err) {
|
|
71628
|
+
process.stderr.write(`[vault-audit] ERROR: could not fsync audit snapshot ${snapshotPath}; leaving active log intact to avoid data loss: ${err.message}
|
|
71629
|
+
`);
|
|
71630
|
+
return;
|
|
71631
|
+
}
|
|
71632
|
+
try {
|
|
71633
|
+
const dirFd = fs2.openSync(path.dirname(snapshotPath), "r");
|
|
71634
|
+
try {
|
|
71635
|
+
fs2.fsyncSync(dirFd);
|
|
71636
|
+
} finally {
|
|
71637
|
+
fs2.closeSync(dirFd);
|
|
71638
|
+
}
|
|
71639
|
+
} catch {}
|
|
71640
|
+
try {
|
|
71641
|
+
fs2.truncateSync(logPath, 0);
|
|
71642
|
+
} catch (err) {
|
|
71643
|
+
process.stderr.write(`[vault-audit] ERROR: could not truncate active audit log ${logPath}: ${err.message}
|
|
71486
71644
|
`);
|
|
71487
71645
|
}
|
|
71488
71646
|
}
|
|
@@ -73891,7 +74049,7 @@ class VaultBroker {
|
|
|
73891
74049
|
this.passphrase = this.testOpts._testPassphrase;
|
|
73892
74050
|
}
|
|
73893
74051
|
process.umask(63);
|
|
73894
|
-
const parentDir =
|
|
74052
|
+
const parentDir = dirname12(this.socketPath);
|
|
73895
74053
|
mkdirSync24(parentDir, { recursive: true, mode: 448 });
|
|
73896
74054
|
try {
|
|
73897
74055
|
chmodSync7(parentDir, 448);
|
|
@@ -74986,7 +75144,8 @@ class VaultBroker {
|
|
|
74986
75144
|
return;
|
|
74987
75145
|
}
|
|
74988
75146
|
try {
|
|
74989
|
-
const
|
|
75147
|
+
const agentsDir = this.config ? resolveAgentsDir(this.config) : path3.join(os4.homedir(), ".switchroom", "agents");
|
|
75148
|
+
const tokenDir = path3.join(agentsDir, agent);
|
|
74990
75149
|
mkdirSync24(tokenDir, { recursive: true });
|
|
74991
75150
|
const tokenPath = path3.join(tokenDir, ".vault-token");
|
|
74992
75151
|
const tmpPath = `${tokenPath}.tmp.${process.pid}`;
|
|
@@ -75050,7 +75209,8 @@ class VaultBroker {
|
|
|
75050
75209
|
try {
|
|
75051
75210
|
const row = this.grantsDb.query("SELECT agent_slug FROM vault_grants WHERE id = ?").get(id);
|
|
75052
75211
|
if (row && AgentNameSchema.safeParse(row.agent_slug).success) {
|
|
75053
|
-
const
|
|
75212
|
+
const agentsDir = this.config ? resolveAgentsDir(this.config) : path3.join(os4.homedir(), ".switchroom", "agents");
|
|
75213
|
+
const tokenPath = path3.join(agentsDir, row.agent_slug, ".vault-token");
|
|
75054
75214
|
if (existsSync41(tokenPath)) {
|
|
75055
75215
|
try {
|
|
75056
75216
|
unlinkSync10(tokenPath);
|
|
@@ -75370,15 +75530,15 @@ class VaultBroker {
|
|
|
75370
75530
|
}
|
|
75371
75531
|
}
|
|
75372
75532
|
function detectVaultLayoutDrift(vaultPath) {
|
|
75373
|
-
const dir =
|
|
75533
|
+
const dir = dirname12(vaultPath);
|
|
75374
75534
|
if (basename7(dir) !== "vault")
|
|
75375
75535
|
return;
|
|
75376
75536
|
if (basename7(vaultPath) !== "vault.enc")
|
|
75377
75537
|
return;
|
|
75378
|
-
const switchroomDir =
|
|
75538
|
+
const switchroomDir = dirname12(dir);
|
|
75379
75539
|
if (basename7(switchroomDir) !== ".switchroom")
|
|
75380
75540
|
return;
|
|
75381
|
-
const home2 =
|
|
75541
|
+
const home2 = dirname12(switchroomDir);
|
|
75382
75542
|
const result = inspectVaultLayout(home2);
|
|
75383
75543
|
if (result.kind === "divergent") {
|
|
75384
75544
|
throw new VaultError(`Vault layout divergence detected at boot: ${result.details.oldPath} and ${result.details.newPath} are both regular files with different content. An older switchroom CLI may have written to the legacy path after migration ran. Run \`switchroom apply\` from the host to surface the recovery recipe (state E refusal with literal \`mv\` commands). See docs/operators/state-e-recovery.md.`);
|
|
@@ -76410,7 +76570,7 @@ import {
|
|
|
76410
76570
|
appendFileSync as appendFileSync3,
|
|
76411
76571
|
closeSync as closeSync10,
|
|
76412
76572
|
existsSync as existsSync45,
|
|
76413
|
-
fsyncSync as
|
|
76573
|
+
fsyncSync as fsyncSync7,
|
|
76414
76574
|
mkdirSync as mkdirSync25,
|
|
76415
76575
|
openSync as openSync10,
|
|
76416
76576
|
readdirSync as readdirSync18,
|
|
@@ -76557,7 +76717,7 @@ function backupVault(opts) {
|
|
|
76557
76717
|
const fd = openSync10(tmpPath, "wx", 384);
|
|
76558
76718
|
try {
|
|
76559
76719
|
writeSync7(fd, src);
|
|
76560
|
-
|
|
76720
|
+
fsyncSync7(fd);
|
|
76561
76721
|
} finally {
|
|
76562
76722
|
closeSync10(fd);
|
|
76563
76723
|
}
|
|
@@ -76588,7 +76748,7 @@ function backupVault(opts) {
|
|
|
76588
76748
|
try {
|
|
76589
76749
|
const dirFd = openSync10(opts.destDir, "r");
|
|
76590
76750
|
try {
|
|
76591
|
-
|
|
76751
|
+
fsyncSync7(dirFd);
|
|
76592
76752
|
} finally {
|
|
76593
76753
|
closeSync10(dirFd);
|
|
76594
76754
|
}
|
|
@@ -76604,7 +76764,7 @@ function backupVault(opts) {
|
|
|
76604
76764
|
try {
|
|
76605
76765
|
const dirFd = openSync10(opts.destDir, "r");
|
|
76606
76766
|
try {
|
|
76607
|
-
|
|
76767
|
+
fsyncSync7(dirFd);
|
|
76608
76768
|
} finally {
|
|
76609
76769
|
closeSync10(dirFd);
|
|
76610
76770
|
}
|
|
@@ -78288,7 +78448,11 @@ async function resolveLiteLLMForHindsight(config) {
|
|
|
78288
78448
|
const result = await getViaBrokerStructured("litellm/hindsight/api-key").catch(() => null);
|
|
78289
78449
|
if (!result || result.kind !== "ok" || result.entry.kind !== "string")
|
|
78290
78450
|
return;
|
|
78291
|
-
return {
|
|
78451
|
+
return {
|
|
78452
|
+
baseUrl: topLiteLLM.base_url,
|
|
78453
|
+
apiKey: result.entry.value,
|
|
78454
|
+
model: config.memory?.config?.llm_model
|
|
78455
|
+
};
|
|
78292
78456
|
}
|
|
78293
78457
|
function readRecallLog(agentDir, limit) {
|
|
78294
78458
|
const path4 = join40(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
|
|
@@ -78552,8 +78716,9 @@ Cross-agent reflection plan
|
|
|
78552
78716
|
const provider = "claude-code";
|
|
78553
78717
|
console.log(source_default.gray(" Starting Hindsight Docker container..."));
|
|
78554
78718
|
try {
|
|
78555
|
-
const
|
|
78556
|
-
|
|
78719
|
+
const hindsightConfig = getConfig(program3);
|
|
78720
|
+
const litellmCfg = await resolveLiteLLMForHindsight(hindsightConfig);
|
|
78721
|
+
startHindsight(ports, litellmCfg, opts.tag, hindsightConfig.hindsight?.llm);
|
|
78557
78722
|
if (litellmCfg) {
|
|
78558
78723
|
console.log(source_default.gray(" LiteLLM routing enabled for hindsight (--network host)."));
|
|
78559
78724
|
}
|
|
@@ -78599,7 +78764,7 @@ Cross-agent reflection plan
|
|
|
78599
78764
|
console.log(source_default.bold(`
|
|
78600
78765
|
# Add this to your docker-compose.yml:
|
|
78601
78766
|
`));
|
|
78602
|
-
console.log(generateHindsightComposeSnippet());
|
|
78767
|
+
console.log(generateHindsightComposeSnippet(getConfig(program3).hindsight?.llm));
|
|
78603
78768
|
console.log();
|
|
78604
78769
|
});
|
|
78605
78770
|
memory.command("recall-log [agent]").description("Show recent auto-recall events (per-turn JSONL log) \u2014 see what was injected, when the cap fired, hit rate hints").option("-n, --limit <n>", "Tail the last N events per agent (default 20)", "20").option("--json", "Emit raw JSONL (one entry per line)").action(withConfigError(async (agent, opts) => {
|
|
@@ -78769,7 +78934,7 @@ import {
|
|
|
78769
78934
|
writeSync as writeSync8,
|
|
78770
78935
|
constants as fsConstants3
|
|
78771
78936
|
} from "node:fs";
|
|
78772
|
-
import { resolve as resolve32, extname, join as join49, relative, dirname as
|
|
78937
|
+
import { resolve as resolve32, extname, join as join49, relative, dirname as dirname15 } from "node:path";
|
|
78773
78938
|
import { homedir as homedir27 } from "node:os";
|
|
78774
78939
|
import { timingSafeEqual as timingSafeEqual3, randomBytes as randomBytes11 } from "node:crypto";
|
|
78775
78940
|
|
|
@@ -82045,7 +82210,7 @@ function resolveWebToken() {
|
|
|
82045
82210
|
return existing;
|
|
82046
82211
|
}
|
|
82047
82212
|
const token = randomBytes11(32).toString("hex");
|
|
82048
|
-
mkdirSync30(
|
|
82213
|
+
mkdirSync30(dirname15(tokenPath), { recursive: true, mode: 448 });
|
|
82049
82214
|
try {
|
|
82050
82215
|
const fd = openSync11(tokenPath, fsConstants3.O_WRONLY | fsConstants3.O_CREAT | fsConstants3.O_EXCL, 384);
|
|
82051
82216
|
try {
|
|
@@ -82803,8 +82968,8 @@ Starting Switchroom dashboard...
|
|
|
82803
82968
|
init_source();
|
|
82804
82969
|
init_loader();
|
|
82805
82970
|
init_scaffold();
|
|
82806
|
-
import { existsSync as existsSync56, copyFileSync as
|
|
82807
|
-
import { resolve as resolve33, dirname as
|
|
82971
|
+
import { existsSync as existsSync56, copyFileSync as copyFileSync9, readFileSync as readFileSync51, writeFileSync as writeFileSync26, mkdirSync as mkdirSync31 } from "node:fs";
|
|
82972
|
+
import { resolve as resolve33, dirname as dirname16 } from "node:path";
|
|
82808
82973
|
init_state();
|
|
82809
82974
|
init_vault();
|
|
82810
82975
|
init_manager();
|
|
@@ -83109,8 +83274,8 @@ async function copyExampleConfig(nonInteractive) {
|
|
|
83109
83274
|
if (!existsSync56(srcFile)) {
|
|
83110
83275
|
throw new ConfigError(`Example config not found: ${choice}.yaml`);
|
|
83111
83276
|
}
|
|
83112
|
-
mkdirSync31(
|
|
83113
|
-
|
|
83277
|
+
mkdirSync31(dirname16(destFile), { recursive: true });
|
|
83278
|
+
copyFileSync9(srcFile, destFile);
|
|
83114
83279
|
console.log(source_default.green(` Copied ${choice}.yaml -> ${destFile}`));
|
|
83115
83280
|
console.log(source_default.yellow(` Edit ${destFile} to customize, then re-run switchroom setup.`));
|
|
83116
83281
|
await writeDetectedTimezone(destFile, nonInteractive);
|
|
@@ -83403,7 +83568,11 @@ async function resolveLiteLLMForHindsight2(config) {
|
|
|
83403
83568
|
const result = await getViaBrokerStructured("litellm/hindsight/api-key").catch(() => null);
|
|
83404
83569
|
if (!result || result.kind !== "ok" || result.entry.kind !== "string")
|
|
83405
83570
|
return;
|
|
83406
|
-
return {
|
|
83571
|
+
return {
|
|
83572
|
+
baseUrl: topLiteLLM.base_url,
|
|
83573
|
+
apiKey: result.entry.value,
|
|
83574
|
+
model: config.memory?.config?.llm_model
|
|
83575
|
+
};
|
|
83407
83576
|
}
|
|
83408
83577
|
async function stepMemoryBackend(config, nonInteractive, switchroomConfigPath) {
|
|
83409
83578
|
stepHeader(6, "Memory backend", STEP_ACTIVE);
|
|
@@ -83495,7 +83664,7 @@ async function stepMemoryBackend(config, nonInteractive, switchroomConfigPath) {
|
|
|
83495
83664
|
const spin = spinner("Starting Hindsight Docker container...");
|
|
83496
83665
|
try {
|
|
83497
83666
|
const litellmCfg = await resolveLiteLLMForHindsight2(config);
|
|
83498
|
-
startHindsight(ports, litellmCfg);
|
|
83667
|
+
startHindsight(ports, litellmCfg, undefined, config.hindsight?.llm);
|
|
83499
83668
|
if (litellmCfg) {
|
|
83500
83669
|
console.log(source_default.gray(" LiteLLM routing enabled (--network host, ANTHROPIC_BASE_URL set)."));
|
|
83501
83670
|
}
|
|
@@ -83888,7 +84057,7 @@ init_lifecycle();
|
|
|
83888
84057
|
init_compose_env();
|
|
83889
84058
|
import { cpSync as cpSync2, existsSync as existsSync63, mkdirSync as mkdirSync33, readFileSync as readFileSync56, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync28, chownSync as chownSync5 } from "node:fs";
|
|
83890
84059
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
83891
|
-
import { join as join63, dirname as
|
|
84060
|
+
import { join as join63, dirname as dirname19, resolve as resolve38 } from "node:path";
|
|
83892
84061
|
import { homedir as homedir38 } from "node:os";
|
|
83893
84062
|
|
|
83894
84063
|
// src/cli/release-yaml.ts
|
|
@@ -83997,7 +84166,7 @@ function defaultPersistPin(configPath) {
|
|
|
83997
84166
|
}
|
|
83998
84167
|
var DEFAULT_COMPOSE_PATH = join63(homedir38(), ".switchroom", "compose", "docker-compose.yml");
|
|
83999
84168
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
84000
|
-
let dir =
|
|
84169
|
+
let dir = dirname19(scriptPath);
|
|
84001
84170
|
for (let i = 0;i < 12; i++) {
|
|
84002
84171
|
if (existsSync63(join63(dir, ".git"))) {
|
|
84003
84172
|
try {
|
|
@@ -84006,7 +84175,7 @@ function runningFromSwitchroomCheckout(scriptPath) {
|
|
|
84006
84175
|
return true;
|
|
84007
84176
|
} catch {}
|
|
84008
84177
|
}
|
|
84009
|
-
const parent =
|
|
84178
|
+
const parent = dirname19(dir);
|
|
84010
84179
|
if (parent === dir)
|
|
84011
84180
|
break;
|
|
84012
84181
|
dir = parent;
|
|
@@ -84222,7 +84391,7 @@ function planUpdate(opts) {
|
|
|
84222
84391
|
if (existsSync63(dest)) {
|
|
84223
84392
|
rmSync12(dest, { recursive: true, force: true });
|
|
84224
84393
|
}
|
|
84225
|
-
mkdirSync33(
|
|
84394
|
+
mkdirSync33(dirname19(dest), { recursive: true });
|
|
84226
84395
|
cpSync2(source, dest, { recursive: true, dereference: false });
|
|
84227
84396
|
} catch (err) {
|
|
84228
84397
|
throw new Error(`sync-bundled-skills failed: ${err.message}`);
|
|
@@ -84335,7 +84504,7 @@ function defaultStatusProbe(composePath) {
|
|
|
84335
84504
|
try {
|
|
84336
84505
|
cliBuiltAt = new Date(statSync28(scriptPath).mtimeMs).toISOString();
|
|
84337
84506
|
} catch {}
|
|
84338
|
-
let dir =
|
|
84507
|
+
let dir = dirname19(scriptPath);
|
|
84339
84508
|
for (let i = 0;i < 8; i++) {
|
|
84340
84509
|
const pkgPath = join63(dir, "package.json");
|
|
84341
84510
|
if (existsSync63(pkgPath)) {
|
|
@@ -84348,7 +84517,7 @@ function defaultStatusProbe(composePath) {
|
|
|
84348
84517
|
}
|
|
84349
84518
|
break;
|
|
84350
84519
|
}
|
|
84351
|
-
const parent =
|
|
84520
|
+
const parent = dirname19(dir);
|
|
84352
84521
|
if (parent === dir)
|
|
84353
84522
|
break;
|
|
84354
84523
|
dir = parent;
|
|
@@ -84979,7 +85148,7 @@ init_helpers();
|
|
|
84979
85148
|
init_lifecycle();
|
|
84980
85149
|
import { execSync as execSync4 } from "node:child_process";
|
|
84981
85150
|
import { existsSync as existsSync64, readFileSync as readFileSync58 } from "node:fs";
|
|
84982
|
-
import { dirname as
|
|
85151
|
+
import { dirname as dirname20, join as join64 } from "node:path";
|
|
84983
85152
|
function getClaudeCodeVersion() {
|
|
84984
85153
|
try {
|
|
84985
85154
|
const out = execSync4("claude --version 2>/dev/null", {
|
|
@@ -85038,7 +85207,7 @@ function locateSwitchroomInstallDir() {
|
|
|
85038
85207
|
}
|
|
85039
85208
|
} catch {}
|
|
85040
85209
|
}
|
|
85041
|
-
dir =
|
|
85210
|
+
dir = dirname20(dir);
|
|
85042
85211
|
}
|
|
85043
85212
|
return null;
|
|
85044
85213
|
}
|
|
@@ -86239,7 +86408,7 @@ import {
|
|
|
86239
86408
|
rmSync as rmSync13,
|
|
86240
86409
|
writeFileSync as writeFileSync28
|
|
86241
86410
|
} from "node:fs";
|
|
86242
|
-
import { dirname as
|
|
86411
|
+
import { dirname as dirname21, join as join67 } from "node:path";
|
|
86243
86412
|
import { homedir as homedir40 } from "node:os";
|
|
86244
86413
|
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
86245
86414
|
|
|
@@ -86286,7 +86455,7 @@ function ensurePythonEnv(opts) {
|
|
|
86286
86455
|
if (existsSync67(venvDir)) {
|
|
86287
86456
|
rmSync13(venvDir, { recursive: true, force: true });
|
|
86288
86457
|
}
|
|
86289
|
-
mkdirSync35(
|
|
86458
|
+
mkdirSync35(dirname21(venvDir), { recursive: true });
|
|
86290
86459
|
try {
|
|
86291
86460
|
execFileSync19(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
|
|
86292
86461
|
} catch (err) {
|
|
@@ -86320,14 +86489,14 @@ function ensurePythonEnv(opts) {
|
|
|
86320
86489
|
// src/deps/node.ts
|
|
86321
86490
|
import { createHash as createHash13 } from "node:crypto";
|
|
86322
86491
|
import {
|
|
86323
|
-
copyFileSync as
|
|
86492
|
+
copyFileSync as copyFileSync10,
|
|
86324
86493
|
existsSync as existsSync68,
|
|
86325
86494
|
mkdirSync as mkdirSync36,
|
|
86326
86495
|
readFileSync as readFileSync61,
|
|
86327
86496
|
rmSync as rmSync14,
|
|
86328
86497
|
writeFileSync as writeFileSync29
|
|
86329
86498
|
} from "node:fs";
|
|
86330
|
-
import { dirname as
|
|
86499
|
+
import { dirname as dirname22, join as join68 } from "node:path";
|
|
86331
86500
|
import { homedir as homedir41 } from "node:os";
|
|
86332
86501
|
import { execFileSync as execFileSync20 } from "node:child_process";
|
|
86333
86502
|
|
|
@@ -86354,7 +86523,7 @@ function defaultNodeCacheRoot() {
|
|
|
86354
86523
|
return join68(homedir41(), ".switchroom", "deps", "node");
|
|
86355
86524
|
}
|
|
86356
86525
|
function hashDepInputs(packageJsonPath) {
|
|
86357
|
-
const sourceDir =
|
|
86526
|
+
const sourceDir = dirname22(packageJsonPath);
|
|
86358
86527
|
const hasher = createHash13("sha256");
|
|
86359
86528
|
hasher.update(`package.json
|
|
86360
86529
|
`);
|
|
@@ -86379,7 +86548,7 @@ function ensureNodeEnv(opts) {
|
|
|
86379
86548
|
if (!existsSync68(packageJsonPath)) {
|
|
86380
86549
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
86381
86550
|
}
|
|
86382
|
-
const sourceDir =
|
|
86551
|
+
const sourceDir = dirname22(packageJsonPath);
|
|
86383
86552
|
const envDir = join68(cacheRoot, skillName);
|
|
86384
86553
|
const stampPath = join68(envDir, ".package.sha256");
|
|
86385
86554
|
const nodeModulesDir = join68(envDir, "node_modules");
|
|
@@ -86401,12 +86570,12 @@ function ensureNodeEnv(opts) {
|
|
|
86401
86570
|
rmSync14(envDir, { recursive: true, force: true });
|
|
86402
86571
|
}
|
|
86403
86572
|
mkdirSync36(envDir, { recursive: true });
|
|
86404
|
-
|
|
86573
|
+
copyFileSync10(packageJsonPath, join68(envDir, "package.json"));
|
|
86405
86574
|
let copiedLockfile = false;
|
|
86406
86575
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
86407
86576
|
const lockPath = join68(sourceDir, lockName);
|
|
86408
86577
|
if (existsSync68(lockPath)) {
|
|
86409
|
-
|
|
86578
|
+
copyFileSync10(lockPath, join68(envDir, lockName));
|
|
86410
86579
|
copiedLockfile = true;
|
|
86411
86580
|
}
|
|
86412
86581
|
}
|
|
@@ -87405,7 +87574,7 @@ function safeParseInt(value, fallback) {
|
|
|
87405
87574
|
init_helpers();
|
|
87406
87575
|
init_loader();
|
|
87407
87576
|
init_merge();
|
|
87408
|
-
import { copyFileSync as
|
|
87577
|
+
import { copyFileSync as copyFileSync11, existsSync as existsSync71, readFileSync as readFileSync62, writeFileSync as writeFileSync30 } from "node:fs";
|
|
87409
87578
|
import { join as join70, resolve as resolve44 } from "node:path";
|
|
87410
87579
|
init_scaffold();
|
|
87411
87580
|
init_profiles();
|
|
@@ -87482,7 +87651,7 @@ function registerSoulCommand(program3) {
|
|
|
87482
87651
|
if (existsSync71(backupPath)) {
|
|
87483
87652
|
backupPath = `${t.soulPath}.bak.${Date.now()}`;
|
|
87484
87653
|
}
|
|
87485
|
-
|
|
87654
|
+
copyFileSync11(t.soulPath, backupPath);
|
|
87486
87655
|
}
|
|
87487
87656
|
writeFileSync30(t.soulPath, content, "utf-8");
|
|
87488
87657
|
if (backupPath) {
|
|
@@ -88968,7 +89137,7 @@ function registerDriveMcpLauncherCommand(program3) {
|
|
|
88968
89137
|
init_scaffold_integration();
|
|
88969
89138
|
import { spawn as spawn5 } from "node:child_process";
|
|
88970
89139
|
import { writeFileSync as writeFileSync33, mkdirSync as mkdirSync41 } from "node:fs";
|
|
88971
|
-
import { dirname as
|
|
89140
|
+
import { dirname as dirname23, join as join76 } from "node:path";
|
|
88972
89141
|
var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
|
|
88973
89142
|
var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
88974
89143
|
var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
|
|
@@ -88993,7 +89162,7 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
|
|
|
88993
89162
|
function writeRefreshHeartbeat(agentName, data) {
|
|
88994
89163
|
const path7 = heartbeatPath(agentName);
|
|
88995
89164
|
try {
|
|
88996
|
-
mkdirSync41(
|
|
89165
|
+
mkdirSync41(dirname23(path7), { recursive: true });
|
|
88997
89166
|
writeFileSync33(path7, JSON.stringify(data, null, 2), { mode: 420 });
|
|
88998
89167
|
} catch {}
|
|
88999
89168
|
}
|
|
@@ -89186,7 +89355,7 @@ function registerM365McpLauncherCommand(program3) {
|
|
|
89186
89355
|
init_scaffold_integration();
|
|
89187
89356
|
import { spawn as spawn6 } from "node:child_process";
|
|
89188
89357
|
import { existsSync as existsSync78, mkdirSync as mkdirSync42, writeFileSync as writeFileSync34 } from "node:fs";
|
|
89189
|
-
import { dirname as
|
|
89358
|
+
import { dirname as dirname24 } from "node:path";
|
|
89190
89359
|
var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
|
|
89191
89360
|
var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
|
|
89192
89361
|
var DEFAULT_VAULT_KEY = "notion/integration-token";
|
|
@@ -89196,7 +89365,7 @@ function buildNotionMcpArgs(opts) {
|
|
|
89196
89365
|
}
|
|
89197
89366
|
function defaultWriteHeartbeat(path7, contents) {
|
|
89198
89367
|
try {
|
|
89199
|
-
const dir =
|
|
89368
|
+
const dir = dirname24(path7);
|
|
89200
89369
|
if (!existsSync78(dir))
|
|
89201
89370
|
mkdirSync42(dir, { recursive: true });
|
|
89202
89371
|
writeFileSync34(path7, contents);
|
|
@@ -90351,7 +90520,7 @@ init_paths();
|
|
|
90351
90520
|
import {
|
|
90352
90521
|
closeSync as closeSync14,
|
|
90353
90522
|
existsSync as existsSync84,
|
|
90354
|
-
fsyncSync as
|
|
90523
|
+
fsyncSync as fsyncSync8,
|
|
90355
90524
|
mkdirSync as mkdirSync47,
|
|
90356
90525
|
openSync as openSync14,
|
|
90357
90526
|
readdirSync as readdirSync30,
|
|
@@ -90434,7 +90603,7 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
90434
90603
|
const fd = openSync14(stagingPath, "w", 384);
|
|
90435
90604
|
try {
|
|
90436
90605
|
writeSync10(fd, yamlText);
|
|
90437
|
-
|
|
90606
|
+
fsyncSync8(fd);
|
|
90438
90607
|
} finally {
|
|
90439
90608
|
closeSync14(fd);
|
|
90440
90609
|
}
|
|
@@ -90451,7 +90620,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
90451
90620
|
const fd = openSync14(stagingPath, "w", 384);
|
|
90452
90621
|
try {
|
|
90453
90622
|
writeSync10(fd, yamlText);
|
|
90454
|
-
|
|
90623
|
+
fsyncSync8(fd);
|
|
90455
90624
|
} finally {
|
|
90456
90625
|
closeSync14(fd);
|
|
90457
90626
|
}
|
|
@@ -90658,7 +90827,7 @@ function reconcileAgentCronOnly(agent) {
|
|
|
90658
90827
|
import {
|
|
90659
90828
|
closeSync as closeSync15,
|
|
90660
90829
|
existsSync as existsSync85,
|
|
90661
|
-
fsyncSync as
|
|
90830
|
+
fsyncSync as fsyncSync9,
|
|
90662
90831
|
mkdirSync as mkdirSync48,
|
|
90663
90832
|
openSync as openSync15,
|
|
90664
90833
|
readdirSync as readdirSync31,
|
|
@@ -90702,7 +90871,7 @@ function stagePendingScheduleEntry(opts) {
|
|
|
90702
90871
|
const fd = openSync15(yamlTmp, "w", 384);
|
|
90703
90872
|
try {
|
|
90704
90873
|
writeSync11(fd, opts.yamlText);
|
|
90705
|
-
|
|
90874
|
+
fsyncSync9(fd);
|
|
90706
90875
|
} finally {
|
|
90707
90876
|
closeSync15(fd);
|
|
90708
90877
|
}
|
|
@@ -91670,7 +91839,7 @@ import {
|
|
|
91670
91839
|
writeFileSync as writeFileSync40
|
|
91671
91840
|
} from "node:fs";
|
|
91672
91841
|
import { tmpdir as tmpdir5, homedir as homedir49 } from "node:os";
|
|
91673
|
-
import { dirname as
|
|
91842
|
+
import { dirname as dirname29, join as join85, relative as relative2, resolve as resolve52 } from "node:path";
|
|
91674
91843
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
91675
91844
|
|
|
91676
91845
|
// src/cli/skill-common.ts
|
|
@@ -92140,7 +92309,7 @@ function writePayload(poolDir, name, files) {
|
|
|
92140
92309
|
try {
|
|
92141
92310
|
for (const [path8, content] of Object.entries(files)) {
|
|
92142
92311
|
const full = join85(staging, path8);
|
|
92143
|
-
mkdirSync49(
|
|
92312
|
+
mkdirSync49(dirname29(full), { recursive: true, mode: 493 });
|
|
92144
92313
|
const fd = openSync16(full, "wx");
|
|
92145
92314
|
try {
|
|
92146
92315
|
writeFileSync40(fd, content);
|
|
@@ -92262,7 +92431,7 @@ import {
|
|
|
92262
92431
|
utimesSync,
|
|
92263
92432
|
writeFileSync as writeFileSync41
|
|
92264
92433
|
} from "node:fs";
|
|
92265
|
-
import { dirname as
|
|
92434
|
+
import { dirname as dirname30, join as join86, relative as relative3, resolve as resolve53 } from "node:path";
|
|
92266
92435
|
import { homedir as homedir50, tmpdir as tmpdir6 } from "node:os";
|
|
92267
92436
|
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
92268
92437
|
init_helpers();
|
|
@@ -92515,13 +92684,13 @@ function writePersonalSkill(targetDir, files) {
|
|
|
92515
92684
|
if (targetIsSymlink) {
|
|
92516
92685
|
fail4(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
|
|
92517
92686
|
}
|
|
92518
|
-
mkdirSync50(
|
|
92519
|
-
const staging = mkdtempSync6(join86(
|
|
92687
|
+
mkdirSync50(dirname30(targetDir), { recursive: true, mode: 493 });
|
|
92688
|
+
const staging = mkdtempSync6(join86(dirname30(targetDir), `.skill-personal-stage-`));
|
|
92520
92689
|
let oldRename = null;
|
|
92521
92690
|
try {
|
|
92522
92691
|
for (const [path8, content] of Object.entries(files)) {
|
|
92523
92692
|
const full = join86(staging, path8);
|
|
92524
|
-
mkdirSync50(
|
|
92693
|
+
mkdirSync50(dirname30(full), { recursive: true, mode: 493 });
|
|
92525
92694
|
const fd = openSync17(full, "wx");
|
|
92526
92695
|
try {
|
|
92527
92696
|
writeFileSync41(fd, content);
|
|
@@ -93264,7 +93433,7 @@ import {
|
|
|
93264
93433
|
statSync as statSync39,
|
|
93265
93434
|
lstatSync as lstatSync11,
|
|
93266
93435
|
realpathSync as realpathSync8,
|
|
93267
|
-
copyFileSync as
|
|
93436
|
+
copyFileSync as copyFileSync13
|
|
93268
93437
|
} from "node:fs";
|
|
93269
93438
|
import { homedir as homedir53 } from "node:os";
|
|
93270
93439
|
import { join as join89 } from "node:path";
|
|
@@ -93535,7 +93704,7 @@ function backupExistingCompose() {
|
|
|
93535
93704
|
return null;
|
|
93536
93705
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
93537
93706
|
const bak = `${p}.bak-${ts}`;
|
|
93538
|
-
|
|
93707
|
+
copyFileSync13(p, bak);
|
|
93539
93708
|
return bak;
|
|
93540
93709
|
}
|
|
93541
93710
|
function runDocker(args) {
|
|
@@ -93748,7 +93917,7 @@ The log is created when hostd handles its first privileged-verb request.`));
|
|
|
93748
93917
|
init_source();
|
|
93749
93918
|
init_helpers();
|
|
93750
93919
|
init_operator_uid();
|
|
93751
|
-
import { existsSync as existsSync93, mkdirSync as mkdirSync52, writeFileSync as writeFileSync43, copyFileSync as
|
|
93920
|
+
import { existsSync as existsSync93, mkdirSync as mkdirSync52, writeFileSync as writeFileSync43, copyFileSync as copyFileSync14 } from "node:fs";
|
|
93752
93921
|
import { homedir as homedir54 } from "node:os";
|
|
93753
93922
|
import { join as join90 } from "node:path";
|
|
93754
93923
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
@@ -93846,7 +94015,7 @@ function backupExistingCompose2() {
|
|
|
93846
94015
|
return null;
|
|
93847
94016
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
93848
94017
|
const bak = `${p}.bak-${ts}`;
|
|
93849
|
-
|
|
94018
|
+
copyFileSync14(p, bak);
|
|
93850
94019
|
return bak;
|
|
93851
94020
|
}
|
|
93852
94021
|
function runDocker2(args) {
|