switchroom 0.15.13 → 0.15.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-scheduler/index.js +312 -13
- package/dist/auth-broker/index.js +49 -3
- package/dist/cli/notion-write-pretool.mjs +49 -3
- package/dist/cli/switchroom.js +335 -107
- package/dist/host-control/main.js +49 -3
- package/dist/vault/approvals/kernel-server.js +50 -4
- package/dist/vault/broker/server.js +50 -4
- package/package.json +1 -1
- package/profiles/_base/cron-session.sh.hbs +49 -15
- package/telegram-plugin/bridge/bridge.ts +7 -1
- package/telegram-plugin/dist/bridge/bridge.js +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +108 -9
- package/telegram-plugin/dist/server.js +1 -1
- package/telegram-plugin/gateway/gateway.ts +46 -0
- package/telegram-plugin/gateway/ipc-protocol.ts +31 -1
- package/telegram-plugin/gateway/ipc-server.ts +29 -0
- package/telegram-plugin/runtime-metrics.ts +14 -0
- package/telegram-plugin/tests/bridge-liveness-override.test.ts +21 -0
- package/telegram-plugin/tests/ipc-server-validate-send-outbound.test.ts +54 -0
- package/telegram-plugin/tests/runtime-metrics.test.ts +9 -0
- package/telegram-plugin/tests/send-outbound-wiring.test.ts +63 -0
package/dist/cli/switchroom.js
CHANGED
|
@@ -13520,7 +13520,7 @@ var init_zod = __esm(() => {
|
|
|
13520
13520
|
});
|
|
13521
13521
|
|
|
13522
13522
|
// src/config/schema.ts
|
|
13523
|
-
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, TelegramReactionsPollSchema, PollSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, ReleaseBlock, NetworkIsolationSchema, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, DEFAULT_PROFILE = "default", AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, SwitchroomConfigSchema;
|
|
13523
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, TelegramReactionsPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, ReleaseBlock, NetworkIsolationSchema, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, DEFAULT_PROFILE = "default", AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, SwitchroomConfigSchema;
|
|
13524
13524
|
var init_schema = __esm(() => {
|
|
13525
13525
|
init_zod();
|
|
13526
13526
|
CodeRepoEntrySchema = exports_external.object({
|
|
@@ -13553,11 +13553,29 @@ var init_schema = __esm(() => {
|
|
|
13553
13553
|
HttpDiffPollSchema,
|
|
13554
13554
|
TelegramReactionsPollSchema
|
|
13555
13555
|
]);
|
|
13556
|
+
TelegramMessageActionSchema = exports_external.object({
|
|
13557
|
+
type: exports_external.literal("telegram-message"),
|
|
13558
|
+
text: exports_external.string().min(1).describe("Operator-authored message text. Posts to the AGENT'S OWN chat only " + "(the chat is not configurable \u2014 fenced by construction), into the " + "entry-level `topic` when set. Supports ONLY deterministic placeholders " + "{{date}} / {{time}} (UTC, from the fire clock) and {{agent}}. NO vault " + "secrets (use a webhook action for secret-bearing requests) and NO model " + "output \u2014 the text is fully determined by config."),
|
|
13559
|
+
parse_mode: exports_external.enum(["html", "text"]).default("html").describe("Telegram parse mode for the message body.")
|
|
13560
|
+
});
|
|
13561
|
+
WebhookActionSchema = exports_external.object({
|
|
13562
|
+
type: exports_external.literal("webhook"),
|
|
13563
|
+
url: exports_external.string().url().describe("Webhook target. SAME egress fence as an http-diff poll (\u00a76.1): https " + "only, host on the operator egress allowlist, loopback/private/link-local " + "rejected, resolved IP DNS-rebind-pinned. Not agent-writable without an " + "operator commit."),
|
|
13564
|
+
method: exports_external.enum(["GET", "POST"]).default("POST"),
|
|
13565
|
+
headers: exports_external.record(exports_external.string()).optional().describe("Static headers; {{secret}} substitution applies."),
|
|
13566
|
+
body: exports_external.string().optional().describe("Static request body; {{secret}} substitution applies."),
|
|
13567
|
+
secrets: exports_external.array(exports_external.string().regex(/^[a-zA-Z0-9_\-/]+$/, "Secret key names must contain only alphanumeric characters, underscores, hyphens, and forward slashes")).default([]).describe("Vault keys this webhook may inject into headers/body via {{name}}. Each " + "is HOST-PINNED (\u00a76.1): a secret may only be sent to the host it is bound " + "to in operator config, so an approved action cannot exfil it elsewhere.")
|
|
13568
|
+
});
|
|
13569
|
+
ActionSpecSchema = exports_external.discriminatedUnion("type", [
|
|
13570
|
+
TelegramMessageActionSchema,
|
|
13571
|
+
WebhookActionSchema
|
|
13572
|
+
]);
|
|
13556
13573
|
ScheduleEntrySchema = exports_external.object({
|
|
13557
13574
|
cron: exports_external.string().describe("Cron expression (e.g., '0 8 * * *')"),
|
|
13558
|
-
prompt: exports_external.string().describe("Prompt to send at the scheduled time (the escalation prompt when kind=poll; templated with {{diff}})."),
|
|
13559
|
-
kind: exports_external.enum(["poll", "prompt"]).optional().describe("Tier-0 routing (docs/rfcs/cheap-cron-sessions.md). 'prompt' (default) " + "fires a model turn every tick (Tier 1/2 per `context`). 'poll' runs a " + "model-free deterministic check (requires `poll`) and only escalates to " + "a model fire on a hit.
|
|
13575
|
+
prompt: exports_external.string().optional().describe("Prompt to send at the scheduled time (the escalation prompt when " + "kind=poll; templated with {{diff}}). Required for kind prompt/poll; " + "absent for kind=action (an action has no model fire, so no prompt)."),
|
|
13576
|
+
kind: exports_external.enum(["poll", "prompt", "action"]).optional().describe("Tier-0 routing (docs/rfcs/cheap-cron-sessions.md). 'prompt' (default) " + "fires a model turn every tick (Tier 1/2 per `context`). 'poll' runs a " + "model-free deterministic check (requires `poll`) and only escalates to " + "a model fire on a hit. 'action' runs a model-free deterministic verb " + "(requires `action`) that COMPLETES the work and never escalates \u2014 zero " + "tokens, no session. poll/prompt are honoured only when " + "SWITCHROOM_CHEAP_CRON is on; an action is model-free regardless (the " + "kill-switch governs model tiering, not deterministic actions)."),
|
|
13560
13577
|
poll: PollSpecSchema.optional().describe("Required iff kind=poll. The declarative poll spec."),
|
|
13578
|
+
action: ActionSpecSchema.optional().describe("Required iff kind=action. The declarative action spec (telegram-message or webhook)."),
|
|
13561
13579
|
model: exports_external.string().optional().describe("Cron model hint. Reactivated by SWITCHROOM_CHEAP_CRON (was DEPRECATED/" + "IGNORED in v0.8). A known-cheap id (sonnet/haiku family) routes the " + "fire to a fresh cheap cron session (Tier 1, `context: fresh`); 'opus', " + "a custom id, or unset routes to the agent's live session (Tier 2, " + "`context: agent`) \u2014 the conservative default that preserves pre-v0.8 " + "behaviour. Note: a live session's model is fixed at launch, so on Tier " + "2 this is informational. See docs/scheduling.md."),
|
|
13562
13580
|
context: exports_external.enum(["fresh", "agent"]).optional().describe("Does this cron need the agent, or just a model? 'fresh' \u2192 a minimal-" + "context cheap cron session (Tier 1). 'agent' \u2192 the agent's live " + "session with full persona/memory (Tier 2). Unset \u2192 inferred from " + "`model` (cheap\u2192fresh, else agent). Honoured only when " + "SWITCHROOM_CHEAP_CRON is on."),
|
|
13563
13581
|
secrets: exports_external.array(exports_external.string().regex(/^[a-zA-Z0-9_\-/]+$/, "Secret key names must contain only alphanumeric characters, underscores, hyphens, and forward slashes")).default([]).describe("Vault key names this cron task may read via the vault-broker daemon. " + "Empty by default \u2014 broker requests for unlisted keys are denied. " + "Note: this is misconfiguration protection (a typo in cron-A doesn't " + "accidentally read cron-B's keys) rather than a security boundary \u2014 " + "anyone who can edit cron scripts can also edit switchroom.yaml, and " + "anyone with the vault passphrase can read the vault file directly. " + "See docs/configuration.md for the full framing."),
|
|
@@ -13574,13 +13592,41 @@ var init_schema = __esm(() => {
|
|
|
13574
13592
|
message: "kind: poll requires a `poll` spec (http-diff or telegram-reactions)."
|
|
13575
13593
|
});
|
|
13576
13594
|
}
|
|
13577
|
-
if (kind
|
|
13595
|
+
if (kind !== "poll" && entry.poll) {
|
|
13578
13596
|
ctx.addIssue({
|
|
13579
13597
|
code: exports_external.ZodIssueCode.custom,
|
|
13580
13598
|
path: ["poll"],
|
|
13581
13599
|
message: "`poll` is only valid when kind: poll."
|
|
13582
13600
|
});
|
|
13583
13601
|
}
|
|
13602
|
+
if (kind === "action" && !entry.action) {
|
|
13603
|
+
ctx.addIssue({
|
|
13604
|
+
code: exports_external.ZodIssueCode.custom,
|
|
13605
|
+
path: ["action"],
|
|
13606
|
+
message: "kind: action requires an `action` spec (telegram-message or webhook)."
|
|
13607
|
+
});
|
|
13608
|
+
}
|
|
13609
|
+
if (kind !== "action" && entry.action) {
|
|
13610
|
+
ctx.addIssue({
|
|
13611
|
+
code: exports_external.ZodIssueCode.custom,
|
|
13612
|
+
path: ["action"],
|
|
13613
|
+
message: "`action` is only valid when kind: action."
|
|
13614
|
+
});
|
|
13615
|
+
}
|
|
13616
|
+
if (kind !== "action" && (entry.prompt === undefined || entry.prompt.trim() === "")) {
|
|
13617
|
+
ctx.addIssue({
|
|
13618
|
+
code: exports_external.ZodIssueCode.custom,
|
|
13619
|
+
path: ["prompt"],
|
|
13620
|
+
message: `kind: ${kind} requires a non-empty \`prompt\`.`
|
|
13621
|
+
});
|
|
13622
|
+
}
|
|
13623
|
+
if (kind === "action" && entry.prompt !== undefined) {
|
|
13624
|
+
ctx.addIssue({
|
|
13625
|
+
code: exports_external.ZodIssueCode.custom,
|
|
13626
|
+
path: ["prompt"],
|
|
13627
|
+
message: "`prompt` is not valid for kind: action (an action never fires a model)."
|
|
13628
|
+
});
|
|
13629
|
+
}
|
|
13584
13630
|
});
|
|
13585
13631
|
AgentSoulSchema = exports_external.object({
|
|
13586
13632
|
name: exports_external.string().describe("Agent persona name (e.g., 'Coach', 'Sage')"),
|
|
@@ -15407,6 +15453,9 @@ function resolveCronModel(model) {
|
|
|
15407
15453
|
return isKnownCheapModel(model) ? model : DEFAULT_CRON_MODEL;
|
|
15408
15454
|
}
|
|
15409
15455
|
function resolveCronRouting(input, opts) {
|
|
15456
|
+
if ((input.kind ?? "prompt") === "action") {
|
|
15457
|
+
return { tier: "action", session: null, customModelDowngrade: false };
|
|
15458
|
+
}
|
|
15410
15459
|
if (!opts.cheapCronEnabled) {
|
|
15411
15460
|
return { tier: "main", session: "main", customModelDowngrade: false };
|
|
15412
15461
|
}
|
|
@@ -15451,9 +15500,96 @@ var init_cron_routing = __esm(() => {
|
|
|
15451
15500
|
OPUS_MODEL_RE = /opus/i;
|
|
15452
15501
|
});
|
|
15453
15502
|
|
|
15503
|
+
// src/scheduler/cron-cadence.ts
|
|
15504
|
+
function csvSmallestGap(field) {
|
|
15505
|
+
if (!field.includes(","))
|
|
15506
|
+
return null;
|
|
15507
|
+
const parts = field.split(",").map((s) => Number(s)).filter((n) => Number.isInteger(n) && n >= 0);
|
|
15508
|
+
if (parts.length < 2)
|
|
15509
|
+
return null;
|
|
15510
|
+
const sorted = [...parts].sort((a, b) => a - b);
|
|
15511
|
+
let smallest = Infinity;
|
|
15512
|
+
for (let i = 1;i < sorted.length; i++) {
|
|
15513
|
+
const gap = sorted[i] - sorted[i - 1];
|
|
15514
|
+
if (gap > 0 && gap < smallest)
|
|
15515
|
+
smallest = gap;
|
|
15516
|
+
}
|
|
15517
|
+
return Number.isFinite(smallest) ? smallest : null;
|
|
15518
|
+
}
|
|
15519
|
+
function estimateCronGapMin(expr) {
|
|
15520
|
+
const fields = expr.trim().split(/\s+/);
|
|
15521
|
+
if (fields.length < 5)
|
|
15522
|
+
return Infinity;
|
|
15523
|
+
const [min, hour] = fields;
|
|
15524
|
+
if (min === "*")
|
|
15525
|
+
return 1;
|
|
15526
|
+
const minStep = min.match(/^\*\/(\d+)$/);
|
|
15527
|
+
if (minStep) {
|
|
15528
|
+
const n = Number(minStep[1]);
|
|
15529
|
+
return n > 0 ? n : Infinity;
|
|
15530
|
+
}
|
|
15531
|
+
const minCsv = csvSmallestGap(min);
|
|
15532
|
+
if (minCsv !== null)
|
|
15533
|
+
return minCsv;
|
|
15534
|
+
if (!/^\d+$/.test(min))
|
|
15535
|
+
return Infinity;
|
|
15536
|
+
if (hour === "*")
|
|
15537
|
+
return 60;
|
|
15538
|
+
const hourStep = hour.match(/^\*\/(\d+)$/);
|
|
15539
|
+
if (hourStep) {
|
|
15540
|
+
const n = Number(hourStep[1]);
|
|
15541
|
+
return n > 0 ? n * 60 : Infinity;
|
|
15542
|
+
}
|
|
15543
|
+
const hourCsv = csvSmallestGap(hour);
|
|
15544
|
+
if (hourCsv !== null)
|
|
15545
|
+
return hourCsv * 60;
|
|
15546
|
+
if (/^\d+$/.test(hour))
|
|
15547
|
+
return 1440;
|
|
15548
|
+
return Infinity;
|
|
15549
|
+
}
|
|
15550
|
+
|
|
15454
15551
|
// src/scheduler/tier-selector.ts
|
|
15455
|
-
function
|
|
15456
|
-
|
|
15552
|
+
function resolveCronAutoTier(env2 = process.env) {
|
|
15553
|
+
const v = (env2.SWITCHROOM_CRON_AUTO_TIER ?? "").toLowerCase();
|
|
15554
|
+
return v === "1" || v === "true" || v === "on";
|
|
15555
|
+
}
|
|
15556
|
+
function recommendCronTier(input, frequentGapMin = DEFAULT_FREQUENT_GAP_MIN) {
|
|
15557
|
+
if (input.kind === "action") {
|
|
15558
|
+
return { tier: "action", source: "explicit", reason: "declared kind: action (model-free verb)" };
|
|
15559
|
+
}
|
|
15560
|
+
if (input.kind === "poll") {
|
|
15561
|
+
return { tier: "poll", source: "explicit", reason: "declared kind: poll (model-free check)" };
|
|
15562
|
+
}
|
|
15563
|
+
if (input.context === "fresh") {
|
|
15564
|
+
return { tier: "cheap", source: "explicit", reason: "declared context: fresh (cheap cron session)" };
|
|
15565
|
+
}
|
|
15566
|
+
if (input.context === "agent") {
|
|
15567
|
+
return { tier: "main", source: "explicit", reason: "declared context: agent (full live session)" };
|
|
15568
|
+
}
|
|
15569
|
+
if (input.model !== undefined) {
|
|
15570
|
+
return isKnownCheapModel(input.model) ? { tier: "cheap", source: "explicit", reason: `cheap model '${input.model}' \u2192 cheap cron session` } : { tier: "main", source: "explicit", reason: `model '${input.model}' is not a known-cheap id \u2192 full live session` };
|
|
15571
|
+
}
|
|
15572
|
+
if (input.smallestGapMin <= frequentGapMin) {
|
|
15573
|
+
return {
|
|
15574
|
+
tier: "cheap",
|
|
15575
|
+
source: "cadence-default",
|
|
15576
|
+
reason: `fires every ~${input.smallestGapMin}min (\u2264 ${frequentGapMin}min) \u2014 defaulting to a cheap ` + `session; set context: agent (or an Opus/custom model) if this needs the agent's full context`
|
|
15577
|
+
};
|
|
15578
|
+
}
|
|
15579
|
+
return {
|
|
15580
|
+
tier: "main",
|
|
15581
|
+
source: "cadence-default",
|
|
15582
|
+
reason: `fires every ~${input.smallestGapMin}min (> ${frequentGapMin}min) \u2014 defaulting to the agent's ` + `full session; set model: sonnet (or context: fresh) to run it cheaply`
|
|
15583
|
+
};
|
|
15584
|
+
}
|
|
15585
|
+
function applyDefaultTier(entry, frequentGapMin = DEFAULT_FREQUENT_GAP_MIN, autoTierEnabled = resolveCronAutoTier()) {
|
|
15586
|
+
if (!autoTierEnabled)
|
|
15587
|
+
return entry;
|
|
15588
|
+
if (entry.kind !== undefined || entry.context !== undefined || entry.model !== undefined) {
|
|
15589
|
+
return entry;
|
|
15590
|
+
}
|
|
15591
|
+
const rec = recommendCronTier({ smallestGapMin: estimateCronGapMin(entry.cron) }, frequentGapMin);
|
|
15592
|
+
return rec.tier === "cheap" ? { ...entry, context: "fresh" } : entry;
|
|
15457
15593
|
}
|
|
15458
15594
|
var DEFAULT_FREQUENT_GAP_MIN = 60;
|
|
15459
15595
|
var init_tier_selector = __esm(() => {
|
|
@@ -30769,8 +30905,74 @@ var init_doctor_webkite = __esm(() => {
|
|
|
30769
30905
|
init_loader();
|
|
30770
30906
|
});
|
|
30771
30907
|
|
|
30908
|
+
// src/cli/doctor-cron-session.ts
|
|
30909
|
+
import { statSync as realStatSync } from "node:fs";
|
|
30910
|
+
import { resolve as resolve31 } from "node:path";
|
|
30911
|
+
function agentRunsCronSession(config, agent) {
|
|
30912
|
+
const raw = config.agents[agent];
|
|
30913
|
+
if (!raw)
|
|
30914
|
+
return false;
|
|
30915
|
+
const resolved = resolveAgentConfig(config.defaults, config.profiles, raw);
|
|
30916
|
+
const entries = (resolved.schedule ?? []).map((e) => applyDefaultTier({ cron: e.cron, kind: e.kind, model: e.model, context: e.context }));
|
|
30917
|
+
return scheduleNeedsCronSession(entries, { cheapCronEnabled: true });
|
|
30918
|
+
}
|
|
30919
|
+
function runCronSessionChecks(config, deps = defaultDeps2) {
|
|
30920
|
+
const agentsDir = resolveAgentsDir(config);
|
|
30921
|
+
const results = [];
|
|
30922
|
+
for (const agent of Object.keys(config.agents).sort()) {
|
|
30923
|
+
if (!agentRunsCronSession(config, agent))
|
|
30924
|
+
continue;
|
|
30925
|
+
const name = `cron-session: ${agent}`;
|
|
30926
|
+
const alivePath = resolve31(agentsDir, agent, "telegram", ".bridge-alive-cron");
|
|
30927
|
+
let mtimeMs;
|
|
30928
|
+
try {
|
|
30929
|
+
mtimeMs = deps.statMtimeMs(alivePath);
|
|
30930
|
+
} catch (err) {
|
|
30931
|
+
const code = err.code;
|
|
30932
|
+
if (code === "EACCES" || code === "EPERM") {
|
|
30933
|
+
results.push({
|
|
30934
|
+
name,
|
|
30935
|
+
status: "skip",
|
|
30936
|
+
detail: "cron-bridge liveness file present but unreadable by the operator \u2014 re-run doctor without sudo"
|
|
30937
|
+
});
|
|
30938
|
+
continue;
|
|
30939
|
+
}
|
|
30940
|
+
results.push({
|
|
30941
|
+
name,
|
|
30942
|
+
status: "fail",
|
|
30943
|
+
detail: `<${agent}-cron> bridge not registered \u2014 no liveness file at ${alivePath}. Tier-1 cron fires are falling back to the (expensive) main session.`,
|
|
30944
|
+
fix: `Restart the agent (\`switchroom agent restart ${agent} --wait --force\`) and check /var/log/switchroom/cron-session.log for a wizard wedge.`
|
|
30945
|
+
});
|
|
30946
|
+
continue;
|
|
30947
|
+
}
|
|
30948
|
+
const ageMs = deps.now() - mtimeMs;
|
|
30949
|
+
if (ageMs <= MAX_AGE_MS) {
|
|
30950
|
+
results.push({ name, status: "ok", detail: `<${agent}-cron> bridge live (heartbeat ${Math.round(ageMs / 1000)}s ago)` });
|
|
30951
|
+
} else {
|
|
30952
|
+
results.push({
|
|
30953
|
+
name,
|
|
30954
|
+
status: "fail",
|
|
30955
|
+
detail: `<${agent}-cron> bridge heartbeat is stale (${Math.round(ageMs / 1000)}s > ${MAX_AGE_MS / 1000}s) \u2014 the cron session registered then died. Tier-1 fires are falling back to main.`,
|
|
30956
|
+
fix: `Restart the agent (\`switchroom agent restart ${agent} --wait --force\`) and check /var/log/switchroom/cron-session.log.`
|
|
30957
|
+
});
|
|
30958
|
+
}
|
|
30959
|
+
}
|
|
30960
|
+
return results;
|
|
30961
|
+
}
|
|
30962
|
+
var defaultDeps2, MAX_AGE_MS = 30000;
|
|
30963
|
+
var init_doctor_cron_session = __esm(() => {
|
|
30964
|
+
init_loader();
|
|
30965
|
+
init_merge();
|
|
30966
|
+
init_cron_routing();
|
|
30967
|
+
init_tier_selector();
|
|
30968
|
+
defaultDeps2 = {
|
|
30969
|
+
statMtimeMs: (p) => realStatSync(p).mtimeMs,
|
|
30970
|
+
now: () => Date.now()
|
|
30971
|
+
};
|
|
30972
|
+
});
|
|
30973
|
+
|
|
30772
30974
|
// src/cli/doctor-scaffold-wiring.ts
|
|
30773
|
-
import { join as join52, resolve as
|
|
30975
|
+
import { join as join52, resolve as resolve32 } from "node:path";
|
|
30774
30976
|
function readJson2(d, path4) {
|
|
30775
30977
|
if (!d.existsSync(path4))
|
|
30776
30978
|
return { kind: "absent" };
|
|
@@ -30804,7 +31006,7 @@ function checkIntegrationScaffoldWiring(args) {
|
|
|
30804
31006
|
];
|
|
30805
31007
|
}
|
|
30806
31008
|
for (const name of agents) {
|
|
30807
|
-
const agentDir =
|
|
31009
|
+
const agentDir = resolve32(agentsDir, name);
|
|
30808
31010
|
if (!deps.existsSync(agentDir)) {
|
|
30809
31011
|
results.push({
|
|
30810
31012
|
name: `${label}: ${name} scaffold`,
|
|
@@ -30844,7 +31046,7 @@ function checkIntegrationScaffoldWiring(args) {
|
|
|
30844
31046
|
let trustDetail = "no .claude/.claude.json";
|
|
30845
31047
|
if (trustRead.kind === "ok") {
|
|
30846
31048
|
const projects = trustRead.data?.projects ?? {};
|
|
30847
|
-
const proj = projects[
|
|
31049
|
+
const proj = projects[resolve32(agentDir)];
|
|
30848
31050
|
const enabled = proj?.enabledMcpjsonServers;
|
|
30849
31051
|
if (Array.isArray(enabled) && enabled.includes(mcpKey)) {
|
|
30850
31052
|
trustOk = true;
|
|
@@ -31063,7 +31265,7 @@ var init_doctor_microsoft = __esm(() => {
|
|
|
31063
31265
|
import {
|
|
31064
31266
|
existsSync as realExistsSync4,
|
|
31065
31267
|
readFileSync as realReadFileSync4,
|
|
31066
|
-
statSync as
|
|
31268
|
+
statSync as realStatSync2
|
|
31067
31269
|
} from "node:fs";
|
|
31068
31270
|
import { join as join54 } from "node:path";
|
|
31069
31271
|
import { homedir as homedir31 } from "node:os";
|
|
@@ -31072,7 +31274,7 @@ function resolveDeps4(deps) {
|
|
|
31072
31274
|
return {
|
|
31073
31275
|
existsSync: deps.existsSync ?? realExistsSync4,
|
|
31074
31276
|
readFileSync: deps.readFileSync ?? realReadFileSync4,
|
|
31075
|
-
statSync: deps.statSync ??
|
|
31277
|
+
statSync: deps.statSync ?? realStatSync2,
|
|
31076
31278
|
agentsDir: join54(home2, ".switchroom", "agents"),
|
|
31077
31279
|
now: deps.now ?? Date.now,
|
|
31078
31280
|
vaultAclReader: deps.vaultAclReader ?? (async () => ({ kind: "unreachable", msg: "no default reader wired" }))
|
|
@@ -31271,7 +31473,7 @@ var init_doctor_notion = __esm(() => {
|
|
|
31271
31473
|
import {
|
|
31272
31474
|
existsSync as realExistsSync5,
|
|
31273
31475
|
readdirSync as realReaddirSync2,
|
|
31274
|
-
statSync as
|
|
31476
|
+
statSync as realStatSync3
|
|
31275
31477
|
} from "node:fs";
|
|
31276
31478
|
import { homedir as homedir32 } from "node:os";
|
|
31277
31479
|
import { join as join55 } from "node:path";
|
|
@@ -31281,7 +31483,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
|
|
|
31281
31483
|
const readdirSync21 = deps.readdirSync ?? ((p) => realReaddirSync2(p));
|
|
31282
31484
|
const isDirectory = deps.isDirectory ?? ((p) => {
|
|
31283
31485
|
try {
|
|
31284
|
-
return
|
|
31486
|
+
return realStatSync3(p).isDirectory();
|
|
31285
31487
|
} catch {
|
|
31286
31488
|
return false;
|
|
31287
31489
|
}
|
|
@@ -31923,7 +32125,7 @@ import {
|
|
|
31923
32125
|
readdirSync as readdirSync21,
|
|
31924
32126
|
statSync as statSync25
|
|
31925
32127
|
} from "node:fs";
|
|
31926
|
-
import { dirname as dirname13, join as join59, resolve as
|
|
32128
|
+
import { dirname as dirname13, join as join59, resolve as resolve33 } from "node:path";
|
|
31927
32129
|
import { createPublicKey, createPrivateKey } from "node:crypto";
|
|
31928
32130
|
function findInNvm(bin) {
|
|
31929
32131
|
const nvmRoot = join59(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
@@ -32937,7 +33139,7 @@ function checkAgents(config, configPath) {
|
|
|
32937
33139
|
const statuses = getAllAgentStatuses(config);
|
|
32938
33140
|
const authStatuses = getAllAuthStatuses(config);
|
|
32939
33141
|
for (const [name, agentConfig] of Object.entries(config.agents)) {
|
|
32940
|
-
const agentDir =
|
|
33142
|
+
const agentDir = resolve33(agentsDir, name);
|
|
32941
33143
|
if (!existsSync57(agentDir)) {
|
|
32942
33144
|
results.push({
|
|
32943
33145
|
name: `${name}: scaffold`,
|
|
@@ -33120,7 +33322,7 @@ function mffAgentName(config) {
|
|
|
33120
33322
|
function mffEnvPath(config) {
|
|
33121
33323
|
const home2 = process.env.HOME ?? "/root";
|
|
33122
33324
|
const agent = mffAgentName(config);
|
|
33123
|
-
return agent ?
|
|
33325
|
+
return agent ? resolve33(home2, ".switchroom/credentials", agent, "my-family-finance/.env") : resolve33(home2, ".switchroom/credentials/my-family-finance/.env");
|
|
33124
33326
|
}
|
|
33125
33327
|
function mffEnvState(envPath) {
|
|
33126
33328
|
if (!existsSync57(envPath))
|
|
@@ -33536,14 +33738,14 @@ async function checkManifestDrift(probers) {
|
|
|
33536
33738
|
return results;
|
|
33537
33739
|
}
|
|
33538
33740
|
function runDockerSection(config) {
|
|
33539
|
-
const composePath =
|
|
33741
|
+
const composePath = resolve33(process.env.HOME ?? "", ".switchroom", "compose", "docker-compose.yml");
|
|
33540
33742
|
const active = isDockerMode({ composePath });
|
|
33541
33743
|
let composeYaml;
|
|
33542
33744
|
let dockerfileAgent;
|
|
33543
33745
|
try {
|
|
33544
33746
|
composeYaml = readFileSync51(composePath, "utf8");
|
|
33545
33747
|
} catch {}
|
|
33546
|
-
const dockerfilePath =
|
|
33748
|
+
const dockerfilePath = resolve33(process.env.HOME ?? "", ".switchroom", "docker", "Dockerfile.agent");
|
|
33547
33749
|
try {
|
|
33548
33750
|
dockerfileAgent = readFileSync51(dockerfilePath, "utf8");
|
|
33549
33751
|
} catch {}
|
|
@@ -33668,7 +33870,8 @@ function registerDoctorCommand(program3) {
|
|
|
33668
33870
|
})
|
|
33669
33871
|
},
|
|
33670
33872
|
{ title: "MFF Skill", results: await checkMff(passphrase, vaultPath, config) },
|
|
33671
|
-
{ title: "Webkite", results: runWebkiteChecks(config) }
|
|
33873
|
+
{ title: "Webkite", results: runWebkiteChecks(config) },
|
|
33874
|
+
{ title: "Cron Session", results: runCronSessionChecks(config) }
|
|
33672
33875
|
];
|
|
33673
33876
|
const cwd = process.cwd();
|
|
33674
33877
|
if (isSwitchroomCheckout(cwd)) {
|
|
@@ -33730,6 +33933,7 @@ var init_doctor = __esm(() => {
|
|
|
33730
33933
|
init_doctor_hostd();
|
|
33731
33934
|
init_doctor_drive();
|
|
33732
33935
|
init_doctor_webkite();
|
|
33936
|
+
init_doctor_cron_session();
|
|
33733
33937
|
init_doctor_microsoft();
|
|
33734
33938
|
init_doctor_notion();
|
|
33735
33939
|
init_doctor_credentials_migration();
|
|
@@ -41914,7 +42118,7 @@ class Protocol {
|
|
|
41914
42118
|
return;
|
|
41915
42119
|
}
|
|
41916
42120
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
|
|
41917
|
-
await new Promise((
|
|
42121
|
+
await new Promise((resolve50) => setTimeout(resolve50, pollInterval));
|
|
41918
42122
|
options?.signal?.throwIfAborted();
|
|
41919
42123
|
}
|
|
41920
42124
|
} catch (error2) {
|
|
@@ -41926,7 +42130,7 @@ class Protocol {
|
|
|
41926
42130
|
}
|
|
41927
42131
|
request(request, resultSchema, options) {
|
|
41928
42132
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
41929
|
-
return new Promise((
|
|
42133
|
+
return new Promise((resolve50, reject) => {
|
|
41930
42134
|
const earlyReject = (error2) => {
|
|
41931
42135
|
reject(error2);
|
|
41932
42136
|
};
|
|
@@ -42004,7 +42208,7 @@ class Protocol {
|
|
|
42004
42208
|
if (!parseResult.success) {
|
|
42005
42209
|
reject(parseResult.error);
|
|
42006
42210
|
} else {
|
|
42007
|
-
|
|
42211
|
+
resolve50(parseResult.data);
|
|
42008
42212
|
}
|
|
42009
42213
|
} catch (error2) {
|
|
42010
42214
|
reject(error2);
|
|
@@ -42195,12 +42399,12 @@ class Protocol {
|
|
|
42195
42399
|
interval = task.pollInterval;
|
|
42196
42400
|
}
|
|
42197
42401
|
} catch {}
|
|
42198
|
-
return new Promise((
|
|
42402
|
+
return new Promise((resolve50, reject) => {
|
|
42199
42403
|
if (signal.aborted) {
|
|
42200
42404
|
reject(new McpError(ErrorCode2.InvalidRequest, "Request cancelled"));
|
|
42201
42405
|
return;
|
|
42202
42406
|
}
|
|
42203
|
-
const timeoutId = setTimeout(
|
|
42407
|
+
const timeoutId = setTimeout(resolve50, interval);
|
|
42204
42408
|
signal.addEventListener("abort", () => {
|
|
42205
42409
|
clearTimeout(timeoutId);
|
|
42206
42410
|
reject(new McpError(ErrorCode2.InvalidRequest, "Request cancelled"));
|
|
@@ -45185,7 +45389,7 @@ var require_compile = __commonJS((exports2) => {
|
|
|
45185
45389
|
const schOrFunc = root.refs[ref];
|
|
45186
45390
|
if (schOrFunc)
|
|
45187
45391
|
return schOrFunc;
|
|
45188
|
-
let _sch =
|
|
45392
|
+
let _sch = resolve50.call(this, root, ref);
|
|
45189
45393
|
if (_sch === undefined) {
|
|
45190
45394
|
const schema = (_a = root.localRefs) === null || _a === undefined ? undefined : _a[ref];
|
|
45191
45395
|
const { schemaId } = this.opts;
|
|
@@ -45212,7 +45416,7 @@ var require_compile = __commonJS((exports2) => {
|
|
|
45212
45416
|
function sameSchemaEnv(s1, s2) {
|
|
45213
45417
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
45214
45418
|
}
|
|
45215
|
-
function
|
|
45419
|
+
function resolve50(root, ref) {
|
|
45216
45420
|
let sch;
|
|
45217
45421
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
45218
45422
|
ref = sch;
|
|
@@ -45742,7 +45946,7 @@ var require_fast_uri = __commonJS((exports2, module) => {
|
|
|
45742
45946
|
}
|
|
45743
45947
|
return uri;
|
|
45744
45948
|
}
|
|
45745
|
-
function
|
|
45949
|
+
function resolve50(baseURI, relativeURI, options) {
|
|
45746
45950
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
45747
45951
|
const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
45748
45952
|
schemelessOptions.skipEscape = true;
|
|
@@ -45970,7 +46174,7 @@ var require_fast_uri = __commonJS((exports2, module) => {
|
|
|
45970
46174
|
var fastUri = {
|
|
45971
46175
|
SCHEMES,
|
|
45972
46176
|
normalize,
|
|
45973
|
-
resolve:
|
|
46177
|
+
resolve: resolve50,
|
|
45974
46178
|
resolveComponent,
|
|
45975
46179
|
equal,
|
|
45976
46180
|
serialize,
|
|
@@ -49353,12 +49557,12 @@ class StdioServerTransport {
|
|
|
49353
49557
|
this.onclose?.();
|
|
49354
49558
|
}
|
|
49355
49559
|
send(message) {
|
|
49356
|
-
return new Promise((
|
|
49560
|
+
return new Promise((resolve50) => {
|
|
49357
49561
|
const json = serializeMessage(message);
|
|
49358
49562
|
if (this._stdout.write(json)) {
|
|
49359
|
-
|
|
49563
|
+
resolve50();
|
|
49360
49564
|
} else {
|
|
49361
|
-
this._stdout.once("drain",
|
|
49565
|
+
this._stdout.once("drain", resolve50);
|
|
49362
49566
|
}
|
|
49363
49567
|
});
|
|
49364
49568
|
}
|
|
@@ -50263,8 +50467,8 @@ var {
|
|
|
50263
50467
|
} = import__.default;
|
|
50264
50468
|
|
|
50265
50469
|
// src/build-info.ts
|
|
50266
|
-
var VERSION = "0.15.
|
|
50267
|
-
var COMMIT_SHA = "
|
|
50470
|
+
var VERSION = "0.15.15";
|
|
50471
|
+
var COMMIT_SHA = "815adf85";
|
|
50268
50472
|
|
|
50269
50473
|
// src/cli/agent.ts
|
|
50270
50474
|
init_source();
|
|
@@ -50364,7 +50568,7 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
50364
50568
|
// src/agents/cron-unit-name.ts
|
|
50365
50569
|
import { createHash } from "node:crypto";
|
|
50366
50570
|
function cronUnitHash(cron, prompt) {
|
|
50367
|
-
return createHash("sha256").update(cron).update("\x00").update(prompt).digest("hex").slice(0, 12);
|
|
50571
|
+
return createHash("sha256").update(cron).update("\x00").update(prompt ?? "").digest("hex").slice(0, 12);
|
|
50368
50572
|
}
|
|
50369
50573
|
function cronUnitName(cron, prompt) {
|
|
50370
50574
|
return `cron-${cronUnitHash(cron, prompt)}`;
|
|
@@ -50913,8 +51117,8 @@ function findExistingClaudeJson() {
|
|
|
50913
51117
|
console.warn(" Alternatively, agents can be onboarded individually via `switchroom agent attach <name>`.");
|
|
50914
51118
|
return null;
|
|
50915
51119
|
}
|
|
50916
|
-
function copyOnboardingState(sourcePath, agentDir) {
|
|
50917
|
-
const claudeDir = join7(agentDir,
|
|
51120
|
+
function copyOnboardingState(sourcePath, agentDir, configDirName = ".claude") {
|
|
51121
|
+
const claudeDir = join7(agentDir, configDirName);
|
|
50918
51122
|
mkdirSync7(claudeDir, { recursive: true });
|
|
50919
51123
|
const destPath = join7(claudeDir, ".claude.json");
|
|
50920
51124
|
if (!existsSync11(destPath)) {
|
|
@@ -50990,8 +51194,8 @@ function loadUserConfig() {
|
|
|
50990
51194
|
return null;
|
|
50991
51195
|
}
|
|
50992
51196
|
}
|
|
50993
|
-
function preTrustWorkspace(agentDir) {
|
|
50994
|
-
const configPath = join7(agentDir,
|
|
51197
|
+
function preTrustWorkspace(agentDir, configDirName = ".claude") {
|
|
51198
|
+
const configPath = join7(agentDir, configDirName, ".claude.json");
|
|
50995
51199
|
if (!existsSync11(configPath)) {
|
|
50996
51200
|
return;
|
|
50997
51201
|
}
|
|
@@ -51015,8 +51219,8 @@ function preTrustWorkspace(agentDir) {
|
|
|
51015
51219
|
});
|
|
51016
51220
|
} catch {}
|
|
51017
51221
|
}
|
|
51018
|
-
function ensureMcpServersTrusted(agentDir, serverKeys) {
|
|
51019
|
-
const configPath = join7(agentDir,
|
|
51222
|
+
function ensureMcpServersTrusted(agentDir, serverKeys, configDirName = ".claude") {
|
|
51223
|
+
const configPath = join7(agentDir, configDirName, ".claude.json");
|
|
51020
51224
|
if (!existsSync11(configPath)) {
|
|
51021
51225
|
return;
|
|
51022
51226
|
}
|
|
@@ -51044,8 +51248,8 @@ function ensureMcpServersTrusted(agentDir, serverKeys) {
|
|
|
51044
51248
|
console.warn(` WARNING: could not update MCP trust allowlist in ${configPath} ` + `(${err instanceof Error ? err.message : String(err)}). ` + `Scaffolded MCP servers (gdrive, agent-config, hostd) may be ` + `silently ignored by Claude Code for this agent.`);
|
|
51045
51249
|
}
|
|
51046
51250
|
}
|
|
51047
|
-
function createMinimalClaudeConfig(agentDir) {
|
|
51048
|
-
const claudeDir = join7(agentDir,
|
|
51251
|
+
function createMinimalClaudeConfig(agentDir, configDirName = ".claude") {
|
|
51252
|
+
const claudeDir = join7(agentDir, configDirName);
|
|
51049
51253
|
mkdirSync7(claudeDir, { recursive: true });
|
|
51050
51254
|
const configPath = join7(claudeDir, ".claude.json");
|
|
51051
51255
|
if (!existsSync11(configPath)) {
|
|
@@ -51060,6 +51264,11 @@ function createMinimalClaudeConfig(agentDir) {
|
|
|
51060
51264
|
});
|
|
51061
51265
|
}
|
|
51062
51266
|
}
|
|
51267
|
+
function seedCronConfigDir(agentDir, serverKeys) {
|
|
51268
|
+
createMinimalClaudeConfig(agentDir, ".claude-cron");
|
|
51269
|
+
preTrustWorkspace(agentDir, ".claude-cron");
|
|
51270
|
+
ensureMcpServersTrusted(agentDir, serverKeys, ".claude-cron");
|
|
51271
|
+
}
|
|
51063
51272
|
|
|
51064
51273
|
// src/repos/bare-clone.ts
|
|
51065
51274
|
init_paths();
|
|
@@ -51524,20 +51733,33 @@ function buildCronSessionContext(agentConfig) {
|
|
|
51524
51733
|
cronModelQ: shellSingleQuote(DEFAULT_CRON_MODEL)
|
|
51525
51734
|
};
|
|
51526
51735
|
}
|
|
51527
|
-
function
|
|
51736
|
+
function maybeWriteCronMcp(agentDir, mcpServers, cronSessionEnabled) {
|
|
51528
51737
|
if (!cronSessionEnabled)
|
|
51529
51738
|
return null;
|
|
51530
51739
|
const telegram = mcpServers["switchroom-telegram"];
|
|
51531
51740
|
if (!telegram)
|
|
51532
51741
|
return null;
|
|
51742
|
+
const baseStateDir = telegram.env?.TELEGRAM_STATE_DIR;
|
|
51743
|
+
const cronTelegram = {
|
|
51744
|
+
...telegram,
|
|
51745
|
+
env: {
|
|
51746
|
+
...telegram.env,
|
|
51747
|
+
...baseStateDir ? { SWITCHROOM_BRIDGE_ALIVE_PATH: `${baseStateDir}/.bridge-alive-cron` } : {}
|
|
51748
|
+
}
|
|
51749
|
+
};
|
|
51750
|
+
const cronServers = {
|
|
51751
|
+
...mcpServers,
|
|
51752
|
+
"switchroom-telegram": cronTelegram
|
|
51753
|
+
};
|
|
51533
51754
|
const cronDir = join9(agentDir, ".claude-cron");
|
|
51534
51755
|
mkdirSync10(cronDir, { recursive: true });
|
|
51535
51756
|
const path = join9(cronDir, ".mcp.json");
|
|
51536
|
-
const content = JSON.stringify({ mcpServers:
|
|
51757
|
+
const content = JSON.stringify({ mcpServers: cronServers }, null, 2) + `
|
|
51537
51758
|
`;
|
|
51538
51759
|
if (!existsSync14(path) || readFileSync12(path, "utf-8") !== content) {
|
|
51539
51760
|
writeFileSync5(path, content, { encoding: "utf-8", mode: 384 });
|
|
51540
51761
|
}
|
|
51762
|
+
seedCronConfigDir(agentDir, Object.keys(cronServers));
|
|
51541
51763
|
return path;
|
|
51542
51764
|
}
|
|
51543
51765
|
function alignAgentUid(name, agentDir, uid, opts = {}) {
|
|
@@ -52730,7 +52952,7 @@ function scaffoldAgent(name, agentConfigRaw, agentsDir, telegramConfig, switchro
|
|
|
52730
52952
|
}
|
|
52731
52953
|
writeIfChanged(mcpJsonPath, () => JSON.stringify({ mcpServers }, null, 2) + `
|
|
52732
52954
|
`, created, skipped, 384);
|
|
52733
|
-
|
|
52955
|
+
maybeWriteCronMcp(agentDir, mcpServers, buildCronSessionContext(agentConfig).cronSessionEnabled);
|
|
52734
52956
|
mcpServerKeysToTrust = Object.keys(mcpServers);
|
|
52735
52957
|
ensureMcpServersTrusted(agentDir, mcpServerKeysToTrust);
|
|
52736
52958
|
}
|
|
@@ -53805,9 +54027,9 @@ ${body}
|
|
|
53805
54027
|
writeFileSync5(mcpJsonPath, after, { encoding: "utf-8", mode: 384 });
|
|
53806
54028
|
changes.push(mcpJsonPath);
|
|
53807
54029
|
}
|
|
53808
|
-
const
|
|
53809
|
-
if (
|
|
53810
|
-
changes.push(
|
|
54030
|
+
const cronMcp = maybeWriteCronMcp(agentDir, mcpServers, buildCronSessionContext(agentConfig).cronSessionEnabled);
|
|
54031
|
+
if (cronMcp)
|
|
54032
|
+
changes.push(cronMcp);
|
|
53811
54033
|
ensureMcpServersTrusted(agentDir, Object.keys(mcpServers));
|
|
53812
54034
|
}
|
|
53813
54035
|
const reconcileWorkspaceDir = join9(agentDir, "workspace");
|
|
@@ -68374,17 +68596,19 @@ function collectScheduleEntries(config) {
|
|
|
68374
68596
|
const schedule = resolved.schedule ?? [];
|
|
68375
68597
|
for (let i = 0;i < schedule.length; i++) {
|
|
68376
68598
|
const entry = schedule[i];
|
|
68599
|
+
const auditMaterial = entry.prompt ?? `action:${JSON.stringify(entry.action ?? {})}`;
|
|
68377
68600
|
out.push({
|
|
68378
68601
|
agent,
|
|
68379
68602
|
scheduleIndex: i,
|
|
68380
68603
|
cron: entry.cron,
|
|
68381
|
-
prompt: entry.prompt,
|
|
68382
|
-
promptKey: createHash9("sha256").update(
|
|
68604
|
+
...entry.prompt !== undefined ? { prompt: entry.prompt } : {},
|
|
68605
|
+
promptKey: createHash9("sha256").update(auditMaterial).digest("hex").slice(0, 12),
|
|
68383
68606
|
...entry.topic !== undefined ? { topic: entry.topic } : {},
|
|
68384
68607
|
...entry.kind !== undefined ? { kind: entry.kind } : {},
|
|
68385
68608
|
...entry.model !== undefined ? { model: entry.model } : {},
|
|
68386
68609
|
...entry.context !== undefined ? { context: entry.context } : {},
|
|
68387
|
-
...entry.poll !== undefined ? { poll: entry.poll } : {}
|
|
68610
|
+
...entry.poll !== undefined ? { poll: entry.poll } : {},
|
|
68611
|
+
...entry.action !== undefined ? { action: entry.action } : {}
|
|
68388
68612
|
});
|
|
68389
68613
|
}
|
|
68390
68614
|
}
|
|
@@ -76101,7 +76325,7 @@ init_loader();
|
|
|
76101
76325
|
init_lifecycle();
|
|
76102
76326
|
import { cpSync as cpSync2, existsSync as existsSync58, mkdirSync as mkdirSync32, readFileSync as readFileSync52, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync26 } from "node:fs";
|
|
76103
76327
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
76104
|
-
import { join as join60, dirname as dirname14, resolve as
|
|
76328
|
+
import { join as join60, dirname as dirname14, resolve as resolve34 } from "node:path";
|
|
76105
76329
|
import { homedir as homedir36 } from "node:os";
|
|
76106
76330
|
var DEFAULT_COMPOSE_PATH = join60(homedir36(), ".switchroom", "compose", "docker-compose.yml");
|
|
76107
76331
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
@@ -76259,7 +76483,7 @@ function planUpdate(opts) {
|
|
|
76259
76483
|
opts.syncBundledSkillsFn();
|
|
76260
76484
|
return;
|
|
76261
76485
|
}
|
|
76262
|
-
const source =
|
|
76486
|
+
const source = resolve34(import.meta.dirname, "../../skills");
|
|
76263
76487
|
const dest = join60(homedir36(), ".switchroom", "skills", "_bundled");
|
|
76264
76488
|
if (!existsSync58(source)) {
|
|
76265
76489
|
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
|
|
@@ -76586,7 +76810,7 @@ init_source();
|
|
|
76586
76810
|
init_helpers();
|
|
76587
76811
|
init_loader();
|
|
76588
76812
|
init_lifecycle();
|
|
76589
|
-
import { resolve as
|
|
76813
|
+
import { resolve as resolve35 } from "node:path";
|
|
76590
76814
|
|
|
76591
76815
|
// src/cli/version.ts
|
|
76592
76816
|
init_source();
|
|
@@ -76741,7 +76965,7 @@ function registerRestartCommand(program3) {
|
|
|
76741
76965
|
}
|
|
76742
76966
|
const didRestart = res.restarted || !graceful;
|
|
76743
76967
|
if (didRestart) {
|
|
76744
|
-
const agentDir =
|
|
76968
|
+
const agentDir = resolve35(agentsDir, name);
|
|
76745
76969
|
const converged = waitForAuthConverge(name, agentDir);
|
|
76746
76970
|
if (!converged) {
|
|
76747
76971
|
console.log(source_default.yellow(` ${name}: agent is up but auth status didn't converge in 30s \u2014 check logs`));
|
|
@@ -76822,7 +77046,7 @@ Dependency manifest`));
|
|
|
76822
77046
|
// src/cli/handoff.ts
|
|
76823
77047
|
init_helpers();
|
|
76824
77048
|
init_loader();
|
|
76825
|
-
import { resolve as
|
|
77049
|
+
import { resolve as resolve36 } from "node:path";
|
|
76826
77050
|
function registerHandoffCommand(program3) {
|
|
76827
77051
|
program3.command("handoff <agent>", { hidden: true }).description("Build the agent's session handoff sidecars \u2014 a transcript-tail " + "briefing (.handoff.md) and topic line (.handoff-topic). " + "[internal \u2014 used by the Stop hook]").option("--max-turns <n>", "Max turns kept in the handoff transcript tail", String(DEFAULT_MAX_TURNS)).action(withConfigError(async (agentName, opts) => {
|
|
76828
77052
|
let agentConfig;
|
|
@@ -76836,7 +77060,7 @@ function registerHandoffCommand(program3) {
|
|
|
76836
77060
|
return;
|
|
76837
77061
|
}
|
|
76838
77062
|
const agentsDir = resolveAgentsDir(config);
|
|
76839
|
-
agentDir =
|
|
77063
|
+
agentDir = resolve36(agentsDir, agentName);
|
|
76840
77064
|
} catch (err) {
|
|
76841
77065
|
if (!(err instanceof ConfigError))
|
|
76842
77066
|
throw err;
|
|
@@ -76851,7 +77075,7 @@ function registerHandoffCommand(program3) {
|
|
|
76851
77075
|
`);
|
|
76852
77076
|
return;
|
|
76853
77077
|
}
|
|
76854
|
-
const claudeConfigDir =
|
|
77078
|
+
const claudeConfigDir = resolve36(agentDir, ".claude");
|
|
76855
77079
|
const jsonl = findLatestSessionJsonl(claudeConfigDir);
|
|
76856
77080
|
if (!jsonl) {
|
|
76857
77081
|
process.stderr.write(`handoff: no session JSONL under ${claudeConfigDir}/projects; skipping
|
|
@@ -77360,7 +77584,7 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
77360
77584
|
return result;
|
|
77361
77585
|
});
|
|
77362
77586
|
}
|
|
77363
|
-
function
|
|
77587
|
+
function resolve37(stateDir, fingerprint, nowFn = Date.now) {
|
|
77364
77588
|
if (!existsSync60(join62(stateDir, ISSUES_FILE)))
|
|
77365
77589
|
return 0;
|
|
77366
77590
|
return withLock(stateDir, () => {
|
|
@@ -77648,11 +77872,11 @@ function registerIssuesCommand(program3) {
|
|
|
77648
77872
|
const stateDir = resolveStateDir2(opts);
|
|
77649
77873
|
let flipped;
|
|
77650
77874
|
if (opts.source && opts.code) {
|
|
77651
|
-
flipped =
|
|
77875
|
+
flipped = resolve37(stateDir, computeFingerprint(opts.source, opts.code));
|
|
77652
77876
|
} else if (opts.source && !fingerprint) {
|
|
77653
77877
|
flipped = resolveAllBySource(stateDir, opts.source);
|
|
77654
77878
|
} else if (fingerprint) {
|
|
77655
|
-
flipped =
|
|
77879
|
+
flipped = resolve37(stateDir, fingerprint);
|
|
77656
77880
|
} else {
|
|
77657
77881
|
process.stderr.write(`issues resolve: need either <fingerprint>, --source, or --source + --code
|
|
77658
77882
|
`);
|
|
@@ -77749,7 +77973,7 @@ function relTime(deltaMs) {
|
|
|
77749
77973
|
init_source();
|
|
77750
77974
|
import { existsSync as existsSync63 } from "node:fs";
|
|
77751
77975
|
import { homedir as homedir39 } from "node:os";
|
|
77752
|
-
import { join as join65, resolve as
|
|
77976
|
+
import { join as join65, resolve as resolve38 } from "node:path";
|
|
77753
77977
|
|
|
77754
77978
|
// src/deps/python.ts
|
|
77755
77979
|
import { createHash as createHash11 } from "node:crypto";
|
|
@@ -77956,7 +78180,7 @@ function ensureNodeEnv(opts) {
|
|
|
77956
78180
|
|
|
77957
78181
|
// src/cli/deps.ts
|
|
77958
78182
|
function builtinSkillsRoot() {
|
|
77959
|
-
return
|
|
78183
|
+
return resolve38(homedir39(), ".switchroom/skills/_bundled");
|
|
77960
78184
|
}
|
|
77961
78185
|
function registerDepsCommand(program3) {
|
|
77962
78186
|
const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
|
|
@@ -78035,7 +78259,7 @@ function registerDepsCommand(program3) {
|
|
|
78035
78259
|
init_helpers();
|
|
78036
78260
|
init_loader();
|
|
78037
78261
|
import { existsSync as existsSync64 } from "node:fs";
|
|
78038
|
-
import { resolve as
|
|
78262
|
+
import { resolve as resolve39, sep as sep3 } from "node:path";
|
|
78039
78263
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
78040
78264
|
|
|
78041
78265
|
// src/agents/workspace.ts
|
|
@@ -78741,8 +78965,8 @@ function registerWorkspaceCommand(program3) {
|
|
|
78741
78965
|
const dir = resolveAgentWorkspaceDirOrExit(program3, agentName);
|
|
78742
78966
|
if (!dir)
|
|
78743
78967
|
return;
|
|
78744
|
-
const resolvedWorkspace =
|
|
78745
|
-
const target =
|
|
78968
|
+
const resolvedWorkspace = resolve39(dir);
|
|
78969
|
+
const target = resolve39(resolvedWorkspace, file ?? "AGENTS.md");
|
|
78746
78970
|
if (!isInsideWorkspace(resolvedWorkspace, target)) {
|
|
78747
78971
|
process.stderr.write(`workspace edit: refusing path traversal outside workspace dir (${target})
|
|
78748
78972
|
`);
|
|
@@ -78810,7 +79034,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
78810
79034
|
const dir = resolveAgentWorkspaceDirOrExit(program3, agentName);
|
|
78811
79035
|
if (!dir)
|
|
78812
79036
|
return;
|
|
78813
|
-
const gitDir =
|
|
79037
|
+
const gitDir = resolve39(dir, ".git");
|
|
78814
79038
|
if (!existsSync64(gitDir)) {
|
|
78815
79039
|
process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
|
|
78816
79040
|
`);
|
|
@@ -78864,7 +79088,7 @@ function registerWorkspaceCommand(program3) {
|
|
|
78864
79088
|
const dir = resolveAgentWorkspaceDirOrExit(program3, agentName);
|
|
78865
79089
|
if (!dir)
|
|
78866
79090
|
return;
|
|
78867
|
-
const gitDir =
|
|
79091
|
+
const gitDir = resolve39(dir, ".git");
|
|
78868
79092
|
if (!existsSync64(gitDir)) {
|
|
78869
79093
|
process.stdout.write(`Workspace is not a git repository.
|
|
78870
79094
|
`);
|
|
@@ -78888,7 +79112,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
|
|
|
78888
79112
|
return;
|
|
78889
79113
|
}
|
|
78890
79114
|
const agentsDir = resolveAgentsDir(config);
|
|
78891
|
-
const agentDir =
|
|
79115
|
+
const agentDir = resolve39(agentsDir, agentName);
|
|
78892
79116
|
const dir = resolveAgentWorkspaceDir(agentDir);
|
|
78893
79117
|
if (!existsSync64(dir)) {
|
|
78894
79118
|
process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
|
|
@@ -78927,7 +79151,7 @@ init_helpers();
|
|
|
78927
79151
|
init_loader();
|
|
78928
79152
|
init_merge();
|
|
78929
79153
|
import { copyFileSync as copyFileSync10, existsSync as existsSync65, readFileSync as readFileSync57, writeFileSync as writeFileSync31 } from "node:fs";
|
|
78930
|
-
import { join as join66, resolve as
|
|
79154
|
+
import { join as join66, resolve as resolve40 } from "node:path";
|
|
78931
79155
|
init_schema();
|
|
78932
79156
|
function resolveSoulTargetOrExit(program3, agentName) {
|
|
78933
79157
|
const config = getConfig(program3);
|
|
@@ -78940,7 +79164,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
78940
79164
|
const profileName = merged.extends ?? DEFAULT_PROFILE;
|
|
78941
79165
|
const profilePath = getProfilePath(profileName);
|
|
78942
79166
|
const agentsDir = resolveAgentsDir(config);
|
|
78943
|
-
const agentDir =
|
|
79167
|
+
const agentDir = resolve40(agentsDir, agentName);
|
|
78944
79168
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
78945
79169
|
if (!existsSync65(workspaceDir)) {
|
|
78946
79170
|
console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
|
|
@@ -79018,7 +79242,7 @@ function registerSoulCommand(program3) {
|
|
|
79018
79242
|
init_helpers();
|
|
79019
79243
|
init_loader();
|
|
79020
79244
|
import { existsSync as existsSync66, readFileSync as readFileSync58, readdirSync as readdirSync23, statSync as statSync28 } from "node:fs";
|
|
79021
|
-
import { resolve as
|
|
79245
|
+
import { resolve as resolve41, join as join67 } from "node:path";
|
|
79022
79246
|
import { createHash as createHash13 } from "node:crypto";
|
|
79023
79247
|
init_merge();
|
|
79024
79248
|
init_hindsight();
|
|
@@ -79116,7 +79340,7 @@ function registerDebugCommand(program3) {
|
|
|
79116
79340
|
process.exit(1);
|
|
79117
79341
|
}
|
|
79118
79342
|
const agentsDir = resolveAgentsDir(config);
|
|
79119
|
-
const agentDir =
|
|
79343
|
+
const agentDir = resolve41(agentsDir, agentName);
|
|
79120
79344
|
if (!existsSync66(agentDir)) {
|
|
79121
79345
|
console.error(`Agent directory not found: ${agentDir}`);
|
|
79122
79346
|
process.exit(1);
|
|
@@ -79293,7 +79517,7 @@ init_source();
|
|
|
79293
79517
|
// src/worktree/claim.ts
|
|
79294
79518
|
import { execFileSync as execFileSync21 } from "node:child_process";
|
|
79295
79519
|
import { closeSync as closeSync12, mkdirSync as mkdirSync37, openSync as openSync12, existsSync as existsSync68, unlinkSync as unlinkSync13 } from "node:fs";
|
|
79296
|
-
import { join as join69, resolve as
|
|
79520
|
+
import { join as join69, resolve as resolve43 } from "node:path";
|
|
79297
79521
|
import { homedir as homedir41 } from "node:os";
|
|
79298
79522
|
import { randomBytes as randomBytes13 } from "node:crypto";
|
|
79299
79523
|
|
|
@@ -79307,10 +79531,10 @@ import {
|
|
|
79307
79531
|
existsSync as existsSync67,
|
|
79308
79532
|
renameSync as renameSync13
|
|
79309
79533
|
} from "node:fs";
|
|
79310
|
-
import { join as join68, resolve as
|
|
79534
|
+
import { join as join68, resolve as resolve42 } from "node:path";
|
|
79311
79535
|
import { homedir as homedir40 } from "node:os";
|
|
79312
79536
|
function registryDir() {
|
|
79313
|
-
return
|
|
79537
|
+
return resolve42(process.env.SWITCHROOM_WORKTREE_DIR ?? join68(homedir40(), ".switchroom", "worktrees"));
|
|
79314
79538
|
}
|
|
79315
79539
|
function recordPath(id) {
|
|
79316
79540
|
return join68(registryDir(), `${id}.json`);
|
|
@@ -79391,7 +79615,7 @@ function acquireRepoLock(repoPath) {
|
|
|
79391
79615
|
}
|
|
79392
79616
|
var DEFAULT_CONCURRENCY = 5;
|
|
79393
79617
|
function worktreesBaseDir() {
|
|
79394
|
-
return
|
|
79618
|
+
return resolve43(process.env.SWITCHROOM_WORKTREE_BASE ?? join69(homedir41(), ".switchroom", "worktree-checkouts"));
|
|
79395
79619
|
}
|
|
79396
79620
|
function shortId() {
|
|
79397
79621
|
return randomBytes13(4).toString("hex");
|
|
@@ -80111,20 +80335,20 @@ function wireStdio(child) {
|
|
|
80111
80335
|
async function killChild(child, gracefulMs = 3000) {
|
|
80112
80336
|
if (child.exitCode !== null || child.signalCode !== null)
|
|
80113
80337
|
return;
|
|
80114
|
-
return new Promise((
|
|
80338
|
+
return new Promise((resolve44) => {
|
|
80115
80339
|
let killTimer = null;
|
|
80116
80340
|
const onExit = () => {
|
|
80117
80341
|
if (killTimer) {
|
|
80118
80342
|
clearTimeout(killTimer);
|
|
80119
80343
|
killTimer = null;
|
|
80120
80344
|
}
|
|
80121
|
-
|
|
80345
|
+
resolve44();
|
|
80122
80346
|
};
|
|
80123
80347
|
child.once("exit", onExit);
|
|
80124
80348
|
try {
|
|
80125
80349
|
child.kill("SIGTERM");
|
|
80126
80350
|
} catch {
|
|
80127
|
-
|
|
80351
|
+
resolve44();
|
|
80128
80352
|
return;
|
|
80129
80353
|
}
|
|
80130
80354
|
killTimer = setTimeout(() => {
|
|
@@ -80238,8 +80462,8 @@ async function runMs365McpLauncher(opts, rt) {
|
|
|
80238
80462
|
};
|
|
80239
80463
|
process.on("SIGINT", onSignal);
|
|
80240
80464
|
process.on("SIGTERM", onSignal);
|
|
80241
|
-
return new Promise((
|
|
80242
|
-
resolveLauncher =
|
|
80465
|
+
return new Promise((resolve44) => {
|
|
80466
|
+
resolveLauncher = resolve44;
|
|
80243
80467
|
});
|
|
80244
80468
|
}
|
|
80245
80469
|
function registerM365McpLauncherCommand(program3) {
|
|
@@ -80341,23 +80565,23 @@ async function runNotionMcpLauncher(opts, runtime) {
|
|
|
80341
80565
|
};
|
|
80342
80566
|
process.on("SIGTERM", () => forward("SIGTERM"));
|
|
80343
80567
|
process.on("SIGINT", () => forward("SIGINT"));
|
|
80344
|
-
const exitCode = await new Promise((
|
|
80568
|
+
const exitCode = await new Promise((resolve44) => {
|
|
80345
80569
|
child.once("exit", (code, signal) => {
|
|
80346
80570
|
clearTimer(heartbeatHandle);
|
|
80347
80571
|
if (typeof code === "number") {
|
|
80348
|
-
|
|
80572
|
+
resolve44(code);
|
|
80349
80573
|
} else if (signal) {
|
|
80350
80574
|
const sigCode = { SIGTERM: 15, SIGINT: 2, SIGKILL: 9 }[signal] ?? 1;
|
|
80351
|
-
|
|
80575
|
+
resolve44(128 + sigCode);
|
|
80352
80576
|
} else {
|
|
80353
|
-
|
|
80577
|
+
resolve44(1);
|
|
80354
80578
|
}
|
|
80355
80579
|
});
|
|
80356
80580
|
child.once("error", (err) => {
|
|
80357
80581
|
clearTimer(heartbeatHandle);
|
|
80358
80582
|
process.stderr.write(`notion-mcp-launcher: child spawn error: ${err.message}
|
|
80359
80583
|
`);
|
|
80360
|
-
|
|
80584
|
+
resolve44(1);
|
|
80361
80585
|
});
|
|
80362
80586
|
});
|
|
80363
80587
|
return exitCode;
|
|
@@ -81415,7 +81639,7 @@ agents:
|
|
|
81415
81639
|
|
|
81416
81640
|
// src/cli/apply.ts
|
|
81417
81641
|
init_resolver();
|
|
81418
|
-
import { dirname as dirname22, join as join74, resolve as
|
|
81642
|
+
import { dirname as dirname22, join as join74, resolve as resolve45 } from "node:path";
|
|
81419
81643
|
import { homedir as homedir43 } from "node:os";
|
|
81420
81644
|
import { execFileSync as execFileSync24 } from "node:child_process";
|
|
81421
81645
|
init_vault();
|
|
@@ -82016,7 +82240,7 @@ function copyExampleConfig2(name) {
|
|
|
82016
82240
|
if (!/^[a-z0-9_-]+$/.test(name)) {
|
|
82017
82241
|
throw new Error(`Invalid example name: ${name} (must match /^[a-z0-9_-]+$/)`);
|
|
82018
82242
|
}
|
|
82019
|
-
const dest =
|
|
82243
|
+
const dest = resolve45(process.cwd(), "switchroom.yaml");
|
|
82020
82244
|
if (existsSync74(dest)) {
|
|
82021
82245
|
console.error(source_default.yellow("switchroom.yaml already exists \u2014 skipping example copy"));
|
|
82022
82246
|
return;
|
|
@@ -82027,7 +82251,7 @@ function copyExampleConfig2(name) {
|
|
|
82027
82251
|
console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
|
|
82028
82252
|
return;
|
|
82029
82253
|
}
|
|
82030
|
-
const exampleFile =
|
|
82254
|
+
const exampleFile = resolve45(import.meta.dirname, `../../examples/${name}.yaml`);
|
|
82031
82255
|
if (!existsSync74(exampleFile)) {
|
|
82032
82256
|
throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
|
|
82033
82257
|
}
|
|
@@ -82605,10 +82829,10 @@ import {
|
|
|
82605
82829
|
unlinkSync as unlinkSync14,
|
|
82606
82830
|
writeSync as writeSync8
|
|
82607
82831
|
} from "node:fs";
|
|
82608
|
-
import { join as join76, resolve as
|
|
82832
|
+
import { join as join76, resolve as resolve46 } from "node:path";
|
|
82609
82833
|
var STAGING_SUBDIR = ".staging";
|
|
82610
82834
|
function overlayPathsFor(agent, opts = {}) {
|
|
82611
|
-
const base = opts.root ?
|
|
82835
|
+
const base = opts.root ? resolve46(opts.root, agent) : resolve46(resolveDualPath(`~/.switchroom/agents/${agent}`));
|
|
82612
82836
|
const scheduleDir = join76(base, "schedule.d");
|
|
82613
82837
|
const scheduleStagingDir = join76(scheduleDir, STAGING_SUBDIR);
|
|
82614
82838
|
const skillsDir = join76(base, "skills.d");
|
|
@@ -83004,11 +83228,12 @@ import { existsSync as existsSync78, readFileSync as readFileSync66 } from "node
|
|
|
83004
83228
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
83005
83229
|
|
|
83006
83230
|
// src/scheduler/schedule-report.ts
|
|
83007
|
-
var TIER_WEIGHT = { poll: 0, cheap: 1, main: 5 };
|
|
83231
|
+
var TIER_WEIGHT = { poll: 0, action: 0, cheap: 1, main: 5 };
|
|
83008
83232
|
function summarizeScheduleReport(rows, opts = {}) {
|
|
83009
83233
|
const s = {
|
|
83010
83234
|
total: 0,
|
|
83011
83235
|
pollFires: 0,
|
|
83236
|
+
actionFires: 0,
|
|
83012
83237
|
cheapFires: 0,
|
|
83013
83238
|
mainFires: 0,
|
|
83014
83239
|
errors: 0,
|
|
@@ -83023,6 +83248,8 @@ function summarizeScheduleReport(rows, opts = {}) {
|
|
|
83023
83248
|
const tier = r.tier ?? "main";
|
|
83024
83249
|
if (tier === "poll")
|
|
83025
83250
|
s.pollFires += 1;
|
|
83251
|
+
else if (tier === "action")
|
|
83252
|
+
s.actionFires += 1;
|
|
83026
83253
|
else if (tier === "cheap")
|
|
83027
83254
|
s.cheapFires += 1;
|
|
83028
83255
|
else
|
|
@@ -83054,11 +83281,12 @@ function parseScheduleJsonl(blob) {
|
|
|
83054
83281
|
}
|
|
83055
83282
|
function formatScheduleReport(agent, s) {
|
|
83056
83283
|
const modelFires = s.cheapFires + s.mainFires;
|
|
83057
|
-
const
|
|
83284
|
+
const modelFreePct = s.total > 0 ? Math.round((s.pollFires + s.actionFires) / s.total * 100) : 0;
|
|
83058
83285
|
const lines = [
|
|
83059
83286
|
`cron report \u2014 ${agent}`,
|
|
83060
83287
|
` total fires ${s.total}`,
|
|
83061
|
-
` Tier 0 poll (free) ${s.pollFires} (${
|
|
83288
|
+
` Tier 0 poll (free) ${s.pollFires} (${modelFreePct}% model-free incl. actions)`,
|
|
83289
|
+
` Tier 0 action(free)${s.actionFires}`,
|
|
83062
83290
|
` Tier 1 cheap ${s.cheapFires}`,
|
|
83063
83291
|
` Tier 2 main ${s.mainFires}`,
|
|
83064
83292
|
` model fires ${modelFires}`,
|
|
@@ -83860,7 +84088,7 @@ import {
|
|
|
83860
84088
|
writeFileSync as writeFileSync39
|
|
83861
84089
|
} from "node:fs";
|
|
83862
84090
|
import { tmpdir as tmpdir5, homedir as homedir45 } from "node:os";
|
|
83863
|
-
import { dirname as dirname23, join as join79, relative as relative2, resolve as
|
|
84091
|
+
import { dirname as dirname23, join as join79, relative as relative2, resolve as resolve47 } from "node:path";
|
|
83864
84092
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
83865
84093
|
|
|
83866
84094
|
// src/cli/skill-common.ts
|
|
@@ -84058,7 +84286,7 @@ function resolveSkillsPoolDir2(override) {
|
|
|
84058
84286
|
}
|
|
84059
84287
|
if (raw === "~")
|
|
84060
84288
|
return homedir45();
|
|
84061
|
-
return
|
|
84289
|
+
return resolve47(raw);
|
|
84062
84290
|
}
|
|
84063
84291
|
function readStdinSync() {
|
|
84064
84292
|
const chunks = [];
|
|
@@ -84342,7 +84570,7 @@ function registerSkillCommand(program3) {
|
|
|
84342
84570
|
if (opts.from === undefined) {
|
|
84343
84571
|
files = loadFromStdin();
|
|
84344
84572
|
} else {
|
|
84345
|
-
const fromPath =
|
|
84573
|
+
const fromPath = resolve47(opts.from);
|
|
84346
84574
|
if (!existsSync80(fromPath)) {
|
|
84347
84575
|
fail3(`--from path does not exist: ${opts.from}`);
|
|
84348
84576
|
}
|
|
@@ -84411,7 +84639,7 @@ import {
|
|
|
84411
84639
|
utimesSync,
|
|
84412
84640
|
writeFileSync as writeFileSync40
|
|
84413
84641
|
} from "node:fs";
|
|
84414
|
-
import { dirname as dirname24, join as join80, relative as relative3, resolve as
|
|
84642
|
+
import { dirname as dirname24, join as join80, relative as relative3, resolve as resolve48 } from "node:path";
|
|
84415
84643
|
import { homedir as homedir46, tmpdir as tmpdir6 } from "node:os";
|
|
84416
84644
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
84417
84645
|
init_helpers();
|
|
@@ -84422,7 +84650,7 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
|
84422
84650
|
var PERSONAL_SKILLS_SUBPATH = "personal-skills";
|
|
84423
84651
|
function resolveConfigSkillsDir(agent) {
|
|
84424
84652
|
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
84425
|
-
const candidate = override ?
|
|
84653
|
+
const candidate = override ? resolve48(override) : join80(homedir46(), ".switchroom-config");
|
|
84426
84654
|
if (!existsSync81(candidate))
|
|
84427
84655
|
return null;
|
|
84428
84656
|
return join80(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
|
|
@@ -84517,7 +84745,7 @@ function resolveAgent(opts) {
|
|
|
84517
84745
|
}
|
|
84518
84746
|
function resolveAgentsRoot(opts) {
|
|
84519
84747
|
if (opts.root)
|
|
84520
|
-
return
|
|
84748
|
+
return resolve48(opts.root);
|
|
84521
84749
|
return join80(homedir46(), ".switchroom", "agents");
|
|
84522
84750
|
}
|
|
84523
84751
|
function personalSkillDir(agentsRoot, agent, name) {
|
|
@@ -84547,7 +84775,7 @@ function readStdinSync2() {
|
|
|
84547
84775
|
return Buffer.concat(chunks).toString("utf-8");
|
|
84548
84776
|
}
|
|
84549
84777
|
function loadFromDir2(dir) {
|
|
84550
|
-
const abs =
|
|
84778
|
+
const abs = resolve48(dir);
|
|
84551
84779
|
if (!statSync32(abs).isDirectory()) {
|
|
84552
84780
|
fail4(`--from path is not a directory: ${dir}`);
|
|
84553
84781
|
}
|
|
@@ -84741,7 +84969,7 @@ function loadFiles(opts) {
|
|
|
84741
84969
|
if (opts.from === undefined) {
|
|
84742
84970
|
return loadFromStdin2();
|
|
84743
84971
|
}
|
|
84744
|
-
const p =
|
|
84972
|
+
const p = resolve48(opts.from);
|
|
84745
84973
|
if (!existsSync81(p)) {
|
|
84746
84974
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
84747
84975
|
}
|
|
@@ -85010,18 +85238,18 @@ init_helpers();
|
|
|
85010
85238
|
var import_yaml23 = __toESM(require_dist(), 1);
|
|
85011
85239
|
import { existsSync as existsSync82, readdirSync as readdirSync32, readFileSync as readFileSync69, statSync as statSync33 } from "node:fs";
|
|
85012
85240
|
import { homedir as homedir47 } from "node:os";
|
|
85013
|
-
import { join as join81, resolve as
|
|
85241
|
+
import { join as join81, resolve as resolve49 } from "node:path";
|
|
85014
85242
|
var PERSONAL_PREFIX2 = "personal-";
|
|
85015
85243
|
var BUNDLED_SUBDIR = "_bundled";
|
|
85016
85244
|
var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
85017
85245
|
function defaultAgentsRoot() {
|
|
85018
|
-
return
|
|
85246
|
+
return resolve49(homedir47(), ".switchroom/agents");
|
|
85019
85247
|
}
|
|
85020
85248
|
function defaultSharedRoot2() {
|
|
85021
|
-
return
|
|
85249
|
+
return resolve49(homedir47(), ".switchroom/skills");
|
|
85022
85250
|
}
|
|
85023
85251
|
function defaultBundledRoot2() {
|
|
85024
|
-
return
|
|
85252
|
+
return resolve49(homedir47(), ".switchroom/skills/_bundled");
|
|
85025
85253
|
}
|
|
85026
85254
|
function readSkillFrontmatter(skillDir) {
|
|
85027
85255
|
const mdPath = join81(skillDir, "SKILL.md");
|