switchroom 0.20.7 → 0.20.8
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 +105 -11
- package/dist/auth-broker/index.js +68 -7
- package/dist/cli/notion-write-pretool.mjs +67 -6
- package/dist/cli/switchroom.js +384 -28
- package/dist/host-control/main.js +69 -8
- package/dist/vault/approvals/kernel-server.js +68 -7
- package/dist/vault/broker/server.js +68 -7
- package/package.json +1 -1
- package/telegram-plugin/bridge/ipc-client.ts +17 -1
- package/telegram-plugin/dist/bridge/bridge.js +5 -2
- package/telegram-plugin/dist/gateway/gateway.js +237 -122
- package/telegram-plugin/dist/server.js +5 -2
- package/telegram-plugin/gateway/boot-reason.ts +61 -0
- package/telegram-plugin/gateway/cron-session.ts +66 -0
- package/telegram-plugin/gateway/gateway.ts +28 -30
- package/telegram-plugin/gateway/narrative-lane.ts +21 -1
- package/telegram-plugin/gateway/represent-delivery-guard.ts +33 -2
- package/telegram-plugin/gateway/stream-render.ts +11 -2
- package/telegram-plugin/tests/boot-card-reason.test.ts +88 -0
- package/telegram-plugin/tests/cron-bridge-drain-spool-ack.test.ts +150 -0
- package/telegram-plugin/tests/ipc-client-reconnect-rejection.test.ts +70 -0
- package/telegram-plugin/tests/narrative-lane-golden.test.ts +86 -0
- package/telegram-plugin/tests/queued-card-surface.test.ts +66 -0
- package/telegram-plugin/tests/represent-guard.test.ts +45 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +83 -0
- package/telegram-plugin/turn-flush-safety.ts +79 -0
|
@@ -12040,6 +12040,20 @@ var init_overlay_schema = __esm(() => {
|
|
|
12040
12040
|
// src/config/overlay-loader.ts
|
|
12041
12041
|
import { existsSync as existsSync2, readFileSync, readdirSync, statSync } from "node:fs";
|
|
12042
12042
|
import { basename, resolve as resolve2 } from "node:path";
|
|
12043
|
+
function recordReadFailure(agentCfg, failure) {
|
|
12044
|
+
const node = agentCfg;
|
|
12045
|
+
const existing = node[OVERLAY_READ_FAILURES];
|
|
12046
|
+
if (Array.isArray(existing)) {
|
|
12047
|
+
existing.push(failure);
|
|
12048
|
+
return;
|
|
12049
|
+
}
|
|
12050
|
+
Object.defineProperty(agentCfg, OVERLAY_READ_FAILURES, {
|
|
12051
|
+
value: [failure],
|
|
12052
|
+
enumerable: false,
|
|
12053
|
+
configurable: true,
|
|
12054
|
+
writable: false
|
|
12055
|
+
});
|
|
12056
|
+
}
|
|
12043
12057
|
function deriveOverlayTitle(raw, fileName) {
|
|
12044
12058
|
const titleFromComment = raw.match(/^#[^\S\n]*name:[^\S\n]*(\S.*?)[^\S\n]*$/m)?.[1];
|
|
12045
12059
|
if (titleFromComment)
|
|
@@ -12049,17 +12063,39 @@ function deriveOverlayTitle(raw, fileName) {
|
|
|
12049
12063
|
return;
|
|
12050
12064
|
return base.length > 0 ? base : undefined;
|
|
12051
12065
|
}
|
|
12066
|
+
function readOverlayFile(agentName, file, agentCfg, warnings) {
|
|
12067
|
+
try {
|
|
12068
|
+
return readFileSync(file, "utf-8");
|
|
12069
|
+
} catch (err) {
|
|
12070
|
+
const code = err.code;
|
|
12071
|
+
if (code === "ENOENT")
|
|
12072
|
+
return;
|
|
12073
|
+
const w = {
|
|
12074
|
+
agent: agentName,
|
|
12075
|
+
file,
|
|
12076
|
+
reason: `read error: ${err.message}`,
|
|
12077
|
+
code: code ?? "EUNKNOWN"
|
|
12078
|
+
};
|
|
12079
|
+
recordReadFailure(agentCfg, { file, code: w.code });
|
|
12080
|
+
warnings.push(w);
|
|
12081
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${file}': ${w.reason}`);
|
|
12082
|
+
return;
|
|
12083
|
+
}
|
|
12084
|
+
}
|
|
12052
12085
|
function overlayDirFor(agentName, subdir) {
|
|
12053
12086
|
const base = resolveDualPath(`~/.switchroom/agents/${agentName}/${subdir}`);
|
|
12054
12087
|
return resolve2(base);
|
|
12055
12088
|
}
|
|
12056
|
-
function listYamlFiles(dir) {
|
|
12089
|
+
function listYamlFiles(dir, onUnreadableDir) {
|
|
12057
12090
|
if (!existsSync2(dir))
|
|
12058
12091
|
return [];
|
|
12059
12092
|
let entries;
|
|
12060
12093
|
try {
|
|
12061
12094
|
entries = readdirSync(dir);
|
|
12062
|
-
} catch {
|
|
12095
|
+
} catch (err) {
|
|
12096
|
+
const code = err.code;
|
|
12097
|
+
if (code !== "ENOENT")
|
|
12098
|
+
onUnreadableDir?.(code ?? "EUNKNOWN");
|
|
12063
12099
|
return [];
|
|
12064
12100
|
}
|
|
12065
12101
|
const out = [];
|
|
@@ -12097,12 +12133,24 @@ function applyAgentOverlays(config) {
|
|
|
12097
12133
|
for (const [agentName, agentCfg] of Object.entries(agents)) {
|
|
12098
12134
|
try {
|
|
12099
12135
|
const scheduleDir = overlayDirFor(agentName, "schedule.d");
|
|
12100
|
-
const files = listYamlFiles(scheduleDir)
|
|
12136
|
+
const files = listYamlFiles(scheduleDir, (code) => {
|
|
12137
|
+
const w = {
|
|
12138
|
+
agent: agentName,
|
|
12139
|
+
file: scheduleDir,
|
|
12140
|
+
reason: `read error: cannot list overlay directory (${code})`,
|
|
12141
|
+
code
|
|
12142
|
+
};
|
|
12143
|
+
recordReadFailure(agentCfg, { file: scheduleDir, code });
|
|
12144
|
+
warnings.push(w);
|
|
12145
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${scheduleDir}': ${w.reason}`);
|
|
12146
|
+
});
|
|
12101
12147
|
if (files.length > 0) {
|
|
12102
12148
|
const merged = [...agentCfg.schedule ?? []];
|
|
12103
12149
|
for (const file of files) {
|
|
12150
|
+
const raw = readOverlayFile(agentName, file, agentCfg, warnings);
|
|
12151
|
+
if (raw === undefined)
|
|
12152
|
+
continue;
|
|
12104
12153
|
try {
|
|
12105
|
-
const raw = readFileSync(file, "utf-8");
|
|
12106
12154
|
const parsed = import_yaml.parse(raw);
|
|
12107
12155
|
const doc = OverlayDocSchema.parse(parsed);
|
|
12108
12156
|
const title = deriveOverlayTitle(raw, basename(file));
|
|
@@ -12137,13 +12185,25 @@ function applyAgentOverlays(config) {
|
|
|
12137
12185
|
}
|
|
12138
12186
|
try {
|
|
12139
12187
|
const skillsDir = overlayDirFor(agentName, "skills.d");
|
|
12140
|
-
const skillFiles = listYamlFiles(skillsDir)
|
|
12188
|
+
const skillFiles = listYamlFiles(skillsDir, (code) => {
|
|
12189
|
+
const w = {
|
|
12190
|
+
agent: agentName,
|
|
12191
|
+
file: skillsDir,
|
|
12192
|
+
reason: `read error: cannot list overlay directory (${code})`,
|
|
12193
|
+
code
|
|
12194
|
+
};
|
|
12195
|
+
recordReadFailure(agentCfg, { file: skillsDir, code });
|
|
12196
|
+
warnings.push(w);
|
|
12197
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${skillsDir}': ${w.reason}`);
|
|
12198
|
+
});
|
|
12141
12199
|
if (skillFiles.length === 0) {} else {
|
|
12142
12200
|
const merged = [...agentCfg.skills ?? []];
|
|
12143
12201
|
const seen = new Set(merged);
|
|
12144
12202
|
for (const file of skillFiles) {
|
|
12203
|
+
const raw = readOverlayFile(agentName, file, agentCfg, warnings);
|
|
12204
|
+
if (raw === undefined)
|
|
12205
|
+
continue;
|
|
12145
12206
|
try {
|
|
12146
|
-
const raw = readFileSync(file, "utf-8");
|
|
12147
12207
|
const parsed = import_yaml.parse(raw);
|
|
12148
12208
|
const doc = OverlayDocSchema.parse(parsed);
|
|
12149
12209
|
for (const skillName of doc.skills ?? []) {
|
|
@@ -12171,7 +12231,7 @@ function applyAgentOverlays(config) {
|
|
|
12171
12231
|
}
|
|
12172
12232
|
return { config, warnings };
|
|
12173
12233
|
}
|
|
12174
|
-
var import_yaml, OVERLAY_SOURCE, OVERLAY_TITLE;
|
|
12234
|
+
var import_yaml, OVERLAY_SOURCE, OVERLAY_TITLE, OVERLAY_READ_FAILURES;
|
|
12175
12235
|
var init_overlay_loader = __esm(() => {
|
|
12176
12236
|
init_zod();
|
|
12177
12237
|
init_overlay_schema();
|
|
@@ -12179,6 +12239,7 @@ var init_overlay_loader = __esm(() => {
|
|
|
12179
12239
|
import_yaml = __toESM(require_dist(), 1);
|
|
12180
12240
|
OVERLAY_SOURCE = Symbol.for("switchroom.config.overlay-source");
|
|
12181
12241
|
OVERLAY_TITLE = Symbol.for("switchroom.config.overlay-title");
|
|
12242
|
+
OVERLAY_READ_FAILURES = Symbol.for("switchroom.config.overlay-read-failures");
|
|
12182
12243
|
});
|
|
12183
12244
|
|
|
12184
12245
|
// src/config/merge.ts
|
|
@@ -21432,7 +21493,7 @@ function allocateAgentUid(name) {
|
|
|
21432
21493
|
}
|
|
21433
21494
|
|
|
21434
21495
|
// src/build-info.ts
|
|
21435
|
-
var VERSION = "0.20.
|
|
21496
|
+
var VERSION = "0.20.8";
|
|
21436
21497
|
|
|
21437
21498
|
// src/setup/hindsight-recall-tunables.ts
|
|
21438
21499
|
var RECALL_DEADLINE_HEADROOM_SECONDS = 2;
|
|
@@ -12844,6 +12844,20 @@ var init_overlay_schema = __esm(() => {
|
|
|
12844
12844
|
// src/config/overlay-loader.ts
|
|
12845
12845
|
import { existsSync as existsSync3, readFileSync as readFileSync2, readdirSync, statSync } from "node:fs";
|
|
12846
12846
|
import { basename, resolve as resolve3 } from "node:path";
|
|
12847
|
+
function recordReadFailure(agentCfg, failure) {
|
|
12848
|
+
const node = agentCfg;
|
|
12849
|
+
const existing = node[OVERLAY_READ_FAILURES];
|
|
12850
|
+
if (Array.isArray(existing)) {
|
|
12851
|
+
existing.push(failure);
|
|
12852
|
+
return;
|
|
12853
|
+
}
|
|
12854
|
+
Object.defineProperty(agentCfg, OVERLAY_READ_FAILURES, {
|
|
12855
|
+
value: [failure],
|
|
12856
|
+
enumerable: false,
|
|
12857
|
+
configurable: true,
|
|
12858
|
+
writable: false
|
|
12859
|
+
});
|
|
12860
|
+
}
|
|
12847
12861
|
function deriveOverlayTitle(raw, fileName) {
|
|
12848
12862
|
const titleFromComment = raw.match(/^#[^\S\n]*name:[^\S\n]*(\S.*?)[^\S\n]*$/m)?.[1];
|
|
12849
12863
|
if (titleFromComment)
|
|
@@ -12853,17 +12867,39 @@ function deriveOverlayTitle(raw, fileName) {
|
|
|
12853
12867
|
return;
|
|
12854
12868
|
return base.length > 0 ? base : undefined;
|
|
12855
12869
|
}
|
|
12870
|
+
function readOverlayFile(agentName, file, agentCfg, warnings) {
|
|
12871
|
+
try {
|
|
12872
|
+
return readFileSync2(file, "utf-8");
|
|
12873
|
+
} catch (err) {
|
|
12874
|
+
const code = err.code;
|
|
12875
|
+
if (code === "ENOENT")
|
|
12876
|
+
return;
|
|
12877
|
+
const w = {
|
|
12878
|
+
agent: agentName,
|
|
12879
|
+
file,
|
|
12880
|
+
reason: `read error: ${err.message}`,
|
|
12881
|
+
code: code ?? "EUNKNOWN"
|
|
12882
|
+
};
|
|
12883
|
+
recordReadFailure(agentCfg, { file, code: w.code });
|
|
12884
|
+
warnings.push(w);
|
|
12885
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${file}': ${w.reason}`);
|
|
12886
|
+
return;
|
|
12887
|
+
}
|
|
12888
|
+
}
|
|
12856
12889
|
function overlayDirFor(agentName, subdir) {
|
|
12857
12890
|
const base = resolveDualPath(`~/.switchroom/agents/${agentName}/${subdir}`);
|
|
12858
12891
|
return resolve3(base);
|
|
12859
12892
|
}
|
|
12860
|
-
function listYamlFiles(dir) {
|
|
12893
|
+
function listYamlFiles(dir, onUnreadableDir) {
|
|
12861
12894
|
if (!existsSync3(dir))
|
|
12862
12895
|
return [];
|
|
12863
12896
|
let entries;
|
|
12864
12897
|
try {
|
|
12865
12898
|
entries = readdirSync(dir);
|
|
12866
|
-
} catch {
|
|
12899
|
+
} catch (err) {
|
|
12900
|
+
const code = err.code;
|
|
12901
|
+
if (code !== "ENOENT")
|
|
12902
|
+
onUnreadableDir?.(code ?? "EUNKNOWN");
|
|
12867
12903
|
return [];
|
|
12868
12904
|
}
|
|
12869
12905
|
const out = [];
|
|
@@ -12901,12 +12937,24 @@ function applyAgentOverlays(config) {
|
|
|
12901
12937
|
for (const [agentName, agentCfg] of Object.entries(agents)) {
|
|
12902
12938
|
try {
|
|
12903
12939
|
const scheduleDir = overlayDirFor(agentName, "schedule.d");
|
|
12904
|
-
const files = listYamlFiles(scheduleDir)
|
|
12940
|
+
const files = listYamlFiles(scheduleDir, (code) => {
|
|
12941
|
+
const w = {
|
|
12942
|
+
agent: agentName,
|
|
12943
|
+
file: scheduleDir,
|
|
12944
|
+
reason: `read error: cannot list overlay directory (${code})`,
|
|
12945
|
+
code
|
|
12946
|
+
};
|
|
12947
|
+
recordReadFailure(agentCfg, { file: scheduleDir, code });
|
|
12948
|
+
warnings.push(w);
|
|
12949
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${scheduleDir}': ${w.reason}`);
|
|
12950
|
+
});
|
|
12905
12951
|
if (files.length > 0) {
|
|
12906
12952
|
const merged = [...agentCfg.schedule ?? []];
|
|
12907
12953
|
for (const file of files) {
|
|
12954
|
+
const raw = readOverlayFile(agentName, file, agentCfg, warnings);
|
|
12955
|
+
if (raw === undefined)
|
|
12956
|
+
continue;
|
|
12908
12957
|
try {
|
|
12909
|
-
const raw = readFileSync2(file, "utf-8");
|
|
12910
12958
|
const parsed = import_yaml.parse(raw);
|
|
12911
12959
|
const doc = OverlayDocSchema.parse(parsed);
|
|
12912
12960
|
const title = deriveOverlayTitle(raw, basename(file));
|
|
@@ -12941,13 +12989,25 @@ function applyAgentOverlays(config) {
|
|
|
12941
12989
|
}
|
|
12942
12990
|
try {
|
|
12943
12991
|
const skillsDir = overlayDirFor(agentName, "skills.d");
|
|
12944
|
-
const skillFiles = listYamlFiles(skillsDir)
|
|
12992
|
+
const skillFiles = listYamlFiles(skillsDir, (code) => {
|
|
12993
|
+
const w = {
|
|
12994
|
+
agent: agentName,
|
|
12995
|
+
file: skillsDir,
|
|
12996
|
+
reason: `read error: cannot list overlay directory (${code})`,
|
|
12997
|
+
code
|
|
12998
|
+
};
|
|
12999
|
+
recordReadFailure(agentCfg, { file: skillsDir, code });
|
|
13000
|
+
warnings.push(w);
|
|
13001
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${skillsDir}': ${w.reason}`);
|
|
13002
|
+
});
|
|
12945
13003
|
if (skillFiles.length === 0) {} else {
|
|
12946
13004
|
const merged = [...agentCfg.skills ?? []];
|
|
12947
13005
|
const seen = new Set(merged);
|
|
12948
13006
|
for (const file of skillFiles) {
|
|
13007
|
+
const raw = readOverlayFile(agentName, file, agentCfg, warnings);
|
|
13008
|
+
if (raw === undefined)
|
|
13009
|
+
continue;
|
|
12949
13010
|
try {
|
|
12950
|
-
const raw = readFileSync2(file, "utf-8");
|
|
12951
13011
|
const parsed = import_yaml.parse(raw);
|
|
12952
13012
|
const doc = OverlayDocSchema.parse(parsed);
|
|
12953
13013
|
for (const skillName of doc.skills ?? []) {
|
|
@@ -12975,7 +13035,7 @@ function applyAgentOverlays(config) {
|
|
|
12975
13035
|
}
|
|
12976
13036
|
return { config, warnings };
|
|
12977
13037
|
}
|
|
12978
|
-
var import_yaml, OVERLAY_SOURCE, OVERLAY_TITLE;
|
|
13038
|
+
var import_yaml, OVERLAY_SOURCE, OVERLAY_TITLE, OVERLAY_READ_FAILURES;
|
|
12979
13039
|
var init_overlay_loader = __esm(() => {
|
|
12980
13040
|
init_zod();
|
|
12981
13041
|
init_overlay_schema();
|
|
@@ -12983,6 +13043,7 @@ var init_overlay_loader = __esm(() => {
|
|
|
12983
13043
|
import_yaml = __toESM(require_dist(), 1);
|
|
12984
13044
|
OVERLAY_SOURCE = Symbol.for("switchroom.config.overlay-source");
|
|
12985
13045
|
OVERLAY_TITLE = Symbol.for("switchroom.config.overlay-title");
|
|
13046
|
+
OVERLAY_READ_FAILURES = Symbol.for("switchroom.config.overlay-read-failures");
|
|
12986
13047
|
});
|
|
12987
13048
|
|
|
12988
13049
|
// src/config/notion-workspace-acl.ts
|
|
@@ -12772,6 +12772,20 @@ var init_overlay_schema = __esm(() => {
|
|
|
12772
12772
|
// src/config/overlay-loader.ts
|
|
12773
12773
|
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync } from "node:fs";
|
|
12774
12774
|
import { basename, resolve as resolve3 } from "node:path";
|
|
12775
|
+
function recordReadFailure(agentCfg, failure) {
|
|
12776
|
+
const node = agentCfg;
|
|
12777
|
+
const existing = node[OVERLAY_READ_FAILURES];
|
|
12778
|
+
if (Array.isArray(existing)) {
|
|
12779
|
+
existing.push(failure);
|
|
12780
|
+
return;
|
|
12781
|
+
}
|
|
12782
|
+
Object.defineProperty(agentCfg, OVERLAY_READ_FAILURES, {
|
|
12783
|
+
value: [failure],
|
|
12784
|
+
enumerable: false,
|
|
12785
|
+
configurable: true,
|
|
12786
|
+
writable: false
|
|
12787
|
+
});
|
|
12788
|
+
}
|
|
12775
12789
|
function deriveOverlayTitle(raw, fileName) {
|
|
12776
12790
|
const titleFromComment = raw.match(/^#[^\S\n]*name:[^\S\n]*(\S.*?)[^\S\n]*$/m)?.[1];
|
|
12777
12791
|
if (titleFromComment)
|
|
@@ -12781,17 +12795,39 @@ function deriveOverlayTitle(raw, fileName) {
|
|
|
12781
12795
|
return;
|
|
12782
12796
|
return base.length > 0 ? base : undefined;
|
|
12783
12797
|
}
|
|
12798
|
+
function readOverlayFile(agentName, file, agentCfg, warnings) {
|
|
12799
|
+
try {
|
|
12800
|
+
return readFileSync3(file, "utf-8");
|
|
12801
|
+
} catch (err) {
|
|
12802
|
+
const code = err.code;
|
|
12803
|
+
if (code === "ENOENT")
|
|
12804
|
+
return;
|
|
12805
|
+
const w = {
|
|
12806
|
+
agent: agentName,
|
|
12807
|
+
file,
|
|
12808
|
+
reason: `read error: ${err.message}`,
|
|
12809
|
+
code: code ?? "EUNKNOWN"
|
|
12810
|
+
};
|
|
12811
|
+
recordReadFailure(agentCfg, { file, code: w.code });
|
|
12812
|
+
warnings.push(w);
|
|
12813
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${file}': ${w.reason}`);
|
|
12814
|
+
return;
|
|
12815
|
+
}
|
|
12816
|
+
}
|
|
12784
12817
|
function overlayDirFor(agentName, subdir) {
|
|
12785
12818
|
const base = resolveDualPath(`~/.switchroom/agents/${agentName}/${subdir}`);
|
|
12786
12819
|
return resolve3(base);
|
|
12787
12820
|
}
|
|
12788
|
-
function listYamlFiles(dir) {
|
|
12821
|
+
function listYamlFiles(dir, onUnreadableDir) {
|
|
12789
12822
|
if (!existsSync3(dir))
|
|
12790
12823
|
return [];
|
|
12791
12824
|
let entries;
|
|
12792
12825
|
try {
|
|
12793
12826
|
entries = readdirSync(dir);
|
|
12794
|
-
} catch {
|
|
12827
|
+
} catch (err) {
|
|
12828
|
+
const code = err.code;
|
|
12829
|
+
if (code !== "ENOENT")
|
|
12830
|
+
onUnreadableDir?.(code ?? "EUNKNOWN");
|
|
12795
12831
|
return [];
|
|
12796
12832
|
}
|
|
12797
12833
|
const out = [];
|
|
@@ -12829,12 +12865,24 @@ function applyAgentOverlays(config) {
|
|
|
12829
12865
|
for (const [agentName, agentCfg] of Object.entries(agents)) {
|
|
12830
12866
|
try {
|
|
12831
12867
|
const scheduleDir = overlayDirFor(agentName, "schedule.d");
|
|
12832
|
-
const files = listYamlFiles(scheduleDir)
|
|
12868
|
+
const files = listYamlFiles(scheduleDir, (code) => {
|
|
12869
|
+
const w = {
|
|
12870
|
+
agent: agentName,
|
|
12871
|
+
file: scheduleDir,
|
|
12872
|
+
reason: `read error: cannot list overlay directory (${code})`,
|
|
12873
|
+
code
|
|
12874
|
+
};
|
|
12875
|
+
recordReadFailure(agentCfg, { file: scheduleDir, code });
|
|
12876
|
+
warnings.push(w);
|
|
12877
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${scheduleDir}': ${w.reason}`);
|
|
12878
|
+
});
|
|
12833
12879
|
if (files.length > 0) {
|
|
12834
12880
|
const merged = [...agentCfg.schedule ?? []];
|
|
12835
12881
|
for (const file of files) {
|
|
12882
|
+
const raw = readOverlayFile(agentName, file, agentCfg, warnings);
|
|
12883
|
+
if (raw === undefined)
|
|
12884
|
+
continue;
|
|
12836
12885
|
try {
|
|
12837
|
-
const raw = readFileSync3(file, "utf-8");
|
|
12838
12886
|
const parsed = import_yaml.parse(raw);
|
|
12839
12887
|
const doc = OverlayDocSchema.parse(parsed);
|
|
12840
12888
|
const title = deriveOverlayTitle(raw, basename(file));
|
|
@@ -12869,13 +12917,25 @@ function applyAgentOverlays(config) {
|
|
|
12869
12917
|
}
|
|
12870
12918
|
try {
|
|
12871
12919
|
const skillsDir = overlayDirFor(agentName, "skills.d");
|
|
12872
|
-
const skillFiles = listYamlFiles(skillsDir)
|
|
12920
|
+
const skillFiles = listYamlFiles(skillsDir, (code) => {
|
|
12921
|
+
const w = {
|
|
12922
|
+
agent: agentName,
|
|
12923
|
+
file: skillsDir,
|
|
12924
|
+
reason: `read error: cannot list overlay directory (${code})`,
|
|
12925
|
+
code
|
|
12926
|
+
};
|
|
12927
|
+
recordReadFailure(agentCfg, { file: skillsDir, code });
|
|
12928
|
+
warnings.push(w);
|
|
12929
|
+
console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${skillsDir}': ${w.reason}`);
|
|
12930
|
+
});
|
|
12873
12931
|
if (skillFiles.length === 0) {} else {
|
|
12874
12932
|
const merged = [...agentCfg.skills ?? []];
|
|
12875
12933
|
const seen = new Set(merged);
|
|
12876
12934
|
for (const file of skillFiles) {
|
|
12935
|
+
const raw = readOverlayFile(agentName, file, agentCfg, warnings);
|
|
12936
|
+
if (raw === undefined)
|
|
12937
|
+
continue;
|
|
12877
12938
|
try {
|
|
12878
|
-
const raw = readFileSync3(file, "utf-8");
|
|
12879
12939
|
const parsed = import_yaml.parse(raw);
|
|
12880
12940
|
const doc = OverlayDocSchema.parse(parsed);
|
|
12881
12941
|
for (const skillName of doc.skills ?? []) {
|
|
@@ -12903,7 +12963,7 @@ function applyAgentOverlays(config) {
|
|
|
12903
12963
|
}
|
|
12904
12964
|
return { config, warnings };
|
|
12905
12965
|
}
|
|
12906
|
-
var import_yaml, OVERLAY_SOURCE, OVERLAY_TITLE;
|
|
12966
|
+
var import_yaml, OVERLAY_SOURCE, OVERLAY_TITLE, OVERLAY_READ_FAILURES;
|
|
12907
12967
|
var init_overlay_loader = __esm(() => {
|
|
12908
12968
|
init_zod();
|
|
12909
12969
|
init_overlay_schema();
|
|
@@ -12911,6 +12971,7 @@ var init_overlay_loader = __esm(() => {
|
|
|
12911
12971
|
import_yaml = __toESM(require_dist(), 1);
|
|
12912
12972
|
OVERLAY_SOURCE = Symbol.for("switchroom.config.overlay-source");
|
|
12913
12973
|
OVERLAY_TITLE = Symbol.for("switchroom.config.overlay-title");
|
|
12974
|
+
OVERLAY_READ_FAILURES = Symbol.for("switchroom.config.overlay-read-failures");
|
|
12914
12975
|
});
|
|
12915
12976
|
|
|
12916
12977
|
// src/config/notion-workspace-acl.ts
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchroom",
|
|
3
3
|
"//version": "NOT the release version — source of truth is the git tag, resolved by scripts/build.mjs:resolveVersion() (see CLAUDE.md > Standard release process). This field is stale by design and only the Layer-4 dev/non-tag fallback for build.mjs + src/cli/resolve-version.ts; do NOT bump it expecting a release to pick it up. npm-pack tarball naming needs a real version — do that as an UNCOMMITTED pack-time bump (see release step 6), never a committed one.",
|
|
4
|
-
"version": "0.20.
|
|
4
|
+
"version": "0.20.8",
|
|
5
5
|
"description": "Run Claude Code 24/7 on your Claude Pro/Max subscription over Telegram. Open-source alternative to OpenClaw and NanoClaw — no API keys.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -215,10 +215,26 @@ export function createIpcClient(options: IpcClientOptions): Promise<IpcClientHan
|
|
|
215
215
|
|
|
216
216
|
function scheduleReconnect(): void {
|
|
217
217
|
if (closed) return;
|
|
218
|
+
// Single-flight: a broken connection can reach here twice (the
|
|
219
|
+
// Bun.connect failure catch AND the socket close handler both fire
|
|
220
|
+
// for the same attempt). Two live timers mean two parallel
|
|
221
|
+
// doConnect() calls and a spare socket — observed in gateway logs
|
|
222
|
+
// as an instant anonymous connect+disconnect (agent=null) right
|
|
223
|
+
// before the real re-register.
|
|
224
|
+
if (reconnectTimer) return;
|
|
218
225
|
log(`reconnecting in ${currentDelay}ms`);
|
|
219
226
|
reconnectTimer = setTimeout(() => {
|
|
220
227
|
reconnectTimer = null;
|
|
221
|
-
if (!closed)
|
|
228
|
+
if (!closed) {
|
|
229
|
+
// Swallow the rejection: doConnect's own catch has already
|
|
230
|
+
// logged the failure and scheduled the next retry. Without this,
|
|
231
|
+
// every failed BACKGROUND reconnect (e.g. the gateway restarting
|
|
232
|
+
// out from under a long-lived bridge) surfaced as a process-level
|
|
233
|
+
// unhandledRejection — "Error: Failed to connect" in
|
|
234
|
+
// bridge-crash.log — and pre-#3033 killed the bridge process
|
|
235
|
+
// outright (Claude Code never respawns a dead MCP server).
|
|
236
|
+
doConnect().catch(() => {});
|
|
237
|
+
}
|
|
222
238
|
}, currentDelay);
|
|
223
239
|
currentDelay = Math.min(currentDelay * 2, maxReconnectDelayMs);
|
|
224
240
|
}
|
|
@@ -24692,11 +24692,14 @@ function createIpcClient(options) {
|
|
|
24692
24692
|
function scheduleReconnect() {
|
|
24693
24693
|
if (closed)
|
|
24694
24694
|
return;
|
|
24695
|
+
if (reconnectTimer)
|
|
24696
|
+
return;
|
|
24695
24697
|
log(`reconnecting in ${currentDelay}ms`);
|
|
24696
24698
|
reconnectTimer = setTimeout(() => {
|
|
24697
24699
|
reconnectTimer = null;
|
|
24698
|
-
if (!closed)
|
|
24699
|
-
doConnect();
|
|
24700
|
+
if (!closed) {
|
|
24701
|
+
doConnect().catch(() => {});
|
|
24702
|
+
}
|
|
24700
24703
|
}, currentDelay);
|
|
24701
24704
|
currentDelay = Math.min(currentDelay * 2, maxReconnectDelayMs);
|
|
24702
24705
|
}
|