switchroom 0.16.47 → 0.17.0
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 +3 -1
- package/dist/auth-broker/index.js +24 -8
- package/dist/cli/drive-write-pretool.mjs +0 -5
- package/dist/cli/notion-write-pretool.mjs +3 -1
- package/dist/cli/switchroom.js +1358 -1030
- package/dist/cli/ui/index.html +84 -12
- package/dist/host-control/main.js +53 -17
- package/dist/vault/approvals/kernel-server.js +4 -1
- package/dist/vault/broker/server.js +201 -56
- package/package.json +3 -3
- package/profiles/_base/cron-session.sh.hbs +1 -1
- package/profiles/_base/start.sh.hbs +54 -3
- package/skills/switchroom-architecture/telegram.md +8 -15
- package/skills/switchroom-cli/SKILL.md +4 -5
- package/skills/telegram-test-harness/SKILL.md +1 -1
- package/telegram-plugin/README.md +18 -29
- package/telegram-plugin/bridge/bridge.ts +1 -41
- package/telegram-plugin/bridge/tool-filter.ts +3 -4
- package/telegram-plugin/dist/bridge/bridge.js +8 -43
- package/telegram-plugin/dist/gateway/gateway.js +682 -773
- package/telegram-plugin/dist/server.js +8 -43
- package/telegram-plugin/gateway/busy-key-reaper.ts +113 -0
- package/telegram-plugin/gateway/disconnect-flush.ts +11 -0
- package/telegram-plugin/gateway/escalation-bridge-gate.ts +46 -0
- package/telegram-plugin/gateway/gate-parity-probe.ts +102 -0
- package/telegram-plugin/gateway/gateway.ts +518 -624
- package/telegram-plugin/gateway/inbound-delivery-confirm.ts +89 -7
- package/telegram-plugin/gateway/inbound-spool.ts +108 -10
- package/telegram-plugin/gateway/model-command.ts +51 -3
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +26 -0
- package/telegram-plugin/gateway/represent-guard.ts +28 -11
- package/telegram-plugin/gateway/status-pin-store.ts +124 -45
- package/telegram-plugin/gateway/worker-feed-dispatch.ts +19 -0
- package/telegram-plugin/history.ts +5 -0
- package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +1 -2
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +9 -1
- package/telegram-plugin/registry/subagents-schema.ts +126 -1
- package/telegram-plugin/registry/turns-schema.ts +65 -1
- package/telegram-plugin/session-tail.ts +26 -4
- package/telegram-plugin/slot-banner-driver.ts +42 -2
- package/telegram-plugin/status-query-telemetry.ts +100 -0
- package/telegram-plugin/stream-reply-handler.ts +15 -16
- package/telegram-plugin/subagent-watcher.ts +182 -30
- package/telegram-plugin/tests/buffer-gate-broadened.test.ts +4 -10
- package/telegram-plugin/tests/busy-key-reaper.test.ts +191 -0
- package/telegram-plugin/tests/emission-authority-facade.test.ts +11 -17
- package/telegram-plugin/tests/emission-determinism-wiring.test.ts +5 -26
- package/telegram-plugin/tests/escalation-bridge-gate.test.ts +38 -0
- package/telegram-plugin/tests/gate-parity-probe.test.ts +171 -0
- package/telegram-plugin/tests/gateway-disconnect-flush.test.ts +13 -0
- package/telegram-plugin/tests/gateway-outbound-redact.test.ts +14 -11
- package/telegram-plugin/tests/inbound-delivery-confirm.test.ts +146 -0
- package/telegram-plugin/tests/inbound-spool.test.ts +143 -0
- package/telegram-plugin/tests/model-command.test.ts +54 -1
- package/telegram-plugin/tests/multitopic-routing-wiring.test.ts +5 -11
- package/telegram-plugin/tests/nested-worker-visibility-harness.test.ts +329 -0
- package/telegram-plugin/tests/pending-inbound-buffer.test.ts +53 -0
- package/telegram-plugin/tests/progress-update-redact.test.ts +99 -0
- package/telegram-plugin/tests/registry-turns.test.ts +67 -0
- package/telegram-plugin/tests/represent-guard.test.ts +42 -6
- package/telegram-plugin/tests/resume-inbound-builder.test.ts +1 -0
- package/telegram-plugin/tests/session-tail.test.ts +10 -1
- package/telegram-plugin/tests/slot-banner-boot-recovery.test.ts +246 -0
- package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +0 -14
- package/telegram-plugin/tests/status-pin-store.test.ts +220 -5
- package/telegram-plugin/tests/status-query-telemetry.test.ts +115 -0
- package/telegram-plugin/tests/subagent-nested-dispatch.test.ts +209 -0
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +37 -0
- package/telegram-plugin/tests/subagent-watcher-boot-promotion-replay.test.ts +167 -0
- package/telegram-plugin/tests/subagent-watcher-env-thresholds.test.ts +46 -3
- package/telegram-plugin/tests/subagent-watcher-stall-notification.test.ts +70 -0
- package/telegram-plugin/tests/tool-activity-summary.test.ts +16 -0
- package/telegram-plugin/tests/tool-filter.test.ts +1 -3
- package/telegram-plugin/tests/tool-label-pretool.test.ts +1 -4
- package/telegram-plugin/tests/turn-flush-safety.test.ts +222 -1
- package/telegram-plugin/tests/worker-activity-feed.test.ts +202 -9
- package/telegram-plugin/tests/worker-feed-dispatch.test.ts +25 -0
- package/telegram-plugin/tests/worker-visibility-prose-silent-harness.test.ts +295 -0
- package/telegram-plugin/tool-activity-summary.ts +19 -0
- package/telegram-plugin/turn-flush-safety.ts +16 -1
- package/telegram-plugin/uat/scenarios/jtbd-answer-pings.test.ts +8 -9
- package/telegram-plugin/uat/scenarios/jtbd-foreground-feed-visibility-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-narration-intent-dm.test.ts +1 -1
- package/telegram-plugin/worker-activity-feed.ts +75 -15
- package/vendor/hindsight-memory/CHANGELOG.md +24 -0
- package/vendor/hindsight-memory/README.md +5 -0
- package/vendor/hindsight-memory/scripts/lib/client.py +31 -1
- package/vendor/hindsight-memory/scripts/lib/config.py +41 -2
- package/vendor/hindsight-memory/scripts/lib/content.py +4 -1
- package/vendor/hindsight-memory/scripts/lib/daemon.py +11 -2
- package/vendor/hindsight-memory/scripts/recall.py +74 -1
- package/vendor/hindsight-memory/scripts/retain.py +8 -1
- package/vendor/hindsight-memory/scripts/tests/test_config_client_casts.py +111 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_integration.py +85 -1
- package/vendor/hindsight-memory/scripts/tests/test_recall_tag_filters.py +107 -0
- package/vendor/hindsight-memory/settings.json +4 -0
- package/vendor/hindsight-memory/tests/test_client.py +130 -0
- package/vendor/hindsight-memory/tests/test_config.py +47 -0
- package/vendor/hindsight-memory/tests/test_content.py +18 -0
- package/vendor/hindsight-memory/tests/test_hooks.py +62 -0
- package/telegram-plugin/gateway/error-envelope-card.ts +0 -64
- package/telegram-plugin/gateway/resolve-calling-subagent.ts +0 -78
- package/telegram-plugin/silent-reply.ts +0 -58
- package/telegram-plugin/tests/error-envelope-unlock-card.test.ts +0 -79
- package/telegram-plugin/tests/resolve-calling-subagent.test.ts +0 -269
- package/telegram-plugin/tests/silent-reply-guard.test.ts +0 -122
package/dist/cli/ui/index.html
CHANGED
|
@@ -2392,8 +2392,20 @@
|
|
|
2392
2392
|
return;
|
|
2393
2393
|
}
|
|
2394
2394
|
|
|
2395
|
+
// Worst-first posture strip + sort — not-active (down) or auth-failed
|
|
2396
|
+
// agents are the ones that need attention; surface them first.
|
|
2397
|
+
const needsAttention = (a) => a.active !== 'active' || a.auth.authenticated === false;
|
|
2398
|
+
const attnAgents = agents.filter(needsAttention);
|
|
2399
|
+
const cleanAgents = agents.filter(a => !needsAttention(a));
|
|
2400
|
+
const posture = `
|
|
2401
|
+
<div class="ac-posture" style="grid-column:1/-1;margin:0 0 0.4rem;justify-content:flex-start">
|
|
2402
|
+
<span class="ac-stat attn"><span class="n">${attnAgents.length}</span><span class="l">need attention</span></span>
|
|
2403
|
+
<span class="ac-stat clean"><span class="n">${cleanAgents.length}</span><span class="l">running clean</span></span>
|
|
2404
|
+
</div>`;
|
|
2405
|
+
const sortedAgents = attnAgents.concat(cleanAgents);
|
|
2406
|
+
|
|
2395
2407
|
container.className = 'agents-grid';
|
|
2396
|
-
container.innerHTML =
|
|
2408
|
+
container.innerHTML = posture + sortedAgents.map(a => `
|
|
2397
2409
|
<div class="agent-card" data-agent="${escapeHtml(a.name)}">
|
|
2398
2410
|
<div class="card-header" onclick="toggleLogs('${escapeHtml(a.name)}')">
|
|
2399
2411
|
<div class="status-dot ${statusClass(a)}" title="${escapeHtml(a.active)} · bridge heartbeat (not a claude-liveness signal)"></div>
|
|
@@ -2731,15 +2743,36 @@
|
|
|
2731
2743
|
const updating = h.stale
|
|
2732
2744
|
? `<span style="color:var(--text-dim);font-size:.7rem;margin-left:.4rem">(updating…)</span>`
|
|
2733
2745
|
: '';
|
|
2746
|
+
|
|
2747
|
+
// Worst-first posture strip — same "attention vs clean" pattern as
|
|
2748
|
+
// Fleet Health / Accounts. hostd counts as attention when the audit
|
|
2749
|
+
// log is missing/erroring OR any recent verb actually failed (not
|
|
2750
|
+
// the benign in-flight "started" state).
|
|
2751
|
+
const hostdHasFailure = !!(hd.recent && hd.recent.some(e =>
|
|
2752
|
+
e.result === 'error' || (e.exit_code != null && e.exit_code !== 0)));
|
|
2753
|
+
const services = [
|
|
2754
|
+
{ key: 'broker', ok: !!b.reachable, name: 'auth-broker', dotOk: b.reachable,
|
|
2755
|
+
html: `<div class="agent-card"><div class="card-header" style="cursor:default">${dot(b.reachable)}<span class="agent-name">auth-broker</span></div><div style="padding:0 1.25rem 1rem">${brokerBody}</div></div>` },
|
|
2756
|
+
{ key: 'hindsight', ok: hs.running !== false, name: 'hindsight', dotOk: hs.running,
|
|
2757
|
+
html: `<div class="agent-card"><div class="card-header" style="cursor:default">${dot(hs.running)}<span class="agent-name">hindsight</span></div><div style="padding:0 1.25rem 1rem">${hindsightBody}</div></div>` },
|
|
2758
|
+
];
|
|
2759
|
+
const attnCount = services.filter(s => !s.ok).length + (hd.auditLogPresent && !hostdHasFailure ? 0 : 1);
|
|
2760
|
+
const cleanCount = services.length + 1 - attnCount;
|
|
2761
|
+
const posture = `
|
|
2762
|
+
<div class="ac-posture" style="margin:0 0 0.6rem;justify-content:flex-start">
|
|
2763
|
+
<span class="ac-stat attn"><span class="n">${attnCount}</span><span class="l">need attention</span></span>
|
|
2764
|
+
<span class="ac-stat clean"><span class="n">${cleanCount}</span><span class="l">clean</span></span>
|
|
2765
|
+
</div>`;
|
|
2766
|
+
// Sort the service cards worst-first (down services surface before
|
|
2767
|
+
// healthy ones) — the hostd card stays pinned below since it's a
|
|
2768
|
+
// wider table, not part of the two-up grid.
|
|
2769
|
+
const servicesSorted = services.slice().sort((a, b2) => (a.ok === b2.ok) ? 0 : a.ok ? 1 : -1);
|
|
2770
|
+
|
|
2734
2771
|
container.innerHTML = `
|
|
2735
2772
|
${updating ? `<div style="margin-bottom:.4rem">System health ${updating}</div>` : ''}
|
|
2773
|
+
${posture}
|
|
2736
2774
|
<div class="agents-grid">
|
|
2737
|
-
|
|
2738
|
-
${dot(b.reachable)}<span class="agent-name">auth-broker</span></div>
|
|
2739
|
-
<div style="padding:0 1.25rem 1rem">${brokerBody}</div></div>
|
|
2740
|
-
<div class="agent-card"><div class="card-header" style="cursor:default">
|
|
2741
|
-
${dot(hs.running)}<span class="agent-name">hindsight</span></div>
|
|
2742
|
-
<div style="padding:0 1.25rem 1rem">${hindsightBody}</div></div>
|
|
2775
|
+
${servicesSorted.map(s => s.html).join('')}
|
|
2743
2776
|
</div>
|
|
2744
2777
|
<div class="agent-card" style="margin-top:1rem"><div class="card-header" style="cursor:default">
|
|
2745
2778
|
${dot(hd.auditLogPresent)}<span class="agent-name">hostd — recent privileged verbs</span></div>
|
|
@@ -2848,6 +2881,25 @@
|
|
|
2848
2881
|
const linear = data.linear || { configured: false, agents: [] };
|
|
2849
2882
|
const agentNames = data.agentNames || [];
|
|
2850
2883
|
|
|
2884
|
+
// Worst-first: a config-only account (no live broker slot — never
|
|
2885
|
+
// actually connected) is the real "needs attention" signal here. A
|
|
2886
|
+
// brokerKnown account past its (short-lived) access-token expiry is
|
|
2887
|
+
// NOT broken — it auto-refreshes on next use via the refresh token —
|
|
2888
|
+
// so it must NOT be sorted/flagged as an attention item (matches the
|
|
2889
|
+
// "don't alarm on auto-refresh" fix in renderOAuthAccountCard above).
|
|
2890
|
+
const isConnBroken = (a) => a.brokerKnown === false;
|
|
2891
|
+
const worstFirst = (list) => list.slice().sort((a, b) => (isConnBroken(a) === isConnBroken(b)) ? 0 : isConnBroken(a) ? -1 : 1);
|
|
2892
|
+
const googleSorted = worstFirst(google);
|
|
2893
|
+
const microsoftSorted = worstFirst(microsoft);
|
|
2894
|
+
const oauthAttn = google.filter(isConnBroken).length + microsoft.filter(isConnBroken).length;
|
|
2895
|
+
const oauthClean = (google.length + microsoft.length) - oauthAttn;
|
|
2896
|
+
const posture = (google.length + microsoft.length) > 0
|
|
2897
|
+
? `<div class="ac-posture" style="margin:0 0 1rem;justify-content:flex-start">
|
|
2898
|
+
<span class="ac-stat attn"><span class="n">${oauthAttn}</span><span class="l">need attention</span></span>
|
|
2899
|
+
<span class="ac-stat clean"><span class="n">${oauthClean}</span><span class="l">connected</span></span>
|
|
2900
|
+
</div>`
|
|
2901
|
+
: '';
|
|
2902
|
+
|
|
2851
2903
|
// When a provider's fetch FAILED (not genuinely empty), its empty-state
|
|
2852
2904
|
// must say "couldn't load", never "none configured" — otherwise a
|
|
2853
2905
|
// transient blip reads as "you have no accounts".
|
|
@@ -2856,10 +2908,10 @@
|
|
|
2856
2908
|
data.googleFailed
|
|
2857
2909
|
? "Couldn't load Google accounts — the data source was unreachable (retrying)."
|
|
2858
2910
|
: 'No Google accounts. Add one under <code>google_accounts:</code> and run <code>switchroom auth google account add</code>.',
|
|
2859
|
-
|
|
2911
|
+
googleSorted.map(a => renderOAuthAccountCard(a, { showType: false, provider: 'google', agentNames })).join(''),
|
|
2860
2912
|
);
|
|
2861
2913
|
|
|
2862
|
-
const msCards =
|
|
2914
|
+
const msCards = microsoftSorted.map(a => renderOAuthAccountCard(a, { showType: true, provider: 'microsoft', agentNames })).join('');
|
|
2863
2915
|
const msEmpty = data.microsoftFailed
|
|
2864
2916
|
? "Couldn't load Microsoft accounts — the data source was unreachable (retrying)."
|
|
2865
2917
|
: 'No Microsoft accounts yet — click <b>Connect a Microsoft account</b> above (or <code>/connect microsoft</code> from Telegram).';
|
|
@@ -2957,7 +3009,7 @@
|
|
|
2957
3009
|
? renderProblem(problemFor('connections-unreachable', {}))
|
|
2958
3010
|
: '';
|
|
2959
3011
|
|
|
2960
|
-
container.innerHTML = unreachableBanner + degradedBanner + googleSection + microsoftSection + notionSection + linearSection;
|
|
3012
|
+
container.innerHTML = unreachableBanner + degradedBanner + posture + googleSection + microsoftSection + notionSection + linearSection;
|
|
2961
3013
|
}
|
|
2962
3014
|
|
|
2963
3015
|
function renderSchedule(data) {
|
|
@@ -3050,7 +3102,27 @@
|
|
|
3050
3102
|
</div>`;
|
|
3051
3103
|
};
|
|
3052
3104
|
|
|
3053
|
-
|
|
3105
|
+
// Worst-first: an agent whose most recent fire for ANY of its crons
|
|
3106
|
+
// failed (non-zero exit) needs attention before agents whose fires
|
|
3107
|
+
// are all clean (or unfired). Posture strip mirrors the Fleet
|
|
3108
|
+
// Health / Accounts "need attention vs clean" pattern.
|
|
3109
|
+
const agentHasFailure = (agent) => {
|
|
3110
|
+
const agentFires = recent[agent] || [];
|
|
3111
|
+
return byAgent[agent].some(e => {
|
|
3112
|
+
const latest = agentFires.filter(f => f.promptKey === e.promptKey).slice(-1)[0];
|
|
3113
|
+
return latest && latest.exitCode !== 0;
|
|
3114
|
+
});
|
|
3115
|
+
};
|
|
3116
|
+
const agentNamesSorted = Object.keys(byAgent).sort();
|
|
3117
|
+
const attnAgentNames = agentNamesSorted.filter(agentHasFailure);
|
|
3118
|
+
const cleanAgentNames = agentNamesSorted.filter(a => !agentHasFailure(a));
|
|
3119
|
+
const posture = `
|
|
3120
|
+
<div class="ac-posture" style="margin:0 0 0.8rem;justify-content:flex-start">
|
|
3121
|
+
<span class="ac-stat attn"><span class="n">${attnAgentNames.length}</span><span class="l">need attention</span></span>
|
|
3122
|
+
<span class="ac-stat clean"><span class="n">${cleanAgentNames.length}</span><span class="l">clean</span></span>
|
|
3123
|
+
</div>`;
|
|
3124
|
+
|
|
3125
|
+
const cards = attnAgentNames.concat(cleanAgentNames).map(agent => {
|
|
3054
3126
|
const agentFires = recent[agent] || [];
|
|
3055
3127
|
const list = byAgent[agent].map(e => renderCron(e, agentFires)).join('');
|
|
3056
3128
|
return `
|
|
@@ -3062,7 +3134,7 @@
|
|
|
3062
3134
|
<div style="padding:.5rem 1.25rem 1rem">${list}</div>
|
|
3063
3135
|
</div>`;
|
|
3064
3136
|
}).join('');
|
|
3065
|
-
container.innerHTML = degradedBanner + truncatedNote + cards;
|
|
3137
|
+
container.innerHTML = degradedBanner + truncatedNote + posture + cards;
|
|
3066
3138
|
}
|
|
3067
3139
|
|
|
3068
3140
|
function renderApprovals(data, grants) {
|
|
@@ -15903,7 +15903,9 @@ var SessionContinuitySchema = exports_external.object({
|
|
|
15903
15903
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
15904
15904
|
max_turns_in_briefing: exports_external.number().int().positive().optional().describe("Cap on recent user/assistant turn pairs fed to the summarizer."),
|
|
15905
15905
|
resume_mode: exports_external.enum(["auto", "continue", "handoff", "none"]).optional().describe("How to resume the next session. 'handoff' (default as of #362) " + "never passes --continue; a fresh Claude starts each restart and " + "reads a briefing assembled from recent Telegram messages, Hindsight " + "recall, and today's daily memory file. 'auto' uses --continue when " + "the latest JSONL is smaller than resume_max_bytes, else falls back " + "to the handoff briefing. 'continue' always passes --continue. " + "'none' starts completely fresh every time."),
|
|
15906
|
-
resume_max_bytes: exports_external.number().int().positive().optional().describe("Byte threshold above which 'auto' mode falls back to handoff " + "instead of --continue. Default 2_000_000 (~2MB). Large transcripts " + "can blow out the context window even with prefix caching, and " + "--continue replay is known-fragile at scale.")
|
|
15906
|
+
resume_max_bytes: exports_external.number().int().positive().optional().describe("Byte threshold above which 'auto' mode falls back to handoff " + "instead of --continue. Default 2_000_000 (~2MB). Large transcripts " + "can blow out the context window even with prefix caching, and " + "--continue replay is known-fragile at scale."),
|
|
15907
|
+
session_retention_max_count: exports_external.number().int().nonnegative().optional().describe("Session-JSONL retention (issue #2792): keep at most this many " + "newest session transcripts under .claude/projects; older ones " + "past both this count and the age bound are pruned by the Stop " + "hook. The newest sessions (and the handoff source) are always " + "kept. Default 20; set 0 to disable the count bound."),
|
|
15908
|
+
session_retention_max_age_days: exports_external.number().int().nonnegative().optional().describe("Session-JSONL retention (issue #2792): prune session transcripts " + "older than this many days (a file is deleted only when it is BOTH " + "over the count bound and older than this). Default 30; set 0 to " + "disable the age bound.")
|
|
15907
15909
|
}).optional();
|
|
15908
15910
|
var webhookDispatchRule = exports_external.object({
|
|
15909
15911
|
description: exports_external.string().optional(),
|
|
@@ -17184,6 +17186,36 @@ var CONTAINER_DEFAULT_UTC_ZONES = new Set([
|
|
|
17184
17186
|
"Universal"
|
|
17185
17187
|
]);
|
|
17186
17188
|
|
|
17189
|
+
// src/broker-common/peercred-path.ts
|
|
17190
|
+
function matchSocketName(socketPath, patterns, maxNameLen) {
|
|
17191
|
+
if (typeof socketPath !== "string" || socketPath.length === 0)
|
|
17192
|
+
return null;
|
|
17193
|
+
let name = null;
|
|
17194
|
+
for (const re of patterns) {
|
|
17195
|
+
const m = socketPath.match(re);
|
|
17196
|
+
if (m && typeof m[1] === "string") {
|
|
17197
|
+
name = m[1];
|
|
17198
|
+
break;
|
|
17199
|
+
}
|
|
17200
|
+
}
|
|
17201
|
+
if (name === null || name.length === 0)
|
|
17202
|
+
return null;
|
|
17203
|
+
if (maxNameLen !== undefined && name.length > maxNameLen)
|
|
17204
|
+
return null;
|
|
17205
|
+
return name;
|
|
17206
|
+
}
|
|
17207
|
+
function parseSocketIdentity(socketPath, spec) {
|
|
17208
|
+
const name = matchSocketName(socketPath, spec.patterns, spec.maxNameLen);
|
|
17209
|
+
if (name === null)
|
|
17210
|
+
return null;
|
|
17211
|
+
const operatorName = spec.operatorName ?? "operator";
|
|
17212
|
+
if (name === operatorName)
|
|
17213
|
+
return { kind: "operator" };
|
|
17214
|
+
if (spec.reservedNames.has(name))
|
|
17215
|
+
return null;
|
|
17216
|
+
return { kind: "agent", name };
|
|
17217
|
+
}
|
|
17218
|
+
|
|
17187
17219
|
// src/vault/broker/peercred.ts
|
|
17188
17220
|
var RESERVED_AGENT_NAMES = new Set(["operator", "hostd"]);
|
|
17189
17221
|
function isReservedAgentName(name) {
|
|
@@ -22179,17 +22211,10 @@ var SOCKET_PATH_CONTAINER_SUBDIR_RE = /^\/run\/switchroom\/hostd\/([a-zA-Z0-9][a
|
|
|
22179
22211
|
var SOCKET_PATH_HOST_SUBDIR_RE = /\/\.switchroom\/hostd\/([a-zA-Z0-9][a-zA-Z0-9_-]*)\/sock$/;
|
|
22180
22212
|
var RESERVED_AGENT_NAMES2 = new Set(["operator", "hostd"]);
|
|
22181
22213
|
function socketPathToIdentity(socketPath) {
|
|
22182
|
-
|
|
22183
|
-
|
|
22184
|
-
|
|
22185
|
-
|
|
22186
|
-
return null;
|
|
22187
|
-
const name = m[1];
|
|
22188
|
-
if (name === "operator")
|
|
22189
|
-
return { kind: "operator" };
|
|
22190
|
-
if (RESERVED_AGENT_NAMES2.has(name))
|
|
22191
|
-
return null;
|
|
22192
|
-
return { kind: "agent", name };
|
|
22214
|
+
return parseSocketIdentity(socketPath, {
|
|
22215
|
+
patterns: [SOCKET_PATH_CONTAINER_SUBDIR_RE, SOCKET_PATH_HOST_SUBDIR_RE],
|
|
22216
|
+
reservedNames: RESERVED_AGENT_NAMES2
|
|
22217
|
+
});
|
|
22193
22218
|
}
|
|
22194
22219
|
|
|
22195
22220
|
// telegram-plugin/secret-detect/patterns.ts
|
|
@@ -22624,7 +22649,7 @@ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:f
|
|
|
22624
22649
|
import { dirname as dirname4, join as join2 } from "node:path";
|
|
22625
22650
|
|
|
22626
22651
|
// src/build-info.ts
|
|
22627
|
-
var VERSION = "0.
|
|
22652
|
+
var VERSION = "0.17.0";
|
|
22628
22653
|
|
|
22629
22654
|
// src/cli/resolve-version.ts
|
|
22630
22655
|
function readPackageVersion() {
|
|
@@ -24478,19 +24503,30 @@ class HostdServer {
|
|
|
24478
24503
|
const recoveryNote = recRes2.exit_code === 0 ? "rolled back successfully" : `rolled back but recovery reconcile also failed (exit ${recRes2.exit_code})`;
|
|
24479
24504
|
await approval.finalize({
|
|
24480
24505
|
outcome: "reconcile_failed_rolled_back",
|
|
24481
|
-
detail: recoveryNote
|
|
24506
|
+
detail: `${recoveryNote}; reconcile stderr: ${tail(recRes.stderr, 512) || "(empty)"}`
|
|
24482
24507
|
});
|
|
24483
|
-
return this.reconcileFailedRolledBack(`reconcile exit ${recRes.exit_code}; ${recoveryNote}`, req, caller, started);
|
|
24508
|
+
return this.reconcileFailedRolledBack(`reconcile exit ${recRes.exit_code}; ${recoveryNote}`, req, caller, started, { primary: recRes, recovery: recRes2 });
|
|
24484
24509
|
} finally {
|
|
24485
24510
|
release();
|
|
24486
24511
|
}
|
|
24487
24512
|
}
|
|
24488
|
-
reconcileFailedRolledBack(detail, req, caller, started) {
|
|
24513
|
+
reconcileFailedRolledBack(detail, req, caller, started, output) {
|
|
24489
24514
|
const legacy = `E_RECONCILE_FAILED_ROLLED_BACK: ${detail}`;
|
|
24490
24515
|
const built = err("E_RECONCILE_FAILED_ROLLED_BACK", "config write or reconcile failed; live file rolled back to snapshot").why(detail).fixOperatorAction("infra", [
|
|
24491
24516
|
"inspect hostd logs + switchroom apply output to identify the underlying failure"
|
|
24492
24517
|
]).op("config_propose_edit").caller(caller.kind === "agent" ? "agent" : "operator").agentName(caller.kind === "agent" ? caller.name : undefined).build(req.request_id, Date.now() - started);
|
|
24493
|
-
|
|
24518
|
+
const resp = { ...built, error: legacy };
|
|
24519
|
+
if (output) {
|
|
24520
|
+
const recoveryStdout = output.recovery && output.recovery.exit_code !== 0 ? `
|
|
24521
|
+
--- recovery reconcile stdout ---
|
|
24522
|
+
${output.recovery.stdout}` : "";
|
|
24523
|
+
const recoveryStderr = output.recovery && output.recovery.exit_code !== 0 ? `
|
|
24524
|
+
--- recovery reconcile stderr ---
|
|
24525
|
+
${output.recovery.stderr}` : "";
|
|
24526
|
+
resp.stdout_tail = tail(output.primary.stdout + recoveryStdout);
|
|
24527
|
+
resp.stderr_tail = tail(output.primary.stderr + recoveryStderr);
|
|
24528
|
+
}
|
|
24529
|
+
return resp;
|
|
24494
24530
|
}
|
|
24495
24531
|
configApplyLock = Promise.resolve();
|
|
24496
24532
|
async acquireConfigApplyLock() {
|
|
@@ -4151,7 +4151,9 @@ var init_schema = __esm(() => {
|
|
|
4151
4151
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
4152
4152
|
max_turns_in_briefing: exports_external.number().int().positive().optional().describe("Cap on recent user/assistant turn pairs fed to the summarizer."),
|
|
4153
4153
|
resume_mode: exports_external.enum(["auto", "continue", "handoff", "none"]).optional().describe("How to resume the next session. 'handoff' (default as of #362) " + "never passes --continue; a fresh Claude starts each restart and " + "reads a briefing assembled from recent Telegram messages, Hindsight " + "recall, and today's daily memory file. 'auto' uses --continue when " + "the latest JSONL is smaller than resume_max_bytes, else falls back " + "to the handoff briefing. 'continue' always passes --continue. " + "'none' starts completely fresh every time."),
|
|
4154
|
-
resume_max_bytes: exports_external.number().int().positive().optional().describe("Byte threshold above which 'auto' mode falls back to handoff " + "instead of --continue. Default 2_000_000 (~2MB). Large transcripts " + "can blow out the context window even with prefix caching, and " + "--continue replay is known-fragile at scale.")
|
|
4154
|
+
resume_max_bytes: exports_external.number().int().positive().optional().describe("Byte threshold above which 'auto' mode falls back to handoff " + "instead of --continue. Default 2_000_000 (~2MB). Large transcripts " + "can blow out the context window even with prefix caching, and " + "--continue replay is known-fragile at scale."),
|
|
4155
|
+
session_retention_max_count: exports_external.number().int().nonnegative().optional().describe("Session-JSONL retention (issue #2792): keep at most this many " + "newest session transcripts under .claude/projects; older ones " + "past both this count and the age bound are pruned by the Stop " + "hook. The newest sessions (and the handoff source) are always " + "kept. Default 20; set 0 to disable the count bound."),
|
|
4156
|
+
session_retention_max_age_days: exports_external.number().int().nonnegative().optional().describe("Session-JSONL retention (issue #2792): prune session transcripts " + "older than this many days (a file is deleted only when it is BOTH " + "over the count bound and older than this). Default 30; set 0 to " + "disable the age bound.")
|
|
4155
4157
|
}).optional();
|
|
4156
4158
|
webhookDispatchRule = exports_external.object({
|
|
4157
4159
|
description: exports_external.string().optional(),
|
|
@@ -13250,6 +13252,7 @@ function openKernelDb(dbPath) {
|
|
|
13250
13252
|
chmodSync(dbPath, 384);
|
|
13251
13253
|
} catch {}
|
|
13252
13254
|
db.run("PRAGMA journal_mode=WAL");
|
|
13255
|
+
db.run("PRAGMA busy_timeout=5000");
|
|
13253
13256
|
db.run("PRAGMA foreign_keys=ON");
|
|
13254
13257
|
migrateApprovalSchema(db);
|
|
13255
13258
|
return db;
|