@turmind/halo 1.6.5 → 1.6.7
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/README.md +1 -0
- package/admin-out/404.html +1 -1
- package/admin-out/_next/static/chunks/989-cb287787a4aca288.js +1 -0
- package/admin-out/_next/static/chunks/app/{page-812bcbb46db628b6.js → page-3584adfe2f9a0913.js} +1 -1
- package/admin-out/index.html +1 -1
- package/admin-out/index.txt +4 -4
- package/bundled-docs/design/a2a.md +1 -1
- package/bundled-docs/design/canvas-extensions.md +8 -0
- package/bundled-docs/design/cron.md +1 -1
- package/bundled-docs/design/feishu.md +2 -0
- package/bundled-docs/design/halo-city.md +4 -0
- package/bundled-docs/design/slack.md +2 -2
- package/bundled-docs/design/wecom.md +1 -1
- package/bundled-docs/dev/add-model-provider.md +1 -1
- package/bundled-docs/dev/api.md +6 -5
- package/bundled-docs/guide/secrets-and-credentials.md +17 -5
- package/bundled-docs/requirements/settings.md +3 -0
- package/dist/index.js +199 -73
- package/package.json +1 -1
- package/templates/models/mimo-token-plan-china.yaml +3 -55
- package/templates/skills/halo/SKILL.md +2 -0
- package/admin-out/_next/static/chunks/989-066a874961bd519f.js +0 -1
- /package/admin-out/_next/static/{8v1hCXbUqJhyMuK7LVeet → x5jl6YGvzL0Ys4lxsHVg1}/_buildManifest.js +0 -0
- /package/admin-out/_next/static/{8v1hCXbUqJhyMuK7LVeet → x5jl6YGvzL0Ys4lxsHVg1}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -2227,6 +2227,7 @@ __export(config_exports, {
|
|
|
2227
2227
|
getServerParam: () => getServerParam,
|
|
2228
2228
|
getServerSecret: () => getServerSecret,
|
|
2229
2229
|
invalidateModelsRegistry: () => invalidateModelsRegistry,
|
|
2230
|
+
missingEnvRefs: () => missingEnvRefs,
|
|
2230
2231
|
modelSupportsAudio: () => modelSupportsAudio,
|
|
2231
2232
|
modelSupportsImage: () => modelSupportsImage,
|
|
2232
2233
|
modelSupportsVideo: () => modelSupportsVideo,
|
|
@@ -2410,6 +2411,9 @@ function expandEnv(raw) {
|
|
|
2410
2411
|
return "";
|
|
2411
2412
|
return raw.replace(ENV_PATTERN2, (m, name) => process.env[name] ?? m);
|
|
2412
2413
|
}
|
|
2414
|
+
function missingEnvRefs(raw) {
|
|
2415
|
+
return [...new Set([...raw.matchAll(ENV_PATTERN2)].map((m) => m[1]))].filter((name) => process.env[name] === void 0);
|
|
2416
|
+
}
|
|
2413
2417
|
function getWorkspaceSettings(workspaceRoot) {
|
|
2414
2418
|
const p = path11.join(workspaceRoot, ".halo", "settings.yaml");
|
|
2415
2419
|
let mtimeMs = 0;
|
|
@@ -2900,6 +2904,8 @@ function classifyKind(msg, errName, httpStatus) {
|
|
|
2900
2904
|
if (msg.includes("too many input tokens") || msg.includes("prompt_too_long") || msg.includes("ContextWindowOverflow") || msg.includes("Input is too long")) {
|
|
2901
2905
|
return "context_overflow";
|
|
2902
2906
|
}
|
|
2907
|
+
if (errName === "MissingEnvCredentialError")
|
|
2908
|
+
return "account";
|
|
2903
2909
|
const isAccountError = httpStatus !== void 0 ? httpStatus === 401 || httpStatus === 402 || httpStatus === 403 : msg.includes("insufficient balance") || msg.includes("suspended") || msg.includes("invalid api key") || msg.includes("Invalid API Key") || msg.includes("Unauthorized") || msg.includes("authentication");
|
|
2904
2910
|
if (isAccountError) {
|
|
2905
2911
|
return "account";
|
|
@@ -2924,9 +2930,13 @@ function classifyKind(msg, errName, httpStatus) {
|
|
|
2924
2930
|
}
|
|
2925
2931
|
return "fatal";
|
|
2926
2932
|
}
|
|
2933
|
+
var MissingEnvCredentialError;
|
|
2927
2934
|
var init_model_error = __esm({
|
|
2928
2935
|
"../server/dist/agents/model-error.js"() {
|
|
2929
2936
|
"use strict";
|
|
2937
|
+
MissingEnvCredentialError = class extends Error {
|
|
2938
|
+
name = "MissingEnvCredentialError";
|
|
2939
|
+
};
|
|
2930
2940
|
}
|
|
2931
2941
|
});
|
|
2932
2942
|
|
|
@@ -4149,7 +4159,7 @@ var init_init = __esm({
|
|
|
4149
4159
|
path14.resolve(__dirname, "..", "bundled-docs"),
|
|
4150
4160
|
path14.resolve(__dirname, "..", "..", "..", ".halo", "docs")
|
|
4151
4161
|
];
|
|
4152
|
-
TEMPLATE_VERSION =
|
|
4162
|
+
TEMPLATE_VERSION = 105;
|
|
4153
4163
|
VERSION_FILE = ".template-version";
|
|
4154
4164
|
SKIP_NAMES = /* @__PURE__ */ new Set([".DS_Store", "schema.sql", "__pycache__", ".pytest_cache"]);
|
|
4155
4165
|
BUILTIN_AGENT_IDS = /* @__PURE__ */ new Set([
|
|
@@ -13708,6 +13718,7 @@ var init_session_agent_builder = __esm({
|
|
|
13708
13718
|
init_system_prompts();
|
|
13709
13719
|
init_md_loader();
|
|
13710
13720
|
init_config();
|
|
13721
|
+
init_model_error();
|
|
13711
13722
|
init_agent_loader();
|
|
13712
13723
|
init_db();
|
|
13713
13724
|
init_skill_sources();
|
|
@@ -14023,6 +14034,16 @@ ${roster}`;
|
|
|
14023
14034
|
apiKey,
|
|
14024
14035
|
sessionId
|
|
14025
14036
|
});
|
|
14037
|
+
const unresolved = [
|
|
14038
|
+
...apiKey ? [{ key: "api_key", value: apiKey }] : [],
|
|
14039
|
+
...awsCreds2 ? [{ key: "access_key_id", value: awsCreds2.accessKeyId }, { key: "secret_access_key", value: awsCreds2.secretAccessKey }] : []
|
|
14040
|
+
].map((f) => ({ key: f.key, names: missingEnvRefs(f.value) })).filter((f) => f.names.length > 0);
|
|
14041
|
+
if (unresolved.length > 0) {
|
|
14042
|
+
const names = [...new Set(unresolved.flatMap((f) => f.names))];
|
|
14043
|
+
const message = `${providerId} ${unresolved.map((f) => `${f.key} uses ${f.names.map((n) => `<<${n}>>`).join(" ")}`).join(", ")}, but ${names.join(", ")} ${names.length > 1 ? "are" : "is"} not set in the Halo server's environment. Background servers (systemd, \`halo server start -d\`) and the desktop app opened from the Dock don't read ~/.bashrc / ~/.zshrc \u2014 enter the key in Settings, or add the variable to the server's environment and restart it (from a new terminal: \`halo server restart\`).`;
|
|
14044
|
+
const run = agent.run.bind(agent);
|
|
14045
|
+
agent.run = (input, options) => run(input, { ...options, beforeCallModel: () => Promise.reject(new MissingEnvCredentialError(message)) });
|
|
14046
|
+
}
|
|
14026
14047
|
const thinkingEffort = thinkingConfig?.enabled ? thinkingConfig.effort ?? thinkingConfig.budget ?? (thinkingConfig.budget_tokens != null ? `${thinkingConfig.budget_tokens}` : "medium") : "off";
|
|
14027
14048
|
return { agent, thinkingEffort, contextConfig };
|
|
14028
14049
|
}
|
|
@@ -14303,7 +14324,7 @@ async function initObservability(haloProcess = "server") {
|
|
|
14303
14324
|
return true;
|
|
14304
14325
|
}
|
|
14305
14326
|
const { registerSdk: registerSdk2 } = await Promise.resolve().then(() => (init_otel_sdk(), otel_sdk_exports));
|
|
14306
|
-
providers = await registerSdk2(process.env.OTEL_SERVICE_NAME, "1.6.
|
|
14327
|
+
providers = await registerSdk2(process.env.OTEL_SERVICE_NAME, "1.6.7", haloProcess);
|
|
14307
14328
|
console.log(`[Observability] OTLP export \u2192 ${process.env.OTEL_EXPORTER_OTLP_ENDPOINT} (service.name=${process.env.OTEL_SERVICE_NAME}, halo.process=${haloProcess}, capture_content=${config.observability.captureContent})`);
|
|
14308
14329
|
return true;
|
|
14309
14330
|
}
|
|
@@ -23792,7 +23813,8 @@ function fieldFor(section, f, globalTree, workspaceTree) {
|
|
|
23792
23813
|
value,
|
|
23793
23814
|
hasValue: raw !== void 0,
|
|
23794
23815
|
source,
|
|
23795
|
-
inheritedFromGlobal
|
|
23816
|
+
inheritedFromGlobal,
|
|
23817
|
+
envMissing: raw === void 0 ? [] : missingEnvRefs(raw)
|
|
23796
23818
|
};
|
|
23797
23819
|
}
|
|
23798
23820
|
function detectOrphans(tree, declared) {
|
|
@@ -23953,6 +23975,7 @@ var init_settings = __esm({
|
|
|
23953
23975
|
"use strict";
|
|
23954
23976
|
init_settings_schema();
|
|
23955
23977
|
init_workspace_path();
|
|
23978
|
+
init_config();
|
|
23956
23979
|
GLOBAL_SETTINGS_PATH2 = path64.join(homedir22(), ".halo", "secrets", "settings.yaml");
|
|
23957
23980
|
_changeListeners = [];
|
|
23958
23981
|
}
|
|
@@ -25556,10 +25579,27 @@ var init_session_types = __esm({
|
|
|
25556
25579
|
}
|
|
25557
25580
|
});
|
|
25558
25581
|
|
|
25582
|
+
// ../server/dist/routes/etag.js
|
|
25583
|
+
function etagMatches(header, etag) {
|
|
25584
|
+
if (!header)
|
|
25585
|
+
return false;
|
|
25586
|
+
const bare = etag.replace(/^W\//, "");
|
|
25587
|
+
return header.split(",").some((t2) => {
|
|
25588
|
+
const tag = t2.trim();
|
|
25589
|
+
return tag === "*" || tag.replace(/^W\//, "") === bare;
|
|
25590
|
+
});
|
|
25591
|
+
}
|
|
25592
|
+
var init_etag = __esm({
|
|
25593
|
+
"../server/dist/routes/etag.js"() {
|
|
25594
|
+
"use strict";
|
|
25595
|
+
}
|
|
25596
|
+
});
|
|
25597
|
+
|
|
25559
25598
|
// ../server/dist/routes/halo-city.js
|
|
25560
25599
|
import { Hono as Hono12 } from "hono";
|
|
25561
25600
|
import fs59 from "node:fs";
|
|
25562
25601
|
import path67 from "node:path";
|
|
25602
|
+
import crypto6 from "node:crypto";
|
|
25563
25603
|
import Database7 from "better-sqlite3";
|
|
25564
25604
|
function scanSkills2(dir) {
|
|
25565
25605
|
let names;
|
|
@@ -25843,6 +25883,10 @@ function discoverWorkspaces(registry2) {
|
|
|
25843
25883
|
}
|
|
25844
25884
|
return out;
|
|
25845
25885
|
}
|
|
25886
|
+
function stateEtag(accessLevel, skills, workspaces, uptime) {
|
|
25887
|
+
const json = JSON.stringify({ accessLevel, skills, workspaces, uptimeMin: Math.floor(uptime / 60) });
|
|
25888
|
+
return `W/"${crypto6.createHash("sha1").update(json).digest("base64url")}"`;
|
|
25889
|
+
}
|
|
25846
25890
|
function createShowRoutes(registry2) {
|
|
25847
25891
|
const app2 = new Hono12();
|
|
25848
25892
|
function auth(c) {
|
|
@@ -25875,11 +25919,18 @@ function createShowRoutes(registry2) {
|
|
|
25875
25919
|
}
|
|
25876
25920
|
}
|
|
25877
25921
|
workspaces.sort((x, y) => workspaceBirth(x.path) - workspaceBirth(y.path) || x.key.localeCompare(y.key));
|
|
25922
|
+
const uptime = process.uptime();
|
|
25923
|
+
const skills = scanSkills2(GLOBAL_SKILLS_DIR);
|
|
25924
|
+
const etag = stateEtag(a.accessLevel, skills, workspaces, uptime);
|
|
25925
|
+
c.header("ETag", etag);
|
|
25926
|
+
c.header("Cache-Control", "private, no-cache");
|
|
25927
|
+
if (etagMatches(c.req.header("if-none-match"), etag))
|
|
25928
|
+
return c.body(null, 304);
|
|
25878
25929
|
return c.json({
|
|
25879
25930
|
serverTime: Date.now(),
|
|
25880
|
-
uptime
|
|
25931
|
+
uptime,
|
|
25881
25932
|
accessLevel: a.accessLevel,
|
|
25882
|
-
skills
|
|
25933
|
+
skills,
|
|
25883
25934
|
workspaces
|
|
25884
25935
|
});
|
|
25885
25936
|
});
|
|
@@ -25957,6 +26008,7 @@ var init_halo_city = __esm({
|
|
|
25957
26008
|
init_session_store();
|
|
25958
26009
|
init_session_types();
|
|
25959
26010
|
init_mtime_cache();
|
|
26011
|
+
init_etag();
|
|
25960
26012
|
SESSIONS_PER_WS = 80;
|
|
25961
26013
|
_skillCache = createMtimeCache();
|
|
25962
26014
|
_metaCache = /* @__PURE__ */ new Map();
|
|
@@ -28125,7 +28177,9 @@ function setupWebSocketHandler(deps) {
|
|
|
28125
28177
|
sendJson(ws, { type: "error", error: "No workspace context for delete", sessionId: delSessionId });
|
|
28126
28178
|
return;
|
|
28127
28179
|
}
|
|
28128
|
-
await sm.deleteSession(delSessionId)
|
|
28180
|
+
for (const id of await sm.deleteSession(delSessionId)) {
|
|
28181
|
+
await findAndDeleteSessionFile(id, sm.workspaceRoot);
|
|
28182
|
+
}
|
|
28129
28183
|
client.subscriptions.get(delSessionId)?.();
|
|
28130
28184
|
client.subscriptions.delete(delSessionId);
|
|
28131
28185
|
sendJson(ws, { type: "session:deleted", sessionId: delSessionId });
|
|
@@ -28876,7 +28930,7 @@ CREATE INDEX IF NOT EXISTS idx_cron_runs_status ON cron_runs(status);
|
|
|
28876
28930
|
// ../server/dist/a2a/exposure.js
|
|
28877
28931
|
import fs65 from "node:fs";
|
|
28878
28932
|
import path74 from "node:path";
|
|
28879
|
-
import
|
|
28933
|
+
import crypto7 from "node:crypto";
|
|
28880
28934
|
function samePath(a, b, platform = process.platform) {
|
|
28881
28935
|
if (platform !== "win32")
|
|
28882
28936
|
return a === b;
|
|
@@ -28942,7 +28996,7 @@ function buildCard(workspace, interfaceUrl, caps) {
|
|
|
28942
28996
|
return {
|
|
28943
28997
|
name: raw.name,
|
|
28944
28998
|
description: raw.description,
|
|
28945
|
-
version: typeof raw.version === "string" ? raw.version : "1.6.
|
|
28999
|
+
version: typeof raw.version === "string" ? raw.version : "1.6.7",
|
|
28946
29000
|
supportedInterfaces: [{ url: interfaceUrl, protocolBinding: "JSONRPC", protocolVersion: A2A_VERSION }],
|
|
28947
29001
|
capabilities: { streaming: caps.streaming, pushNotifications: true, extendedAgentCard: false },
|
|
28948
29002
|
...caps.tokenAuth === false ? {} : {
|
|
@@ -28958,7 +29012,7 @@ function buildCard(workspace, interfaceUrl, caps) {
|
|
|
28958
29012
|
};
|
|
28959
29013
|
}
|
|
28960
29014
|
function etagOf(body) {
|
|
28961
|
-
return `"${
|
|
29015
|
+
return `W/"${crypto7.createHash("sha1").update(body).digest("base64url")}"`;
|
|
28962
29016
|
}
|
|
28963
29017
|
var CARD_FILE, IO_MODES, baseCache, homeStrategies;
|
|
28964
29018
|
var init_exposure = __esm({
|
|
@@ -29001,7 +29055,7 @@ var init_exposure = __esm({
|
|
|
29001
29055
|
// ../server/dist/a2a/routes.js
|
|
29002
29056
|
import { Hono as Hono16 } from "hono";
|
|
29003
29057
|
import { streamSSE } from "hono/streaming";
|
|
29004
|
-
import
|
|
29058
|
+
import crypto8 from "node:crypto";
|
|
29005
29059
|
function str(v) {
|
|
29006
29060
|
return typeof v === "string" ? v : "";
|
|
29007
29061
|
}
|
|
@@ -29111,7 +29165,7 @@ async function dispatchMessage(x, p) {
|
|
|
29111
29165
|
}
|
|
29112
29166
|
const newSession = !sessionId;
|
|
29113
29167
|
if (newSession)
|
|
29114
|
-
sessionId = `${sessionPrefixFor(x.caller)}${Date.now().toString(36)}${
|
|
29168
|
+
sessionId = `${sessionPrefixFor(x.caller)}${Date.now().toString(36)}${crypto8.randomBytes(3).toString("hex")}`;
|
|
29115
29169
|
const followUp = task !== null;
|
|
29116
29170
|
if (!task) {
|
|
29117
29171
|
try {
|
|
@@ -29315,7 +29369,7 @@ function createA2ARoutes(deps) {
|
|
|
29315
29369
|
const etag = etagOf(body);
|
|
29316
29370
|
c.header("Cache-Control", "private, max-age=300");
|
|
29317
29371
|
c.header("ETag", etag);
|
|
29318
|
-
if (c.req.header("if-none-match")
|
|
29372
|
+
if (etagMatches(c.req.header("if-none-match"), etag))
|
|
29319
29373
|
return c.body(null, 304);
|
|
29320
29374
|
return c.body(body, 200, { "Content-Type": "application/json" });
|
|
29321
29375
|
});
|
|
@@ -29390,6 +29444,7 @@ var init_routes = __esm({
|
|
|
29390
29444
|
init_commands2();
|
|
29391
29445
|
init_accounts();
|
|
29392
29446
|
init_session_prefix();
|
|
29447
|
+
init_etag();
|
|
29393
29448
|
init_url_policy();
|
|
29394
29449
|
init_files();
|
|
29395
29450
|
init_exposure();
|
|
@@ -29428,7 +29483,7 @@ var init_routes = __esm({
|
|
|
29428
29483
|
import fs66 from "node:fs";
|
|
29429
29484
|
import os10 from "node:os";
|
|
29430
29485
|
import path75 from "node:path";
|
|
29431
|
-
import
|
|
29486
|
+
import crypto9 from "node:crypto";
|
|
29432
29487
|
import { Hono as Hono17 } from "hono";
|
|
29433
29488
|
function readLease(file) {
|
|
29434
29489
|
let raw;
|
|
@@ -29611,7 +29666,7 @@ var init_agentcore = __esm({
|
|
|
29611
29666
|
WorkspaceLease = class {
|
|
29612
29667
|
file;
|
|
29613
29668
|
hooks;
|
|
29614
|
-
owner =
|
|
29669
|
+
owner = crypto9.randomUUID();
|
|
29615
29670
|
_held = false;
|
|
29616
29671
|
timer = null;
|
|
29617
29672
|
waitLogged = false;
|
|
@@ -32720,7 +32775,7 @@ var init_descriptor = __esm({
|
|
|
32720
32775
|
});
|
|
32721
32776
|
|
|
32722
32777
|
// ../server/dist/channels/wechat/api.js
|
|
32723
|
-
import
|
|
32778
|
+
import crypto10 from "node:crypto";
|
|
32724
32779
|
function buildClientVersion(version) {
|
|
32725
32780
|
const [ma, mi, pa] = version.split(".").map((p) => parseInt(p, 10));
|
|
32726
32781
|
return (ma & 255) << 16 | (mi & 255) << 8 | pa & 255;
|
|
@@ -32729,7 +32784,7 @@ function buildBaseInfo() {
|
|
|
32729
32784
|
return { channel_version: CHANNEL_VERSION };
|
|
32730
32785
|
}
|
|
32731
32786
|
function randomWechatUin() {
|
|
32732
|
-
const n =
|
|
32787
|
+
const n = crypto10.randomBytes(4).readUInt32BE(0);
|
|
32733
32788
|
return Buffer.from(String(n), "utf-8").toString("base64");
|
|
32734
32789
|
}
|
|
32735
32790
|
function commonHeaders() {
|
|
@@ -34106,7 +34161,7 @@ var init_descriptor2 = __esm({
|
|
|
34106
34161
|
// ../server/dist/routes/web.js
|
|
34107
34162
|
import { Hono as Hono22 } from "hono";
|
|
34108
34163
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
34109
|
-
import
|
|
34164
|
+
import crypto11 from "node:crypto";
|
|
34110
34165
|
import fs77 from "node:fs";
|
|
34111
34166
|
function streamWithKeepalive(c, chunks) {
|
|
34112
34167
|
return streamSSE2(c, async (stream) => {
|
|
@@ -34144,8 +34199,8 @@ function createWebRoutes(deps) {
|
|
|
34144
34199
|
const bodyError = accountBodyError(body, ACCOUNT_ACCESS_LEVELS);
|
|
34145
34200
|
if (bodyError)
|
|
34146
34201
|
return c.json({ error: bodyError }, 400);
|
|
34147
|
-
const accountId =
|
|
34148
|
-
const token =
|
|
34202
|
+
const accountId = crypto11.randomUUID().slice(0, 8);
|
|
34203
|
+
const token = crypto11.randomBytes(24).toString("base64url");
|
|
34149
34204
|
insertAccount2(db3, {
|
|
34150
34205
|
accountId,
|
|
34151
34206
|
token,
|
|
@@ -34483,7 +34538,7 @@ function formatForFeishu(input) {
|
|
|
34483
34538
|
inFence = !inFence;
|
|
34484
34539
|
continue;
|
|
34485
34540
|
}
|
|
34486
|
-
if (inFence) {
|
|
34541
|
+
if (inFence || /^MEDIA:/.test(raw)) {
|
|
34487
34542
|
out.push(raw);
|
|
34488
34543
|
continue;
|
|
34489
34544
|
}
|
|
@@ -34508,16 +34563,16 @@ var init_markdown = __esm({
|
|
|
34508
34563
|
});
|
|
34509
34564
|
|
|
34510
34565
|
// ../server/dist/channels/slack/event-adapter.js
|
|
34511
|
-
var
|
|
34566
|
+
var SLACK_TEXT_LIMIT, SlackResponder;
|
|
34512
34567
|
var init_event_adapter3 = __esm({
|
|
34513
34568
|
"../server/dist/channels/slack/event-adapter.js"() {
|
|
34514
34569
|
"use strict";
|
|
34515
34570
|
init_markdown();
|
|
34516
34571
|
init_responder();
|
|
34517
|
-
|
|
34572
|
+
SLACK_TEXT_LIMIT = 35e3;
|
|
34518
34573
|
SlackResponder = class extends ChunkedResponder {
|
|
34519
34574
|
constructor(deps) {
|
|
34520
|
-
super(deps, { limit:
|
|
34575
|
+
super(deps, { limit: SLACK_TEXT_LIMIT, logTag: "Slack", format: formatForSlack });
|
|
34521
34576
|
}
|
|
34522
34577
|
};
|
|
34523
34578
|
}
|
|
@@ -35253,18 +35308,17 @@ async function dispatch3(accountId, text6, explicitChatId, media) {
|
|
|
35253
35308
|
const { channel, threadTs } = parseChatId(explicitChatId);
|
|
35254
35309
|
const out = [];
|
|
35255
35310
|
if (text6) {
|
|
35256
|
-
|
|
35257
|
-
|
|
35258
|
-
|
|
35259
|
-
|
|
35260
|
-
|
|
35261
|
-
|
|
35262
|
-
|
|
35263
|
-
|
|
35264
|
-
|
|
35265
|
-
error: err instanceof Error ? err.message : String(err)
|
|
35266
|
-
});
|
|
35311
|
+
const chunks = splitText(text6, SLACK_TEXT_LIMIT);
|
|
35312
|
+
let failed;
|
|
35313
|
+
for (const [i, chunk] of chunks.entries()) {
|
|
35314
|
+
try {
|
|
35315
|
+
await postMessage({ botToken: acct.botToken, channel, threadTs, text: formatForSlack(chunk) });
|
|
35316
|
+
} catch (err) {
|
|
35317
|
+
failed = `chunk ${i + 1}/${chunks.length}: ${err instanceof Error ? err.message : String(err)}`;
|
|
35318
|
+
break;
|
|
35319
|
+
}
|
|
35267
35320
|
}
|
|
35321
|
+
out.push(failed ? { channelType: "slack", accountId, chatId: explicitChatId, ok: false, error: failed } : { channelType: "slack", accountId, chatId: explicitChatId, ok: true });
|
|
35268
35322
|
}
|
|
35269
35323
|
for (const filePath of media?.paths ?? []) {
|
|
35270
35324
|
if (!isMediaPathAllowed(filePath, media.workspacePath)) {
|
|
@@ -35303,6 +35357,9 @@ var init_cron_dispatcher3 = __esm({
|
|
|
35303
35357
|
init_channel_db();
|
|
35304
35358
|
init_accounts5();
|
|
35305
35359
|
init_api2();
|
|
35360
|
+
init_event_adapter3();
|
|
35361
|
+
init_chunk();
|
|
35362
|
+
init_markdown();
|
|
35306
35363
|
init_media();
|
|
35307
35364
|
init_dispatcher();
|
|
35308
35365
|
}
|
|
@@ -35380,16 +35437,16 @@ var init_accounts6 = __esm({
|
|
|
35380
35437
|
});
|
|
35381
35438
|
|
|
35382
35439
|
// ../server/dist/channels/feishu/event-adapter.js
|
|
35383
|
-
var
|
|
35440
|
+
var FEISHU_TEXT_LIMIT, FeishuResponder;
|
|
35384
35441
|
var init_event_adapter4 = __esm({
|
|
35385
35442
|
"../server/dist/channels/feishu/event-adapter.js"() {
|
|
35386
35443
|
"use strict";
|
|
35387
35444
|
init_markdown();
|
|
35388
35445
|
init_responder();
|
|
35389
|
-
|
|
35446
|
+
FEISHU_TEXT_LIMIT = 4500;
|
|
35390
35447
|
FeishuResponder = class extends ChunkedResponder {
|
|
35391
35448
|
constructor(deps) {
|
|
35392
|
-
super(deps, { limit:
|
|
35449
|
+
super(deps, { limit: FEISHU_TEXT_LIMIT, logTag: "Feishu", format: formatForFeishu });
|
|
35393
35450
|
}
|
|
35394
35451
|
};
|
|
35395
35452
|
}
|
|
@@ -36211,25 +36268,28 @@ async function dispatch4(accountId, text6, explicitChatId) {
|
|
|
36211
36268
|
throw new Error('feishu cron target requires an explicit chatId (e.g. "oc_abcd1234"). Create the cron from inside a feishu chat to auto-pin, or pass --targets feishu:<accountId>:<chatId>.');
|
|
36212
36269
|
}
|
|
36213
36270
|
const { chatId } = parseChatId2(explicitChatId);
|
|
36214
|
-
|
|
36215
|
-
|
|
36216
|
-
|
|
36217
|
-
|
|
36218
|
-
|
|
36219
|
-
|
|
36220
|
-
|
|
36221
|
-
|
|
36222
|
-
|
|
36223
|
-
|
|
36224
|
-
|
|
36225
|
-
|
|
36226
|
-
|
|
36227
|
-
|
|
36228
|
-
|
|
36229
|
-
|
|
36230
|
-
|
|
36231
|
-
|
|
36271
|
+
const chunks = splitText(text6, FEISHU_TEXT_LIMIT);
|
|
36272
|
+
for (const [i, chunk] of chunks.entries()) {
|
|
36273
|
+
try {
|
|
36274
|
+
await sendMessage3({
|
|
36275
|
+
appId: acct.appId,
|
|
36276
|
+
appSecret: acct.appSecret,
|
|
36277
|
+
receiveIdType: "chat_id",
|
|
36278
|
+
receiveId: chatId,
|
|
36279
|
+
msgType: "text",
|
|
36280
|
+
content: { text: formatForFeishu(chunk) }
|
|
36281
|
+
});
|
|
36282
|
+
} catch (err) {
|
|
36283
|
+
return [{
|
|
36284
|
+
channelType: "feishu",
|
|
36285
|
+
accountId,
|
|
36286
|
+
chatId: explicitChatId,
|
|
36287
|
+
ok: false,
|
|
36288
|
+
error: `chunk ${i + 1}/${chunks.length}: ${err instanceof Error ? err.message : String(err)}`
|
|
36289
|
+
}];
|
|
36290
|
+
}
|
|
36232
36291
|
}
|
|
36292
|
+
return [{ channelType: "feishu", accountId, chatId: explicitChatId, ok: true }];
|
|
36233
36293
|
}
|
|
36234
36294
|
function listTargets4() {
|
|
36235
36295
|
const cdb = getChannelDb();
|
|
@@ -36251,6 +36311,9 @@ var init_cron_dispatcher4 = __esm({
|
|
|
36251
36311
|
init_channel_db();
|
|
36252
36312
|
init_accounts6();
|
|
36253
36313
|
init_api3();
|
|
36314
|
+
init_event_adapter4();
|
|
36315
|
+
init_chunk();
|
|
36316
|
+
init_markdown();
|
|
36254
36317
|
init_dispatcher();
|
|
36255
36318
|
}
|
|
36256
36319
|
});
|
|
@@ -36324,15 +36387,15 @@ var init_accounts7 = __esm({
|
|
|
36324
36387
|
});
|
|
36325
36388
|
|
|
36326
36389
|
// ../server/dist/channels/wecom/event-adapter.js
|
|
36327
|
-
var
|
|
36390
|
+
var WECOM_TEXT_LIMIT, WecomResponder;
|
|
36328
36391
|
var init_event_adapter5 = __esm({
|
|
36329
36392
|
"../server/dist/channels/wecom/event-adapter.js"() {
|
|
36330
36393
|
"use strict";
|
|
36331
36394
|
init_responder();
|
|
36332
|
-
|
|
36395
|
+
WECOM_TEXT_LIMIT = 5e3;
|
|
36333
36396
|
WecomResponder = class extends ChunkedResponder {
|
|
36334
36397
|
constructor(deps) {
|
|
36335
|
-
super(deps, { limit:
|
|
36398
|
+
super(deps, { limit: WECOM_TEXT_LIMIT, logTag: "WeCom" });
|
|
36336
36399
|
}
|
|
36337
36400
|
};
|
|
36338
36401
|
}
|
|
@@ -36838,18 +36901,21 @@ async function dispatch5(accountId, text6, explicitChatId) {
|
|
|
36838
36901
|
if (!client) {
|
|
36839
36902
|
return [{ channelType: "wecom", accountId, chatId: explicitChatId, ok: false, error: "wecom long-connect not active" }];
|
|
36840
36903
|
}
|
|
36841
|
-
|
|
36842
|
-
|
|
36843
|
-
|
|
36844
|
-
|
|
36845
|
-
|
|
36846
|
-
|
|
36847
|
-
|
|
36848
|
-
|
|
36849
|
-
|
|
36850
|
-
|
|
36851
|
-
|
|
36904
|
+
const chunks = splitText(text6, WECOM_TEXT_LIMIT);
|
|
36905
|
+
for (const [i, chunk] of chunks.entries()) {
|
|
36906
|
+
try {
|
|
36907
|
+
await client.sendMessage(explicitChatId, { msgtype: "markdown", markdown: { content: chunk } });
|
|
36908
|
+
} catch (err) {
|
|
36909
|
+
return [{
|
|
36910
|
+
channelType: "wecom",
|
|
36911
|
+
accountId,
|
|
36912
|
+
chatId: explicitChatId,
|
|
36913
|
+
ok: false,
|
|
36914
|
+
error: `chunk ${i + 1}/${chunks.length}: ${err instanceof Error ? err.message : String(err)}`
|
|
36915
|
+
}];
|
|
36916
|
+
}
|
|
36852
36917
|
}
|
|
36918
|
+
return [{ channelType: "wecom", accountId, chatId: explicitChatId, ok: true }];
|
|
36853
36919
|
}
|
|
36854
36920
|
function listTargets5() {
|
|
36855
36921
|
const cdb = getChannelDb();
|
|
@@ -36871,6 +36937,8 @@ var init_cron_dispatcher5 = __esm({
|
|
|
36871
36937
|
init_channel_db();
|
|
36872
36938
|
init_accounts7();
|
|
36873
36939
|
init_handler7();
|
|
36940
|
+
init_event_adapter5();
|
|
36941
|
+
init_chunk();
|
|
36874
36942
|
init_dispatcher();
|
|
36875
36943
|
}
|
|
36876
36944
|
});
|
|
@@ -36915,6 +36983,64 @@ var init_descriptors = __esm({
|
|
|
36915
36983
|
}
|
|
36916
36984
|
});
|
|
36917
36985
|
|
|
36986
|
+
// ../server/dist/middleware/compress.js
|
|
36987
|
+
import { compress, COMPRESSIBLE_CONTENT_TYPE_REGEX } from "hono/compress";
|
|
36988
|
+
function compressResponses(threshold) {
|
|
36989
|
+
const inner = compress({ threshold });
|
|
36990
|
+
return (c, next) => inner(c, async () => {
|
|
36991
|
+
await next();
|
|
36992
|
+
if (c.res.status === 304)
|
|
36993
|
+
varyAcceptEncoding(c);
|
|
36994
|
+
else
|
|
36995
|
+
await setProbedLength(c, threshold);
|
|
36996
|
+
});
|
|
36997
|
+
}
|
|
36998
|
+
function varyAcceptEncoding(c) {
|
|
36999
|
+
const vary = c.res.headers.get("Vary");
|
|
37000
|
+
if (vary === "*" || vary && VARY_ACCEPT_ENCODING_RE.test(vary))
|
|
37001
|
+
return;
|
|
37002
|
+
c.header("Vary", vary ? `${vary}, Accept-Encoding` : "Accept-Encoding");
|
|
37003
|
+
}
|
|
37004
|
+
async function setProbedLength(c, threshold) {
|
|
37005
|
+
const res = c.res;
|
|
37006
|
+
const type = res.headers.get("Content-Type");
|
|
37007
|
+
if (res.headers.has("Content-Length") || res.headers.has("Content-Encoding") || res.headers.has("Transfer-Encoding") || c.req.method === "HEAD" || res.status === 204 || res.status === 206 || !res.body || !type || !COMPRESSIBLE_CONTENT_TYPE_REGEX.test(type))
|
|
37008
|
+
return;
|
|
37009
|
+
const reader = res.body.getReader();
|
|
37010
|
+
const head = [];
|
|
37011
|
+
let size = 0;
|
|
37012
|
+
while (size < threshold) {
|
|
37013
|
+
const { done, value } = await reader.read();
|
|
37014
|
+
if (done) {
|
|
37015
|
+
c.res = new Response(Buffer.concat(head, size), res);
|
|
37016
|
+
c.res.headers.set("Content-Length", String(size));
|
|
37017
|
+
return;
|
|
37018
|
+
}
|
|
37019
|
+
head.push(value);
|
|
37020
|
+
size += value.byteLength;
|
|
37021
|
+
}
|
|
37022
|
+
c.res = new Response(new ReadableStream({
|
|
37023
|
+
async pull(ctrl) {
|
|
37024
|
+
const chunk = head.shift();
|
|
37025
|
+
if (chunk)
|
|
37026
|
+
return ctrl.enqueue(chunk);
|
|
37027
|
+
const { done, value } = await reader.read();
|
|
37028
|
+
if (done)
|
|
37029
|
+
ctrl.close();
|
|
37030
|
+
else
|
|
37031
|
+
ctrl.enqueue(value);
|
|
37032
|
+
},
|
|
37033
|
+
cancel: (reason) => reader.cancel(reason)
|
|
37034
|
+
}), res);
|
|
37035
|
+
}
|
|
37036
|
+
var VARY_ACCEPT_ENCODING_RE;
|
|
37037
|
+
var init_compress = __esm({
|
|
37038
|
+
"../server/dist/middleware/compress.js"() {
|
|
37039
|
+
"use strict";
|
|
37040
|
+
VARY_ACCEPT_ENCODING_RE = /(?:^|,)\s*accept-encoding\s*(?:,|$)/i;
|
|
37041
|
+
}
|
|
37042
|
+
});
|
|
37043
|
+
|
|
36918
37044
|
// ../server/dist/listen-banner.js
|
|
36919
37045
|
function formatListenBanner(address, port, version) {
|
|
36920
37046
|
if (address === "::" || address === "0.0.0.0") {
|
|
@@ -36936,7 +37062,6 @@ var init_listen_banner = __esm({
|
|
|
36936
37062
|
var dist_exports = {};
|
|
36937
37063
|
import { Hono as Hono26 } from "hono";
|
|
36938
37064
|
import { cors } from "hono/cors";
|
|
36939
|
-
import { compress } from "hono/compress";
|
|
36940
37065
|
import { serve } from "@hono/node-server";
|
|
36941
37066
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
36942
37067
|
import { WebSocketServer as WebSocketServer4 } from "ws";
|
|
@@ -37128,6 +37253,7 @@ var init_dist2 = __esm({
|
|
|
37128
37253
|
init_registry4();
|
|
37129
37254
|
init_descriptors();
|
|
37130
37255
|
init_auth();
|
|
37256
|
+
init_compress();
|
|
37131
37257
|
init_logger();
|
|
37132
37258
|
init_listen_banner();
|
|
37133
37259
|
init_otel();
|
|
@@ -37137,7 +37263,7 @@ var init_dist2 = __esm({
|
|
|
37137
37263
|
init_git_ssh();
|
|
37138
37264
|
__dirname3 = path87.dirname(fileURLToPath3(import.meta.url));
|
|
37139
37265
|
PORT = config.server.port;
|
|
37140
|
-
HALO_VERSION = "1.6.
|
|
37266
|
+
HALO_VERSION = "1.6.7";
|
|
37141
37267
|
PROJECT_ROOT = findProjectRoot();
|
|
37142
37268
|
HALO_HOME4 = path87.join(homedir25(), ".halo");
|
|
37143
37269
|
GIT_SHA = readGitSha();
|
|
@@ -37181,7 +37307,7 @@ var init_dist2 = __esm({
|
|
|
37181
37307
|
maxAge: 86400,
|
|
37182
37308
|
credentials: allowlist.length > 0
|
|
37183
37309
|
}));
|
|
37184
|
-
app.use("/*",
|
|
37310
|
+
app.use("/*", compressResponses(1024));
|
|
37185
37311
|
jupyter = AGENTCORE ? null : createJupyterManager();
|
|
37186
37312
|
if (!AGENTCORE) {
|
|
37187
37313
|
app.use("/api/*", authMiddleware());
|
|
@@ -38747,7 +38873,7 @@ function installUserUnit(opts, run = runCommand) {
|
|
|
38747
38873
|
}
|
|
38748
38874
|
|
|
38749
38875
|
// src/index.ts
|
|
38750
|
-
var VERSION = "1.6.
|
|
38876
|
+
var VERSION = "1.6.7";
|
|
38751
38877
|
var HELP_TOP = `Usage: halo <command> [options]
|
|
38752
38878
|
|
|
38753
38879
|
Commands:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
id: mimo-token-plan-china
|
|
2
2
|
runtime: anthropic-messages
|
|
3
|
-
revision:
|
|
3
|
+
revision: 2026101401
|
|
4
4
|
displayName: Xiaomi MiMo
|
|
5
5
|
displayName_zh: 小米 MiMo
|
|
6
6
|
description: Xiaomi MiMo via its Anthropic-compatible gateway — Token Plan (China / Singapore / Europe clusters) or pay-as-you-go. Supports 1M context window.
|
|
@@ -28,9 +28,8 @@ secrets:
|
|
|
28
28
|
# gives `mimo-v2.6-pro` / `mimo-v2.6-flash` 1M context, 128K (131072) max
|
|
29
29
|
# output, full-modal understanding (image input on both — V2.5 only had it on
|
|
30
30
|
# the non-pro tier). `mimo-v2.6-pro-ultraspeed` is "contact us" only, not
|
|
31
|
-
# listed. `mimo-v2.5-pro` / `mimo-v2.5`
|
|
32
|
-
#
|
|
33
|
-
# capabilities until the cutoff; drop both after that date. Thinking shape unchanged from V2.5 — Anthropic-protocol
|
|
31
|
+
# listed. `mimo-v2.5-pro` / `mimo-v2.5` (officially retired 2026-10-21) were
|
|
32
|
+
# dropped from this list on 2026-10-14. Thinking shape unchanged from V2.5 — Anthropic-protocol
|
|
34
33
|
# `thinking.type` enabled/disabled, on by default; the docs say effort levels
|
|
35
34
|
# are not differentiated yet, so the adaptive presets below are a UI knob the
|
|
36
35
|
# gateway accepts, not a real intensity dial. Not live-probed (no valid key at
|
|
@@ -85,54 +84,3 @@ models:
|
|
|
85
84
|
- { value: medium, label: Medium }
|
|
86
85
|
- { value: high, label: High }
|
|
87
86
|
- { value: max, label: Max }
|
|
88
|
-
|
|
89
|
-
# --- deprecated 2026-10-21 10:00 CST (official notice) — remove after cutoff ---
|
|
90
|
-
- id: mimo-v2.5-pro
|
|
91
|
-
displayName: MiMo v2.5 Pro (deprecated 2026-10-21)
|
|
92
|
-
maxOutputTokens: 131072
|
|
93
|
-
contextWindow: 272000
|
|
94
|
-
maxContextWindow: 1000000
|
|
95
|
-
compressAt: 0.9
|
|
96
|
-
capabilities:
|
|
97
|
-
image: false
|
|
98
|
-
video: false
|
|
99
|
-
audio: false
|
|
100
|
-
promptCaching:
|
|
101
|
-
default: 1h
|
|
102
|
-
ttlPresets:
|
|
103
|
-
- { value: 5m, label: 5min }
|
|
104
|
-
- { value: 1h, label: 1hour }
|
|
105
|
-
thinking:
|
|
106
|
-
mode: adaptive
|
|
107
|
-
defaultEnabled: true
|
|
108
|
-
default: high
|
|
109
|
-
effortPresets:
|
|
110
|
-
- { value: low, label: Low }
|
|
111
|
-
- { value: medium, label: Medium }
|
|
112
|
-
- { value: high, label: High }
|
|
113
|
-
- { value: max, label: Max }
|
|
114
|
-
|
|
115
|
-
- id: mimo-v2.5
|
|
116
|
-
displayName: MiMo v2.5 (deprecated 2026-10-21)
|
|
117
|
-
maxOutputTokens: 131072
|
|
118
|
-
contextWindow: 272000
|
|
119
|
-
maxContextWindow: 1000000
|
|
120
|
-
compressAt: 0.9
|
|
121
|
-
capabilities:
|
|
122
|
-
image: true
|
|
123
|
-
video: false
|
|
124
|
-
audio: false
|
|
125
|
-
promptCaching:
|
|
126
|
-
default: 1h
|
|
127
|
-
ttlPresets:
|
|
128
|
-
- { value: 5m, label: 5min }
|
|
129
|
-
- { value: 1h, label: 1hour }
|
|
130
|
-
thinking:
|
|
131
|
-
mode: adaptive
|
|
132
|
-
defaultEnabled: true
|
|
133
|
-
default: high
|
|
134
|
-
effortPresets:
|
|
135
|
-
- { value: low, label: Low }
|
|
136
|
-
- { value: medium, label: Medium }
|
|
137
|
-
- { value: high, label: High }
|
|
138
|
-
- { value: max, label: Max }
|