@sellable/install 0.1.578 → 0.1.579
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.
|
@@ -606,6 +606,21 @@ function installAdminModelConfig(profileRoot, modelConfig) {
|
|
|
606
606
|
modelConfig.credential_pool_strategies
|
|
607
607
|
);
|
|
608
608
|
document.set("fallback_providers", modelConfig.fallback_providers);
|
|
609
|
+
if (document.get("provider_routing") === undefined) {
|
|
610
|
+
// OpenRouter provider selection defaults to highest throughput; an
|
|
611
|
+
// explicit admin config value wins.
|
|
612
|
+
document.set("provider_routing", { sort: "throughput" });
|
|
613
|
+
}
|
|
614
|
+
const currentAuxiliary = document.toJS().auxiliary ?? {};
|
|
615
|
+
if (!currentAuxiliary.vision) {
|
|
616
|
+
// Non-vision main models need a working vision auxiliary; the Nous
|
|
617
|
+
// portal has no credentials on these machines, so image input would
|
|
618
|
+
// silently fail without this default.
|
|
619
|
+
document.set("auxiliary", {
|
|
620
|
+
...currentAuxiliary,
|
|
621
|
+
vision: { provider: "openrouter", model: "google/gemini-3.5-flash" },
|
|
622
|
+
});
|
|
623
|
+
}
|
|
609
624
|
atomicPrivateFile(path, document.toString());
|
|
610
625
|
const readback = parseDocument(readFileSync(path, "utf8")).toJS();
|
|
611
626
|
if (
|
|
@@ -93,11 +93,9 @@ const RUNTIME_GENERATION = Number(
|
|
|
93
93
|
const BOOT_SESSION_ID = randomUUID();
|
|
94
94
|
const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.20.0";
|
|
95
95
|
const INSTALLER_PACKAGE =
|
|
96
|
-
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ??
|
|
97
|
-
"@sellable/install@0.1.577";
|
|
96
|
+
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.577";
|
|
98
97
|
const MCP_PACKAGE =
|
|
99
|
-
process.env.SELLABLE_AGENT_MCP_PACKAGE ??
|
|
100
|
-
"@sellable/mcp@0.1.858";
|
|
98
|
+
process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.858";
|
|
101
99
|
|
|
102
100
|
const sha256 = (value) => createHash("sha256").update(value).digest("hex");
|
|
103
101
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -1639,6 +1637,16 @@ function writeRuntimeHermesConfig({
|
|
|
1639
1637
|
);
|
|
1640
1638
|
const sellable = baseConfig.mcp_servers?.sellable;
|
|
1641
1639
|
if (!sellable?.env) throw new Error("customer_mcp_config_rejected");
|
|
1640
|
+
const adminServer = baseConfig.mcp_servers?.["sellable-admin"];
|
|
1641
|
+
if (adminServer?.command && existsSync(adminServer.command)) {
|
|
1642
|
+
// The admin MCP installer has shipped this wrapper without an execute
|
|
1643
|
+
// bit; repair it here so a config refresh never revives a dead admin
|
|
1644
|
+
// server entry.
|
|
1645
|
+
const wrapperMode = lstatSync(adminServer.command).mode;
|
|
1646
|
+
if ((wrapperMode & 0o100) === 0) {
|
|
1647
|
+
chmodSync(adminServer.command, wrapperMode | 0o100);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1642
1650
|
// The named-profile materializer owns the one canonical SOUL file and seeds
|
|
1643
1651
|
// it only when absent. Runtime config refreshes must never rewrite live SOUL
|
|
1644
1652
|
// or create a root-level second copy.
|
|
@@ -1699,6 +1707,23 @@ function writeRuntimeHermesConfig({
|
|
|
1699
1707
|
: {
|
|
1700
1708
|
...modelConfig,
|
|
1701
1709
|
}),
|
|
1710
|
+
provider_routing: {
|
|
1711
|
+
// OpenRouter provider selection defaults to highest throughput; an
|
|
1712
|
+
// explicit profile value always wins.
|
|
1713
|
+
sort: "throughput",
|
|
1714
|
+
...(baseConfig.provider_routing ?? {}),
|
|
1715
|
+
},
|
|
1716
|
+
auxiliary: {
|
|
1717
|
+
...(baseConfig.auxiliary ?? {}),
|
|
1718
|
+
vision: {
|
|
1719
|
+
// Non-vision main models (e.g. deepseek) need a working vision
|
|
1720
|
+
// auxiliary; the Nous portal has no credentials on customer
|
|
1721
|
+
// machines, so image input silently fails without this default.
|
|
1722
|
+
provider: "openrouter",
|
|
1723
|
+
model: "google/gemini-3.5-flash",
|
|
1724
|
+
...(baseConfig.auxiliary?.vision ?? {}),
|
|
1725
|
+
},
|
|
1726
|
+
},
|
|
1702
1727
|
mcp_servers: {
|
|
1703
1728
|
...baseConfig.mcp_servers,
|
|
1704
1729
|
sellable: {
|
|
@@ -297,6 +297,15 @@ const MCP_META_HELPER = `
|
|
|
297
297
|
def _sellable_agent_call_meta():
|
|
298
298
|
if os.getenv("SELLABLE_AGENT_RUNTIME", "") != "1":
|
|
299
299
|
return None
|
|
300
|
+
from gateway.session_context import get_session_env, session_context_engaged
|
|
301
|
+
if (
|
|
302
|
+
not session_context_engaged()
|
|
303
|
+
or get_session_env("HERMES_SESSION_PLATFORM", "").strip().lower() != "slack"
|
|
304
|
+
):
|
|
305
|
+
# Non-Slack runtime sessions (console/TUI, cron) carry no Slack actor
|
|
306
|
+
# identity. Send no actor meta; the serving API enforces its own actor
|
|
307
|
+
# gates, as with installs outside the Sellable runtime.
|
|
308
|
+
return None
|
|
300
309
|
import json
|
|
301
310
|
gate_path = os.getenv("SELLABLE_AGENT_RUNTIME_GATE_FILE", "")
|
|
302
311
|
if not gate_path.startswith("/"):
|
|
@@ -376,8 +385,18 @@ def _sellable_agent_restore_or_build_proven_soul(
|
|
|
376
385
|
expected_home = data_root / "profiles" / profile_id
|
|
377
386
|
try:
|
|
378
387
|
observed_home = Path(get_hermes_home())
|
|
379
|
-
|
|
380
|
-
|
|
388
|
+
observed_resolved = observed_home.resolve(strict=True)
|
|
389
|
+
expected_resolved = expected_home.resolve(strict=True)
|
|
390
|
+
if observed_resolved != expected_resolved:
|
|
391
|
+
# HERMES_HOME may legitimately be the data root (the directory
|
|
392
|
+
# that CONTAINS profiles/<id>) when no per-session override is
|
|
393
|
+
# active yet -- e.g. brand-new sessions in hermes serve. Accept
|
|
394
|
+
# it only when root/profiles/<profile_id> strictly resolves to
|
|
395
|
+
# the canonical profile directory.
|
|
396
|
+
if (
|
|
397
|
+
observed_home / "profiles" / profile_id
|
|
398
|
+
).resolve(strict=True) != expected_resolved:
|
|
399
|
+
raise RuntimeError("Hermes resolved a different SOUL profile")
|
|
381
400
|
except OSError as exc:
|
|
382
401
|
raise RuntimeError("Hermes SOUL profile cannot be resolved") from exc
|
|
383
402
|
|
|
@@ -574,6 +593,15 @@ const MCP_ENDPOINT_META_HELPER = `
|
|
|
574
593
|
def _sellable_agent_call_meta():
|
|
575
594
|
if os.getenv("SELLABLE_AGENT_RUNTIME", "") != "1":
|
|
576
595
|
return None
|
|
596
|
+
from gateway.session_context import get_session_env, session_context_engaged
|
|
597
|
+
if (
|
|
598
|
+
not session_context_engaged()
|
|
599
|
+
or get_session_env("HERMES_SESSION_PLATFORM", "").strip().lower() != "slack"
|
|
600
|
+
):
|
|
601
|
+
# Non-Slack runtime sessions (console/TUI, cron) carry no Slack actor
|
|
602
|
+
# identity. Send no actor meta; the serving API enforces its own actor
|
|
603
|
+
# gates, as with installs outside the Sellable runtime.
|
|
604
|
+
return None
|
|
577
605
|
import json
|
|
578
606
|
gate_path = os.getenv("SELLABLE_AGENT_RUNTIME_GATE_FILE", "")
|
|
579
607
|
if not gate_path.startswith("/"):
|
|
@@ -634,15 +662,11 @@ const MCP_ENDPOINT_META_HELPER_OPTIONAL_SESSION =
|
|
|
634
662
|
);
|
|
635
663
|
|
|
636
664
|
const CRON_MCP_ISOLATION_HELPER = `
|
|
637
|
-
# ${HERMES_AGENT_BRIDGE_MARKER}: cron
|
|
665
|
+
# ${HERMES_AGENT_BRIDGE_MARKER}: cron sessions may use Sellable MCP toolsets.
|
|
638
666
|
def _sellable_agent_strip_cron_mcp(toolsets, cfg):
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
from hermes_cli.tools_config import enabled_mcp_server_names
|
|
643
|
-
mcp_names = set(enabled_mcp_server_names(cfg or {}))
|
|
644
|
-
mcp_names.add("mcp")
|
|
645
|
-
return [name for name in values if name not in mcp_names]
|
|
667
|
+
# Pass-through, kept for injection-site stability: cron sessions carry no
|
|
668
|
+
# Slack actor meta and the serving API enforces its own actor gates.
|
|
669
|
+
return list(toolsets or [])
|
|
646
670
|
`;
|
|
647
671
|
|
|
648
672
|
const V020_WEB_TOOLSET_DISCOVERY_HELPER = `
|
|
@@ -1569,18 +1593,6 @@ export function patchHermesSourceFile(patchKind, source) {
|
|
|
1569
1593
|
' return _sellable_agent_strip_cron_mcp(\n sorted(_get_platform_tools(cfg or {}, "cron")), cfg or {}\n )',
|
|
1570
1594
|
"v0.20 platform cron MCP isolation"
|
|
1571
1595
|
);
|
|
1572
|
-
patched = replaceExactlyOnce(
|
|
1573
|
-
patched,
|
|
1574
|
-
" return None\n\n# Valid delivery platforms",
|
|
1575
|
-
[
|
|
1576
|
-
' if os.getenv("SELLABLE_AGENT_RUNTIME", "") == "1":',
|
|
1577
|
-
' return _sellable_agent_strip_cron_mcp(["safe"], cfg or {})',
|
|
1578
|
-
" return None",
|
|
1579
|
-
"",
|
|
1580
|
-
"# Valid delivery platforms",
|
|
1581
|
-
].join("\n"),
|
|
1582
|
-
"v0.20 cron MCP fail-closed fallback"
|
|
1583
|
-
);
|
|
1584
1596
|
return patched;
|
|
1585
1597
|
}
|
|
1586
1598
|
|