@turmind/halo 1.2.1 → 1.3.1
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/admin-out/404.html +1 -1
- package/admin-out/_next/static/chunks/132-29179dca2ef093ce.js +1 -0
- package/admin-out/_next/static/chunks/{330.3b784cf0dcd69163.js → 170.14d53d7f32df020c.js} +1 -1
- package/admin-out/_next/static/chunks/187.9e822f6f0d366d9a.js +1 -0
- package/admin-out/_next/static/chunks/188.0cc6998eb0ed3519.js +1 -0
- package/admin-out/_next/static/chunks/{1d2d5650-810a3ca1ae20cab5.js → 1d2d5650-f5c38b9df5ca7466.js} +1 -1
- package/admin-out/_next/static/chunks/{304eda7d.b1ee7f523653e027.js → 304eda7d.662c3a1a7cd50701.js} +1 -1
- package/admin-out/_next/static/chunks/311.3cfaf322ec2a9170.js +1 -0
- package/admin-out/_next/static/chunks/401.c5e64882d4305a08.js +1 -0
- package/admin-out/_next/static/chunks/{443.d1b289781793f8de.js → 444.c191bedee971e484.js} +1 -1
- package/admin-out/_next/static/chunks/496.7997fc6558c88c22.js +1 -0
- package/admin-out/_next/static/chunks/530.03c0a6ec971d3528.js +1 -0
- package/admin-out/_next/static/chunks/540.a663b46781b2c309.js +1 -0
- package/admin-out/_next/static/chunks/556.10e21b9f7b45d412.js +1 -0
- package/admin-out/_next/static/chunks/813-9056b8ef0984bee6.js +1 -0
- package/admin-out/_next/static/chunks/{533.5f2d4bfe3d076095.js → 841.48a39cec2ef962f3.js} +1 -1
- package/admin-out/_next/static/chunks/891.cfe754015339e975.js +1 -0
- package/admin-out/_next/static/chunks/915.c6ee9ab9bee0ce9b.js +1 -0
- package/admin-out/_next/static/chunks/957-48c734b74e5437a8.js +1 -0
- package/admin-out/_next/static/chunks/968.1676baa9449dbd9d.js +1 -0
- package/admin-out/_next/static/chunks/app/_not-found/page-2ea4ee5eada04f69.js +1 -0
- package/admin-out/_next/static/chunks/app/layout-1853d21b0644f5a1.js +1 -0
- package/admin-out/_next/static/chunks/app/page-2ab109c9a25f867e.js +1 -0
- package/admin-out/_next/static/chunks/db534642.a148101e95a7d317.js +1 -0
- package/admin-out/_next/static/chunks/e3080c2c-fbf81b7cad547976.js +1 -0
- package/admin-out/_next/static/chunks/framework-67e5f0af3ea64f2b.js +1 -0
- package/admin-out/_next/static/chunks/main-6b28974253a3bc7c.js +1 -0
- package/admin-out/_next/static/chunks/main-app-3367dd99b8642c55.js +1 -0
- package/admin-out/_next/static/chunks/pages/_app-51a53534a0a4a898.js +1 -0
- package/admin-out/_next/static/chunks/pages/_error-dfd19566fa6f79ad.js +1 -0
- package/admin-out/_next/static/chunks/webpack-bf41edfde9cdefec.js +1 -0
- package/admin-out/_next/static/{AB2GFuqDjcxue9wBBq1zq → dpIz5l3hhYjL5gL4n3Huj}/_buildManifest.js +1 -1
- package/admin-out/index.html +1 -1
- package/admin-out/index.txt +13 -13
- package/bundled-docs/design/agentcore.md +9 -0
- package/bundled-docs/design/observability.md +214 -0
- package/bundled-docs/design/session.md +2 -0
- package/bundled-docs/design/storage.md +4 -0
- package/bundled-docs/dev/api.md +9 -0
- package/bundled-docs/dev/deploy.md +21 -1
- package/bundled-docs/dev/env.md +4 -0
- package/bundled-docs/guide/workspace.md +1 -0
- package/dist/index.js +433 -37
- package/package.json +12 -1
- package/admin-out/_next/static/chunks/133-e2df20fc6362a02b.js +0 -1
- package/admin-out/_next/static/chunks/200.dd28be896c7765de.js +0 -1
- package/admin-out/_next/static/chunks/220.e5a2532bda77e816.js +0 -1
- package/admin-out/_next/static/chunks/438-866a1a20e9efa78a.js +0 -1
- package/admin-out/_next/static/chunks/442.14eec8e615cc727b.js +0 -1
- package/admin-out/_next/static/chunks/515.b793a7455c5e8342.js +0 -1
- package/admin-out/_next/static/chunks/525.fe35ab8501121729.js +0 -1
- package/admin-out/_next/static/chunks/579.3ab0b2bb9244fc37.js +0 -1
- package/admin-out/_next/static/chunks/635-d7fd95bff8674116.js +0 -1
- package/admin-out/_next/static/chunks/65.3aac567c42cec99c.js +0 -1
- package/admin-out/_next/static/chunks/688.4e014ac6cfc7e152.js +0 -1
- package/admin-out/_next/static/chunks/868.0c95dfe283400a00.js +0 -1
- package/admin-out/_next/static/chunks/896.2069ab525ef81643.js +0 -1
- package/admin-out/_next/static/chunks/935.91ddb965b44041c9.js +0 -1
- package/admin-out/_next/static/chunks/app/_not-found/page-8e35399c8fd5016d.js +0 -1
- package/admin-out/_next/static/chunks/app/layout-e75df4cae90cf325.js +0 -1
- package/admin-out/_next/static/chunks/app/page-ca15438e8bddb403.js +0 -1
- package/admin-out/_next/static/chunks/db534642.09d3c650ff587175.js +0 -1
- package/admin-out/_next/static/chunks/fb4ec4d6-3fa44f69484f91b9.js +0 -1
- package/admin-out/_next/static/chunks/framework-c3031ae54a63fe16.js +0 -1
- package/admin-out/_next/static/chunks/main-14b48e3c2588354f.js +0 -1
- package/admin-out/_next/static/chunks/main-app-483b436873410ddc.js +0 -1
- package/admin-out/_next/static/chunks/pages/_app-b2fc6e5f920d1b1a.js +0 -1
- package/admin-out/_next/static/chunks/pages/_error-f3696a8f3d67d150.js +0 -1
- package/admin-out/_next/static/chunks/webpack-f80c9b8cca4447a3.js +0 -1
- /package/admin-out/_next/static/{AB2GFuqDjcxue9wBBq1zq → dpIz5l3hhYjL5gL4n3Huj}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -521,6 +521,13 @@ function generalSection() {
|
|
|
521
521
|
{ key: "sandbox.hidden_files", globalOnly: true, description: "Comma-separated files hidden by bwrap (Linux only)", description_zh: "\u901A\u8FC7 /dev/null bind \u9690\u85CF\u7684\u6587\u4EF6\uFF08\u9017\u53F7\u5206\u9694\uFF0C\u4EC5 Linux\uFF09", default: "~/.npmrc,~/.bash_history,~/.gitconfig,~/.git-credentials,~/.netrc,~/.halo/global/evo.db,~/.halo/global/evo.db-wal,~/.halo/global/evo.db-shm,~/.halo/global/cron.db,~/.halo/global/cron.db-wal,~/.halo/global/cron.db-shm,~/.halo/global/runs.db,~/.halo/global/runs.db-wal,~/.halo/global/runs.db-shm" },
|
|
522
522
|
// logging
|
|
523
523
|
{ key: "logging.level", type: "enum", options: ["debug", "info", "warn", "error"], globalOnly: true, description: "Log level", description_zh: "\u65E5\u5FD7\u7EA7\u522B", default: "warn" },
|
|
524
|
+
// observability (OpenTelemetry). Read once at boot by observability/otel.ts
|
|
525
|
+
// and mapped onto the standard OTEL_* env vars — hence "takes effect on
|
|
526
|
+
// restart" everywhere below.
|
|
527
|
+
{ key: "observability.endpoint", type: "string", globalOnly: true, description: "OTLP base URL of an OpenTelemetry collector (e.g. http://localhost:4318). Traces, metrics and logs are all exported there over OTLP http/protobuf \u2014 http:// or https://, on the collector's OTLP/HTTP port (4318 by default); gRPC (4317) is not supported. Empty = observability off. Takes effect on server restart.", description_zh: "OpenTelemetry collector \u7684 OTLP \u57FA\u7840\u5730\u5740\uFF08\u5982 http://localhost:4318\uFF09\uFF0Ctrace / metrics / \u65E5\u5FD7\u4E09\u7C7B\u4FE1\u53F7\u90FD\u4EE5 OTLP http/protobuf \u53D1\u5230\u8FD9\u91CC\u3002http:// \u6216 https:// \u5747\u53EF\uFF0C\u7AEF\u53E3\u987B\u662F collector \u7684 OTLP/HTTP \u7AEF\u53E3\uFF08\u9ED8\u8BA4 4318\uFF09\uFF0C\u4E0D\u652F\u6301 gRPC\uFF084317\uFF09\u3002\u7559\u7A7A = \u5173\u95ED\u3002\u91CD\u542F\u670D\u52A1\u540E\u751F\u6548\u3002", default: "" },
|
|
528
|
+
{ key: "observability.service_name", type: "string", globalOnly: true, description: "OTel resource service.name reported for this server. Takes effect on restart.", description_zh: "\u672C\u670D\u52A1\u5728 OTel \u4E2D\u4E0A\u62A5\u7684 service.name\u3002\u91CD\u542F\u540E\u751F\u6548\u3002", default: "halo" },
|
|
529
|
+
{ key: "observability.headers", type: "string", secret: true, globalOnly: true, description: "Extra headers for every OTLP request, comma-separated k=v (e.g. authorization=Bearer \u2026). Takes effect on restart.", description_zh: "\u9644\u52A0\u5230\u6BCF\u4E2A OTLP \u8BF7\u6C42\u7684 header\uFF0C\u9017\u53F7\u5206\u9694\u7684 k=v\uFF08\u5982 authorization=Bearer \u2026\uFF09\u3002\u91CD\u542F\u540E\u751F\u6548\u3002", default: "" },
|
|
530
|
+
{ key: "observability.capture_content", type: "boolean", globalOnly: true, description: "Put prompt / completion / tool argument and result text on spans (gen_ai.*.messages etc.). Off = only metadata (model, tokens, latency, tool names). Takes effect on restart.", description_zh: "\u662F\u5426\u628A\u63D0\u793A\u8BCD / \u6A21\u578B\u8F93\u51FA / \u5DE5\u5177\u53C2\u6570\u4E0E\u7ED3\u679C\u6B63\u6587\u5199\u5230 span \u4E0A\uFF08gen_ai.*.messages \u7B49\uFF09\u3002\u5173 = \u53EA\u4E0A\u62A5\u5143\u6570\u636E\uFF08\u6A21\u578B\u3001token\u3001\u8017\u65F6\u3001\u5DE5\u5177\u540D\uFF09\u3002\u91CD\u542F\u540E\u751F\u6548\u3002", default: "false" },
|
|
524
531
|
// self-evolution (see plans/self-evolution.md). All evo settings are
|
|
525
532
|
// global-only — the worker / ticker live in the server process and
|
|
526
533
|
// would have to reload mid-flight if a workspace could override them.
|
|
@@ -753,7 +760,7 @@ function loadYamlFile(filePath) {
|
|
|
753
760
|
}
|
|
754
761
|
}
|
|
755
762
|
function loadModelsRegistry() {
|
|
756
|
-
const
|
|
763
|
+
const providers2 = [];
|
|
757
764
|
const modelsDir = path5.join(HALO_GLOBAL_DIR, "models");
|
|
758
765
|
try {
|
|
759
766
|
const entries = fs5.readdirSync(modelsDir, { withFileTypes: true });
|
|
@@ -764,7 +771,7 @@ function loadModelsRegistry() {
|
|
|
764
771
|
const raw = fs5.readFileSync(path5.join(modelsDir, entry.name), "utf-8");
|
|
765
772
|
const parsed = YAML3.parse(raw);
|
|
766
773
|
if (parsed && typeof parsed === "object" && typeof parsed.id === "string") {
|
|
767
|
-
|
|
774
|
+
providers2.push(parsed);
|
|
768
775
|
} else {
|
|
769
776
|
console.log(`[config] Skipping ${entry.name}: missing provider id`);
|
|
770
777
|
}
|
|
@@ -774,7 +781,7 @@ function loadModelsRegistry() {
|
|
|
774
781
|
}
|
|
775
782
|
} catch {
|
|
776
783
|
}
|
|
777
|
-
return { providers };
|
|
784
|
+
return { providers: providers2 };
|
|
778
785
|
}
|
|
779
786
|
function modelsRegistry() {
|
|
780
787
|
if (!_modelsRegistryCache)
|
|
@@ -953,9 +960,9 @@ function reloadSandboxConfig() {
|
|
|
953
960
|
}
|
|
954
961
|
function findModelEntry(modelId) {
|
|
955
962
|
try {
|
|
956
|
-
const
|
|
957
|
-
if (
|
|
958
|
-
for (const provider of
|
|
963
|
+
const providers2 = modelsRegistry().providers;
|
|
964
|
+
if (providers2) {
|
|
965
|
+
for (const provider of providers2) {
|
|
959
966
|
for (const model of provider.models ?? []) {
|
|
960
967
|
if (model.id === modelId)
|
|
961
968
|
return model;
|
|
@@ -1203,6 +1210,14 @@ var init_config = __esm({
|
|
|
1203
1210
|
// 10MB
|
|
1204
1211
|
maxFiles: systemInt("HALO_LOG_MAX_FILES", "logging.max_files", 5)
|
|
1205
1212
|
},
|
|
1213
|
+
// OpenTelemetry export. Read once at boot (observability/otel.ts maps these
|
|
1214
|
+
// onto OTEL_* env vars) — changes take effect on restart.
|
|
1215
|
+
observability: {
|
|
1216
|
+
endpoint: settingsStr("general.observability.endpoint", ""),
|
|
1217
|
+
serviceName: settingsStr("general.observability.service_name", "halo"),
|
|
1218
|
+
headers: settingsStr("general.observability.headers", ""),
|
|
1219
|
+
captureContent: settingsBool("general.observability.capture_content", false)
|
|
1220
|
+
},
|
|
1206
1221
|
// Self-evolution (see plans/self-evolution.md). Getter-based so user edits
|
|
1207
1222
|
// in settings.yaml take effect on the next ticker run without restart.
|
|
1208
1223
|
evolution: {
|
|
@@ -1600,20 +1615,20 @@ Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
|
1600
1615
|
const resultTextFull = resultText.length > uiCap ? resultText.slice(0, uiCap) + `
|
|
1601
1616
|
|
|
1602
1617
|
[Content truncated: ${resultText.length} chars total, showing first ${uiCap}. Use file_read for the complete content.]` : resultText;
|
|
1603
|
-
const
|
|
1618
|
+
const cap2 = tu.name === "activate_skill" ? Infinity : config.limits.toolResultMax;
|
|
1604
1619
|
const truncationNote = (origLen) => `
|
|
1605
1620
|
|
|
1606
|
-
[Content truncated: ${origLen} chars total, showing first ${
|
|
1607
|
-
if (typeof resultContent === "string" && resultContent.length >
|
|
1621
|
+
[Content truncated: ${origLen} chars total, showing first ${cap2}. Re-run with narrower scope (e.g. grep / file_read with offset+limit) to see specific sections.]`;
|
|
1622
|
+
if (typeof resultContent === "string" && resultContent.length > cap2) {
|
|
1608
1623
|
const orig = resultContent.length;
|
|
1609
|
-
resultContent = resultContent.slice(0,
|
|
1624
|
+
resultContent = resultContent.slice(0, cap2) + truncationNote(orig);
|
|
1610
1625
|
resultText = resultContent;
|
|
1611
1626
|
} else if (Array.isArray(resultContent)) {
|
|
1612
1627
|
let mutated = false;
|
|
1613
1628
|
resultContent = resultContent.map((b) => {
|
|
1614
|
-
if (b.type === "text" && b.text.length >
|
|
1629
|
+
if (b.type === "text" && b.text.length > cap2) {
|
|
1615
1630
|
mutated = true;
|
|
1616
|
-
return { type: "text", text: b.text.slice(0,
|
|
1631
|
+
return { type: "text", text: b.text.slice(0, cap2) + truncationNote(b.text.length) };
|
|
1617
1632
|
}
|
|
1618
1633
|
return b;
|
|
1619
1634
|
});
|
|
@@ -3411,8 +3426,8 @@ ${report}`;
|
|
|
3411
3426
|
await deliver(host, gId, worker.id, header, report);
|
|
3412
3427
|
}
|
|
3413
3428
|
async function deliver(host, gId, workerId, header, report) {
|
|
3414
|
-
const
|
|
3415
|
-
const body = report.length >
|
|
3429
|
+
const cap2 = config.limits.autoReportMax;
|
|
3430
|
+
const body = report.length > cap2 ? report.slice(0, cap2) + `
|
|
3416
3431
|
|
|
3417
3432
|
[Report truncated: ${report.length} chars total. Use get_session_output("${workerId}") for the full text.]` : report;
|
|
3418
3433
|
await host.querySession(gId, workerId, `${header}
|
|
@@ -5387,8 +5402,8 @@ async function deliverRelayReport(host, session) {
|
|
|
5387
5402
|
|
|
5388
5403
|
${report}`;
|
|
5389
5404
|
}
|
|
5390
|
-
const
|
|
5391
|
-
const body = report.length >
|
|
5405
|
+
const cap2 = config.limits.autoReportMax;
|
|
5406
|
+
const body = report.length > cap2 ? report.slice(0, cap2) + `
|
|
5392
5407
|
|
|
5393
5408
|
[Report truncated: ${report.length} chars total. Use relay_read("${host.workspaceRoot}", "${session.id}") for the full text.]` : report;
|
|
5394
5409
|
const header = `[Relay report \xB7 workspace ${host.workspaceRoot} \xB7 session ${session.id}]`;
|
|
@@ -11273,6 +11288,348 @@ var init_session_state_store = __esm({
|
|
|
11273
11288
|
}
|
|
11274
11289
|
});
|
|
11275
11290
|
|
|
11291
|
+
// ../server/dist/observability/otel-sdk.js
|
|
11292
|
+
var otel_sdk_exports = {};
|
|
11293
|
+
__export(otel_sdk_exports, {
|
|
11294
|
+
registerSdk: () => registerSdk
|
|
11295
|
+
});
|
|
11296
|
+
import { metrics } from "@opentelemetry/api";
|
|
11297
|
+
import { logs } from "@opentelemetry/api-logs";
|
|
11298
|
+
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
11299
|
+
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
11300
|
+
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
11301
|
+
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
11302
|
+
import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
|
11303
|
+
import { LoggerProvider, BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
|
11304
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
|
11305
|
+
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-proto";
|
|
11306
|
+
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-proto";
|
|
11307
|
+
function registerSdk(serviceName, serviceVersion) {
|
|
11308
|
+
const resource = resourceFromAttributes({
|
|
11309
|
+
[ATTR_SERVICE_NAME]: serviceName,
|
|
11310
|
+
[ATTR_SERVICE_VERSION]: serviceVersion
|
|
11311
|
+
});
|
|
11312
|
+
const tracerProvider = new NodeTracerProvider({
|
|
11313
|
+
resource,
|
|
11314
|
+
spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter())]
|
|
11315
|
+
});
|
|
11316
|
+
tracerProvider.register();
|
|
11317
|
+
const meterProvider = new MeterProvider({
|
|
11318
|
+
resource,
|
|
11319
|
+
readers: [new PeriodicExportingMetricReader({ exporter: new OTLPMetricExporter(), exportIntervalMillis: 15e3 })]
|
|
11320
|
+
});
|
|
11321
|
+
metrics.setGlobalMeterProvider(meterProvider);
|
|
11322
|
+
const loggerProvider = new LoggerProvider({
|
|
11323
|
+
resource,
|
|
11324
|
+
processors: [new BatchLogRecordProcessor({ exporter: new OTLPLogExporter() })]
|
|
11325
|
+
});
|
|
11326
|
+
logs.setGlobalLoggerProvider(loggerProvider);
|
|
11327
|
+
return [tracerProvider, meterProvider, loggerProvider];
|
|
11328
|
+
}
|
|
11329
|
+
var init_otel_sdk = __esm({
|
|
11330
|
+
"../server/dist/observability/otel-sdk.js"() {
|
|
11331
|
+
"use strict";
|
|
11332
|
+
}
|
|
11333
|
+
});
|
|
11334
|
+
|
|
11335
|
+
// ../server/dist/observability/otel.js
|
|
11336
|
+
import { trace, metrics as metrics2, ProxyTracerProvider } from "@opentelemetry/api";
|
|
11337
|
+
import { logs as logs2 } from "@opentelemetry/api-logs";
|
|
11338
|
+
function externalSdkRegistered() {
|
|
11339
|
+
const provider = trace.getTracerProvider();
|
|
11340
|
+
if (!(provider instanceof ProxyTracerProvider))
|
|
11341
|
+
return true;
|
|
11342
|
+
return provider.getDelegate() !== new ProxyTracerProvider().getDelegate();
|
|
11343
|
+
}
|
|
11344
|
+
async function initObservability() {
|
|
11345
|
+
const { endpoint, serviceName, headers } = config.observability;
|
|
11346
|
+
if (endpoint)
|
|
11347
|
+
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ??= endpoint;
|
|
11348
|
+
if (headers)
|
|
11349
|
+
process.env.OTEL_EXPORTER_OTLP_HEADERS ??= headers;
|
|
11350
|
+
process.env.OTEL_SERVICE_NAME ??= serviceName;
|
|
11351
|
+
process.env.OTEL_EXPORTER_OTLP_PROTOCOL ??= "http/protobuf";
|
|
11352
|
+
if (!process.env.OTEL_EXPORTER_OTLP_ENDPOINT)
|
|
11353
|
+
return false;
|
|
11354
|
+
enabled = true;
|
|
11355
|
+
if (externalSdkRegistered()) {
|
|
11356
|
+
console.log("[Observability] external OpenTelemetry SDK already registered \u2014 using it, skipping built-in exporters");
|
|
11357
|
+
return true;
|
|
11358
|
+
}
|
|
11359
|
+
const { registerSdk: registerSdk2 } = await Promise.resolve().then(() => (init_otel_sdk(), otel_sdk_exports));
|
|
11360
|
+
providers = registerSdk2(process.env.OTEL_SERVICE_NAME, "1.3.1");
|
|
11361
|
+
console.log(`[Observability] OTLP export \u2192 ${process.env.OTEL_EXPORTER_OTLP_ENDPOINT} (service.name=${process.env.OTEL_SERVICE_NAME}, capture_content=${config.observability.captureContent})`);
|
|
11362
|
+
return true;
|
|
11363
|
+
}
|
|
11364
|
+
async function shutdownObservability() {
|
|
11365
|
+
if (providers.length === 0)
|
|
11366
|
+
return;
|
|
11367
|
+
const all = Promise.all(providers.map(async (p) => {
|
|
11368
|
+
try {
|
|
11369
|
+
await p.forceFlush();
|
|
11370
|
+
} catch {
|
|
11371
|
+
}
|
|
11372
|
+
try {
|
|
11373
|
+
await p.shutdown();
|
|
11374
|
+
} catch {
|
|
11375
|
+
}
|
|
11376
|
+
}));
|
|
11377
|
+
await Promise.race([all, new Promise((resolve2) => setTimeout(resolve2, 3e3))]);
|
|
11378
|
+
}
|
|
11379
|
+
var SCOPE, enabled, tracer, getMeter, otelLogger, captureContent, providers;
|
|
11380
|
+
var init_otel = __esm({
|
|
11381
|
+
"../server/dist/observability/otel.js"() {
|
|
11382
|
+
"use strict";
|
|
11383
|
+
init_config();
|
|
11384
|
+
SCOPE = "opentelemetry.instrumentation.halo";
|
|
11385
|
+
enabled = false;
|
|
11386
|
+
tracer = trace.getTracer(SCOPE, "1");
|
|
11387
|
+
getMeter = () => metrics2.getMeter(SCOPE, "1");
|
|
11388
|
+
otelLogger = logs2.getLogger(SCOPE, "1");
|
|
11389
|
+
captureContent = () => config.observability.captureContent;
|
|
11390
|
+
providers = [];
|
|
11391
|
+
}
|
|
11392
|
+
});
|
|
11393
|
+
|
|
11394
|
+
// ../server/dist/observability/genai-spans.js
|
|
11395
|
+
import { context, trace as trace2, SpanStatusCode } from "@opentelemetry/api";
|
|
11396
|
+
function instruments() {
|
|
11397
|
+
const meter = getMeter();
|
|
11398
|
+
return {
|
|
11399
|
+
tokenUsage: meter.createHistogram("gen_ai.client.token.usage", { unit: "{token}", description: "Tokens per model call, split by gen_ai.token.type" }),
|
|
11400
|
+
operationDuration: meter.createHistogram("gen_ai.client.operation.duration", { unit: "s", description: "Model call latency" }),
|
|
11401
|
+
toolDuration: meter.createHistogram("halo.tool.duration", { unit: "s", description: "Tool execution latency" }),
|
|
11402
|
+
turnDuration: meter.createHistogram("halo.turn.duration", { unit: "s", description: "Agent turn latency (invoke_agent)" }),
|
|
11403
|
+
modelRetries: meter.createCounter("halo.model.retries", { description: "Model-call retries by kind" })
|
|
11404
|
+
};
|
|
11405
|
+
}
|
|
11406
|
+
function cap(text6, max) {
|
|
11407
|
+
return text6.length > max ? `${text6.slice(0, max)}\u2026[truncated ${text6.length - max} chars]` : text6;
|
|
11408
|
+
}
|
|
11409
|
+
function imagePlaceholder(source) {
|
|
11410
|
+
return `[image mimeType=${source.media_type} size=${Math.floor(source.data.length * 3 / 4)}B]`;
|
|
11411
|
+
}
|
|
11412
|
+
function inputText(message) {
|
|
11413
|
+
if (typeof message === "string")
|
|
11414
|
+
return message;
|
|
11415
|
+
return message.map((b) => {
|
|
11416
|
+
if (b.type === "text")
|
|
11417
|
+
return b.text;
|
|
11418
|
+
if (b.type === "image")
|
|
11419
|
+
return imagePlaceholder(b.source);
|
|
11420
|
+
return "";
|
|
11421
|
+
}).filter(Boolean).join("\n");
|
|
11422
|
+
}
|
|
11423
|
+
function toSemconvMessage(m) {
|
|
11424
|
+
if (typeof m.content === "string")
|
|
11425
|
+
return { role: m.role, parts: [{ type: "text", content: cap(m.content, PART_CAP) }] };
|
|
11426
|
+
const parts = [];
|
|
11427
|
+
let onlyToolResults = m.content.length > 0;
|
|
11428
|
+
for (const b of m.content) {
|
|
11429
|
+
if (b.type !== "tool_result")
|
|
11430
|
+
onlyToolResults = false;
|
|
11431
|
+
switch (b.type) {
|
|
11432
|
+
case "text":
|
|
11433
|
+
parts.push({ type: "text", content: cap(b.text, PART_CAP) });
|
|
11434
|
+
break;
|
|
11435
|
+
case "image":
|
|
11436
|
+
parts.push({ type: "text", content: imagePlaceholder(b.source) });
|
|
11437
|
+
break;
|
|
11438
|
+
case "tool_use":
|
|
11439
|
+
parts.push({ type: "tool_call", id: b.id, name: b.name, arguments: b.input });
|
|
11440
|
+
break;
|
|
11441
|
+
case "tool_result": {
|
|
11442
|
+
const text6 = typeof b.content === "string" ? b.content : b.content.map((c) => c.type === "text" ? c.text : imagePlaceholder(c.source)).join("\n");
|
|
11443
|
+
parts.push({ type: "tool_call_response", id: b.tool_use_id, response: cap(text6, PART_CAP) });
|
|
11444
|
+
break;
|
|
11445
|
+
}
|
|
11446
|
+
}
|
|
11447
|
+
}
|
|
11448
|
+
return { role: onlyToolResults ? "tool" : m.role, parts };
|
|
11449
|
+
}
|
|
11450
|
+
function messagesAttr(messages2) {
|
|
11451
|
+
const parts = messages2.map((m) => JSON.stringify(toSemconvMessage(m)));
|
|
11452
|
+
let total = parts.reduce((n, p) => n + p.length + 1, 1);
|
|
11453
|
+
let dropped = 0;
|
|
11454
|
+
while (total > ATTR_CAP - 100 && dropped < parts.length)
|
|
11455
|
+
total -= parts[dropped++].length + 1;
|
|
11456
|
+
if (dropped === 0)
|
|
11457
|
+
return `[${parts.join(",")}]`;
|
|
11458
|
+
const marker = JSON.stringify({ role: "user", parts: [{ type: "text", content: `[${dropped} earlier messages omitted]` }] });
|
|
11459
|
+
return `[${[marker, ...parts.slice(dropped)].join(",")}]`;
|
|
11460
|
+
}
|
|
11461
|
+
function beginTurn(session, message) {
|
|
11462
|
+
if (!enabled)
|
|
11463
|
+
return;
|
|
11464
|
+
const startMs = Date.now();
|
|
11465
|
+
const span = tracer.startSpan(`invoke_agent ${session.agentName}`, {
|
|
11466
|
+
startTime: startMs,
|
|
11467
|
+
attributes: {
|
|
11468
|
+
"gen_ai.operation.name": "invoke_agent",
|
|
11469
|
+
"gen_ai.system": SYSTEM,
|
|
11470
|
+
"gen_ai.agent.name": session.agentName,
|
|
11471
|
+
"gen_ai.request.model": session.currentModelId,
|
|
11472
|
+
"session.id": session.id
|
|
11473
|
+
}
|
|
11474
|
+
});
|
|
11475
|
+
if (captureContent())
|
|
11476
|
+
span.setAttribute("gen_ai.system_instructions", cap(session.systemPrompt, SYSTEM_PROMPT_CAP));
|
|
11477
|
+
turns.set(session.id, {
|
|
11478
|
+
span,
|
|
11479
|
+
ctx: trace2.setSpan(context.active(), span),
|
|
11480
|
+
startMs,
|
|
11481
|
+
input: inputText(message),
|
|
11482
|
+
finalText: "",
|
|
11483
|
+
allText: "",
|
|
11484
|
+
cycleToolCalls: [],
|
|
11485
|
+
pendingTools: /* @__PURE__ */ new Map(),
|
|
11486
|
+
// The user message is pushed by agent.run() after beginTurn, so the first
|
|
11487
|
+
// chat span's delta starts exactly at it.
|
|
11488
|
+
messageCursor: session.agent.messages.length
|
|
11489
|
+
});
|
|
11490
|
+
}
|
|
11491
|
+
function onAgentEvent(session, event) {
|
|
11492
|
+
if (!enabled)
|
|
11493
|
+
return;
|
|
11494
|
+
const turn = turns.get(session.id);
|
|
11495
|
+
if (!turn)
|
|
11496
|
+
return;
|
|
11497
|
+
switch (event.type) {
|
|
11498
|
+
case "text":
|
|
11499
|
+
turn.allText += event.text ?? "";
|
|
11500
|
+
if (event.final)
|
|
11501
|
+
turn.finalText += event.text ?? "";
|
|
11502
|
+
return;
|
|
11503
|
+
case "tool_call":
|
|
11504
|
+
turn.cycleToolCalls.push({ id: event.toolUseId ?? "", name: event.toolName ?? "", args: event.toolInput });
|
|
11505
|
+
return;
|
|
11506
|
+
case "usage":
|
|
11507
|
+
recordModelCall(session, turn, event);
|
|
11508
|
+
return;
|
|
11509
|
+
case "tool_result":
|
|
11510
|
+
recordToolCall(session, turn, event);
|
|
11511
|
+
return;
|
|
11512
|
+
default:
|
|
11513
|
+
return;
|
|
11514
|
+
}
|
|
11515
|
+
}
|
|
11516
|
+
function recordModelCall(session, turn, event) {
|
|
11517
|
+
const now = Date.now();
|
|
11518
|
+
const durationMs = event.durationMs ?? 0;
|
|
11519
|
+
const model = session.currentModelId;
|
|
11520
|
+
const span = tracer.startSpan(`chat ${model}`, {
|
|
11521
|
+
startTime: now - durationMs,
|
|
11522
|
+
attributes: {
|
|
11523
|
+
"gen_ai.operation.name": "chat",
|
|
11524
|
+
"gen_ai.system": SYSTEM,
|
|
11525
|
+
"gen_ai.request.model": model,
|
|
11526
|
+
"gen_ai.usage.input_tokens": event.usage?.inputTokens ?? 0,
|
|
11527
|
+
"gen_ai.usage.output_tokens": event.usage?.outputTokens ?? 0,
|
|
11528
|
+
"gen_ai.response.finish_reasons": [turn.cycleToolCalls.length > 0 ? "tool_use" : "end_turn"],
|
|
11529
|
+
"session.id": session.id
|
|
11530
|
+
}
|
|
11531
|
+
}, turn.ctx);
|
|
11532
|
+
if (captureContent()) {
|
|
11533
|
+
const messages2 = session.agent.messages;
|
|
11534
|
+
const last = messages2[messages2.length - 1];
|
|
11535
|
+
const hasAssistantTail = last?.role === "assistant";
|
|
11536
|
+
const inputEnd = hasAssistantTail ? messages2.length - 1 : messages2.length;
|
|
11537
|
+
span.setAttribute("gen_ai.input.messages", messagesAttr(messages2.slice(turn.messageCursor, inputEnd)));
|
|
11538
|
+
if (hasAssistantTail)
|
|
11539
|
+
span.setAttribute("gen_ai.output.messages", messagesAttr([last]));
|
|
11540
|
+
turn.messageCursor = messages2.length;
|
|
11541
|
+
}
|
|
11542
|
+
span.end(now);
|
|
11543
|
+
const chatCtx = trace2.setSpan(turn.ctx, span);
|
|
11544
|
+
for (const tc of turn.cycleToolCalls) {
|
|
11545
|
+
turn.pendingTools.set(tc.id, { name: tc.name, args: tc.args, parentCtx: chatCtx, startMs: now });
|
|
11546
|
+
}
|
|
11547
|
+
turn.cycleToolCalls = [];
|
|
11548
|
+
const modelAttr = { "gen_ai.request.model": model };
|
|
11549
|
+
const m = metricsFor();
|
|
11550
|
+
m.tokenUsage.record(event.usage?.inputTokens ?? 0, { ...modelAttr, "gen_ai.token.type": "input" });
|
|
11551
|
+
m.tokenUsage.record(event.usage?.outputTokens ?? 0, { ...modelAttr, "gen_ai.token.type": "output" });
|
|
11552
|
+
m.operationDuration.record(durationMs / 1e3, { ...modelAttr, "gen_ai.operation.name": "chat" });
|
|
11553
|
+
}
|
|
11554
|
+
function recordToolCall(session, turn, event) {
|
|
11555
|
+
const id = event.toolUseId ?? "";
|
|
11556
|
+
const pending = turn.pendingTools.get(id);
|
|
11557
|
+
if (!pending)
|
|
11558
|
+
return;
|
|
11559
|
+
turn.pendingTools.delete(id);
|
|
11560
|
+
const now = Date.now();
|
|
11561
|
+
const durationMs = event.durationMs ?? 0;
|
|
11562
|
+
const span = tracer.startSpan(`execute_tool ${pending.name}`, {
|
|
11563
|
+
startTime: now - durationMs,
|
|
11564
|
+
attributes: {
|
|
11565
|
+
"gen_ai.operation.name": "execute_tool",
|
|
11566
|
+
"gen_ai.system": SYSTEM,
|
|
11567
|
+
"gen_ai.tool.name": pending.name,
|
|
11568
|
+
"gen_ai.tool.call.id": id,
|
|
11569
|
+
"session.id": session.id
|
|
11570
|
+
}
|
|
11571
|
+
}, pending.parentCtx);
|
|
11572
|
+
if (captureContent()) {
|
|
11573
|
+
span.setAttribute("gen_ai.tool.call.arguments", cap(JSON.stringify(pending.args ?? null), ATTR_CAP));
|
|
11574
|
+
span.setAttribute("gen_ai.tool.call.result", cap(event.toolResult || "[no output]", ATTR_CAP));
|
|
11575
|
+
}
|
|
11576
|
+
span.end(now);
|
|
11577
|
+
metricsFor().toolDuration.record(durationMs / 1e3, { "gen_ai.tool.name": pending.name });
|
|
11578
|
+
}
|
|
11579
|
+
function endTurn(session, outcome) {
|
|
11580
|
+
if (!enabled)
|
|
11581
|
+
return;
|
|
11582
|
+
const turn = turns.get(session.id);
|
|
11583
|
+
if (!turn)
|
|
11584
|
+
return;
|
|
11585
|
+
turns.delete(session.id);
|
|
11586
|
+
const now = Date.now();
|
|
11587
|
+
for (const [id, pending] of turn.pendingTools) {
|
|
11588
|
+
const orphan = tracer.startSpan(`execute_tool ${pending.name}`, {
|
|
11589
|
+
startTime: pending.startMs,
|
|
11590
|
+
attributes: {
|
|
11591
|
+
"gen_ai.operation.name": "execute_tool",
|
|
11592
|
+
"gen_ai.system": SYSTEM,
|
|
11593
|
+
"gen_ai.tool.name": pending.name,
|
|
11594
|
+
"gen_ai.tool.call.id": id,
|
|
11595
|
+
"session.id": session.id,
|
|
11596
|
+
"halo.tool.orphaned": true
|
|
11597
|
+
}
|
|
11598
|
+
}, pending.parentCtx);
|
|
11599
|
+
orphan.end(now);
|
|
11600
|
+
}
|
|
11601
|
+
if (captureContent()) {
|
|
11602
|
+
turn.span.setAttribute("gen_ai.task.input", cap(turn.input, ATTR_CAP));
|
|
11603
|
+
turn.span.setAttribute("gen_ai.task.output", cap(turn.finalText || turn.allText, ATTR_CAP));
|
|
11604
|
+
}
|
|
11605
|
+
if (outcome.error) {
|
|
11606
|
+
turn.span.setStatus({ code: SpanStatusCode.ERROR, message: outcome.error });
|
|
11607
|
+
const errName = outcome.error.match(/^([A-Za-z]\w*(?:Exception|Error))\b/)?.[1];
|
|
11608
|
+
turn.span.setAttribute("error.type", errName ?? "_OTHER");
|
|
11609
|
+
}
|
|
11610
|
+
turn.span.end(now);
|
|
11611
|
+
metricsFor().turnDuration.record((now - turn.startMs) / 1e3, { "gen_ai.agent.name": session.agentName, outcome: outcome.error ? "error" : "ok" });
|
|
11612
|
+
}
|
|
11613
|
+
function recordRetry(kind) {
|
|
11614
|
+
if (!enabled)
|
|
11615
|
+
return;
|
|
11616
|
+
metricsFor().modelRetries.add(1, { kind });
|
|
11617
|
+
}
|
|
11618
|
+
var turns, SYSTEM, PART_CAP, SYSTEM_PROMPT_CAP, ATTR_CAP, cachedInstruments, metricsFor;
|
|
11619
|
+
var init_genai_spans = __esm({
|
|
11620
|
+
"../server/dist/observability/genai-spans.js"() {
|
|
11621
|
+
"use strict";
|
|
11622
|
+
init_otel();
|
|
11623
|
+
turns = /* @__PURE__ */ new Map();
|
|
11624
|
+
SYSTEM = "halo";
|
|
11625
|
+
PART_CAP = 2 * 1024;
|
|
11626
|
+
SYSTEM_PROMPT_CAP = 8 * 1024;
|
|
11627
|
+
ATTR_CAP = 32 * 1024;
|
|
11628
|
+
cachedInstruments = null;
|
|
11629
|
+
metricsFor = () => cachedInstruments ??= instruments();
|
|
11630
|
+
}
|
|
11631
|
+
});
|
|
11632
|
+
|
|
11276
11633
|
// ../server/dist/agents/session-manager.js
|
|
11277
11634
|
import fsSync7 from "node:fs";
|
|
11278
11635
|
import path30 from "node:path";
|
|
@@ -11403,6 +11760,7 @@ var init_session_manager = __esm({
|
|
|
11403
11760
|
init_session_agent_builder();
|
|
11404
11761
|
init_session_skill_commands();
|
|
11405
11762
|
init_session_state_store();
|
|
11763
|
+
init_genai_spans();
|
|
11406
11764
|
SessionManager = class _SessionManager {
|
|
11407
11765
|
// The fields below are marked `internal:` because they're consumed by
|
|
11408
11766
|
// helpers in this directory (session-tools.ts). Treat them as
|
|
@@ -12091,6 +12449,7 @@ var init_session_manager = __esm({
|
|
|
12091
12449
|
const i = message.findIndex((b) => b.type === "text");
|
|
12092
12450
|
message = i === -1 ? [{ type: "text", text: stamp }, ...message] : message.map((b, j) => j === i && b.type === "text" ? { type: "text", text: stamped(b.text) } : b);
|
|
12093
12451
|
}
|
|
12452
|
+
beginTurn(session, message);
|
|
12094
12453
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
12095
12454
|
session.abortController = new AbortController();
|
|
12096
12455
|
const signal = session.abortController.signal;
|
|
@@ -12110,6 +12469,7 @@ var init_session_manager = __esm({
|
|
|
12110
12469
|
if (event.type === "text") {
|
|
12111
12470
|
resultText += event.text ?? "";
|
|
12112
12471
|
}
|
|
12472
|
+
onAgentEvent(session, event);
|
|
12113
12473
|
this.processSessionEvent(session, event);
|
|
12114
12474
|
if (session.interruptRequested && event.type === "tool_result") {
|
|
12115
12475
|
console.debug(`[SessionManager] Graceful interrupt after tool result in ${session.id}`);
|
|
@@ -12142,8 +12502,10 @@ var init_session_manager = __esm({
|
|
|
12142
12502
|
if (result.compacted) {
|
|
12143
12503
|
session.agent.messages = result.messages;
|
|
12144
12504
|
this.emitEvent(session.id, { type: "system", text: `Session ${session.agentId} context compacted (${result.messages.length} messages remaining, local fallback)` });
|
|
12145
|
-
if (attempt + 1 < maxRetries)
|
|
12505
|
+
if (attempt + 1 < maxRetries) {
|
|
12506
|
+
recordRetry("context_overflow");
|
|
12146
12507
|
continue;
|
|
12508
|
+
}
|
|
12147
12509
|
}
|
|
12148
12510
|
}
|
|
12149
12511
|
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");
|
|
@@ -12160,6 +12522,7 @@ var init_session_manager = __esm({
|
|
|
12160
12522
|
const jitter = Math.random() * 1e3;
|
|
12161
12523
|
const delay = Math.min(baseDelay + jitter, 6e4);
|
|
12162
12524
|
console.debug(`[SessionManager] Session ${session.id} rate limited, retrying in ${Math.round(delay)}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
12525
|
+
recordRetry("throttle");
|
|
12163
12526
|
this.emitEvent(session.id, { type: "system", text: `Session ${session.agentId} rate limited, retrying in ${Math.round(delay / 1e3)}s...` });
|
|
12164
12527
|
await sleep(delay);
|
|
12165
12528
|
continue;
|
|
@@ -12171,6 +12534,7 @@ var init_session_manager = __esm({
|
|
|
12171
12534
|
const jitter = Math.random() * 1e3;
|
|
12172
12535
|
const delay = Math.min(baseDelay + jitter, 6e4);
|
|
12173
12536
|
console.debug(`[SessionManager] Session ${session.id} transient server error ${errName || httpStatus}, retrying in ${Math.round(delay)}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
12537
|
+
recordRetry("server_error");
|
|
12174
12538
|
this.emitEvent(session.id, { type: "system", text: `Session ${session.agentId} hit a transient server error, retrying in ${Math.round(delay / 1e3)}s...` });
|
|
12175
12539
|
await sleep(delay);
|
|
12176
12540
|
continue;
|
|
@@ -12180,6 +12544,7 @@ var init_session_manager = __esm({
|
|
|
12180
12544
|
if (attempt + 1 < maxRetries) {
|
|
12181
12545
|
const delay = 1e3 * Math.pow(2, attempt) + Math.random() * 500;
|
|
12182
12546
|
console.warn(`[SessionManager] Session ${session.id} transient network error, retrying in ${Math.round(delay)}ms (attempt ${attempt + 1}/${maxRetries}): ${msg}`);
|
|
12547
|
+
recordRetry("network");
|
|
12183
12548
|
this.emitEvent(session.id, { type: "system", text: `Network hiccup, retrying in ${Math.round(delay / 1e3)}s...` });
|
|
12184
12549
|
await sleep(delay);
|
|
12185
12550
|
continue;
|
|
@@ -12189,6 +12554,7 @@ var init_session_manager = __esm({
|
|
|
12189
12554
|
if (attempt + 1 < maxRetries) {
|
|
12190
12555
|
const delay = 1e3 * Math.pow(2, attempt) + Math.random() * 500;
|
|
12191
12556
|
console.debug(`[SessionManager] Session ${session.id} Mantle empty response, retrying in ${Math.round(delay)}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
12557
|
+
recordRetry("empty_response");
|
|
12192
12558
|
await sleep(delay);
|
|
12193
12559
|
continue;
|
|
12194
12560
|
}
|
|
@@ -12197,6 +12563,7 @@ var init_session_manager = __esm({
|
|
|
12197
12563
|
console.debug(`[SessionManager] Session ${session.id} corrupted conversation (attempt ${attempt + 1}/${maxRetries})`);
|
|
12198
12564
|
session.agent.messages = repairConversationMessages(session.agent.messages, `[Session:${session.id}]`);
|
|
12199
12565
|
if (attempt + 1 < maxRetries) {
|
|
12566
|
+
recordRetry("corrupted");
|
|
12200
12567
|
this.emitEvent(session.id, { type: "system", text: "Repairing conversation state..." });
|
|
12201
12568
|
continue;
|
|
12202
12569
|
}
|
|
@@ -12211,6 +12578,7 @@ var init_session_manager = __esm({
|
|
|
12211
12578
|
this.saveAgentState(session);
|
|
12212
12579
|
console.warn(`[SessionManager] Session ${session.id} multimodal 4xx \u2014 replaced ${replaced} image block(s) with placeholders (attempt ${attempt + 1}/${maxRetries})`);
|
|
12213
12580
|
if (attempt + 1 < maxRetries) {
|
|
12581
|
+
recordRetry("multimodal_4xx");
|
|
12214
12582
|
this.emitEvent(session.id, { type: "system", text: `Model rejected image data \u2014 removed ${replaced} image(s) from history, retrying...` });
|
|
12215
12583
|
continue;
|
|
12216
12584
|
}
|
|
@@ -12225,6 +12593,7 @@ var init_session_manager = __esm({
|
|
|
12225
12593
|
}
|
|
12226
12594
|
}
|
|
12227
12595
|
session.abortController = null;
|
|
12596
|
+
endTurn(session, { error: session.turnError ?? void 0 });
|
|
12228
12597
|
session.agent.messages = repairConversationMessages(session.agent.messages, `[Session:${session.id}]`);
|
|
12229
12598
|
this.db.update(agentSessions).set({ updatedAt: Date.now() }).where(eq9(agentSessions.id, session.id)).run();
|
|
12230
12599
|
await this.maybeAutoCompact(session);
|
|
@@ -13320,12 +13689,12 @@ ${result.summary}`, agentName: "user", report: true, taskId });
|
|
|
13320
13689
|
* caller wants and also eats the trailing JSON fields. Self-cap here,
|
|
13321
13690
|
* tail-kept, under the generic cap so the envelope always survives. */
|
|
13322
13691
|
truncateOutputTail(output) {
|
|
13323
|
-
const
|
|
13324
|
-
if (output.length <=
|
|
13692
|
+
const cap2 = config.limits.toolResultMax - 500;
|
|
13693
|
+
if (output.length <= cap2)
|
|
13325
13694
|
return output;
|
|
13326
|
-
return `[Output truncated: ${output.length} chars total, showing LAST ${
|
|
13695
|
+
return `[Output truncated: ${output.length} chars total, showing LAST ${cap2}. Earlier text omitted.]
|
|
13327
13696
|
|
|
13328
|
-
` + output.slice(-
|
|
13697
|
+
` + output.slice(-cap2);
|
|
13329
13698
|
}
|
|
13330
13699
|
getSessionOutput(sessionId) {
|
|
13331
13700
|
const session = this.sessions.get(sessionId);
|
|
@@ -13495,6 +13864,7 @@ var init_session_manager_registry = __esm({
|
|
|
13495
13864
|
import fs25 from "node:fs";
|
|
13496
13865
|
import path31 from "node:path";
|
|
13497
13866
|
import { homedir as homedir13 } from "node:os";
|
|
13867
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
13498
13868
|
function ensureDir() {
|
|
13499
13869
|
fs25.mkdirSync(logDir, { recursive: true });
|
|
13500
13870
|
}
|
|
@@ -13556,6 +13926,18 @@ function shouldLog(level) {
|
|
|
13556
13926
|
const threshold = LEVEL_ORDER[config.logging.level] ?? LEVEL_ORDER.warn;
|
|
13557
13927
|
return LEVEL_ORDER[level] >= threshold;
|
|
13558
13928
|
}
|
|
13929
|
+
function emitOtelLog(level, args) {
|
|
13930
|
+
if (!enabled)
|
|
13931
|
+
return;
|
|
13932
|
+
const body = formatArgs(args);
|
|
13933
|
+
const module = body.match(/^\[([^\]]+)\]/)?.[1];
|
|
13934
|
+
otelLogger.emit({
|
|
13935
|
+
severityNumber: OTEL_SEVERITY[level],
|
|
13936
|
+
severityText: level.toUpperCase(),
|
|
13937
|
+
body,
|
|
13938
|
+
attributes: module ? { "halo.module": module } : void 0
|
|
13939
|
+
});
|
|
13940
|
+
}
|
|
13559
13941
|
function initLogger() {
|
|
13560
13942
|
const origLog = console.log.bind(console);
|
|
13561
13943
|
const origError = console.error.bind(console);
|
|
@@ -13566,39 +13948,50 @@ function initLogger() {
|
|
|
13566
13948
|
return;
|
|
13567
13949
|
origDebug(...args);
|
|
13568
13950
|
writeToFile(formatLine("debug", args));
|
|
13951
|
+
emitOtelLog("debug", args);
|
|
13569
13952
|
};
|
|
13570
13953
|
console.log = (...args) => {
|
|
13571
13954
|
if (!shouldLog("info"))
|
|
13572
13955
|
return;
|
|
13573
13956
|
origLog(...args);
|
|
13574
13957
|
writeToFile(formatLine("info", args));
|
|
13958
|
+
emitOtelLog("info", args);
|
|
13575
13959
|
};
|
|
13576
13960
|
console.warn = (...args) => {
|
|
13577
13961
|
if (!shouldLog("warn"))
|
|
13578
13962
|
return;
|
|
13579
13963
|
origWarn(...args);
|
|
13580
13964
|
writeToFile(formatLine("warn", args));
|
|
13965
|
+
emitOtelLog("warn", args);
|
|
13581
13966
|
};
|
|
13582
13967
|
console.error = (...args) => {
|
|
13583
13968
|
if (!shouldLog("error"))
|
|
13584
13969
|
return;
|
|
13585
13970
|
origError(...args);
|
|
13586
13971
|
writeToFile(formatLine("error", args));
|
|
13972
|
+
emitOtelLog("error", args);
|
|
13587
13973
|
};
|
|
13588
13974
|
if (shouldLog("info")) {
|
|
13589
13975
|
writeToFile(formatLine("info", [`[Logger] File logging initialized: ${logFile} (level: ${config.logging.level})`]));
|
|
13590
13976
|
}
|
|
13591
13977
|
}
|
|
13592
|
-
var LOG_FILENAME, logDir, logFile, currentSize, LEVEL_ORDER;
|
|
13978
|
+
var LOG_FILENAME, logDir, logFile, currentSize, LEVEL_ORDER, OTEL_SEVERITY;
|
|
13593
13979
|
var init_logger = __esm({
|
|
13594
13980
|
"../server/dist/logger.js"() {
|
|
13595
13981
|
"use strict";
|
|
13596
13982
|
init_config();
|
|
13983
|
+
init_otel();
|
|
13597
13984
|
LOG_FILENAME = "server.log";
|
|
13598
13985
|
logDir = path31.join(homedir13(), ".halo", "global", "logs");
|
|
13599
13986
|
logFile = path31.join(logDir, LOG_FILENAME);
|
|
13600
13987
|
currentSize = -1;
|
|
13601
13988
|
LEVEL_ORDER = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
13989
|
+
OTEL_SEVERITY = {
|
|
13990
|
+
debug: SeverityNumber.DEBUG,
|
|
13991
|
+
info: SeverityNumber.INFO,
|
|
13992
|
+
warn: SeverityNumber.WARN,
|
|
13993
|
+
error: SeverityNumber.ERROR
|
|
13994
|
+
};
|
|
13602
13995
|
}
|
|
13603
13996
|
});
|
|
13604
13997
|
|
|
@@ -15326,7 +15719,7 @@ function filterVerbs(items, rawValue) {
|
|
|
15326
15719
|
const partial = m[2].toLowerCase();
|
|
15327
15720
|
return cmd.verbs.filter((v) => v.name.startsWith(partial) && v.name !== partial).map((v) => ({ slashName: `${cmd.slashName} ${v.name}`, description: v.desc ?? "" }));
|
|
15328
15721
|
}
|
|
15329
|
-
function InputBox({ enabled, placeholder, history, onSubmit, hint, commands = [], workspace, onPopupOpenChange }) {
|
|
15722
|
+
function InputBox({ enabled: enabled2, placeholder, history, onSubmit, hint, commands = [], workspace, onPopupOpenChange }) {
|
|
15330
15723
|
const [edit, setEdit] = useState2({ value: "", cursor: 0 });
|
|
15331
15724
|
const { value, cursor } = edit;
|
|
15332
15725
|
const [historyIdx, setHistoryIdx] = useState2(null);
|
|
@@ -15353,7 +15746,7 @@ function InputBox({ enabled, placeholder, history, onSubmit, hint, commands = []
|
|
|
15353
15746
|
insertion = normalized;
|
|
15354
15747
|
}
|
|
15355
15748
|
applyEdit((e) => insertAt(e, insertion));
|
|
15356
|
-
}, { isActive:
|
|
15749
|
+
}, { isActive: enabled2 });
|
|
15357
15750
|
const expandPastes = (v) => v.replace(/\[#(\d+) pasted \d+ lines?\]/g, (m, id) => pendingPastes.current.get(Number(id)) ?? m);
|
|
15358
15751
|
const cmdMatches = useMemo(() => filterCommands(commands, value), [commands, value]);
|
|
15359
15752
|
const verbMatches = useMemo(() => filterVerbs(commands, value), [commands, value]);
|
|
@@ -15539,17 +15932,17 @@ function InputBox({ enabled, placeholder, history, onSubmit, hint, commands = []
|
|
|
15539
15932
|
if (input.length === 0) return;
|
|
15540
15933
|
const clean = input.replace(/[\r\n]+/g, " ");
|
|
15541
15934
|
applyEdit((e) => insertAt(e, clean));
|
|
15542
|
-
}, { isActive:
|
|
15935
|
+
}, { isActive: enabled2 });
|
|
15543
15936
|
useEffect2(() => {
|
|
15544
15937
|
if (historyIdx !== null && value === "") setHistoryIdx(null);
|
|
15545
15938
|
}, [value, historyIdx]);
|
|
15546
15939
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
15547
15940
|
suggestOpen ? /* @__PURE__ */ jsx8(SlashSuggest, { items: filtered, selectedIdx: suggestIdx }) : null,
|
|
15548
15941
|
!suggestOpen && pathOpen ? /* @__PURE__ */ jsx8(PathSuggest, { items: pathScan.items, selectedIdx: pathIdx, cwdLabel: pathScan.cwdLabel }) : null,
|
|
15549
|
-
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor:
|
|
15942
|
+
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: enabled2 ? "cyan" : "gray", paddingX: 1, children: [
|
|
15550
15943
|
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
15551
|
-
/* @__PURE__ */ jsx8(Text8, { color:
|
|
15552
|
-
|
|
15944
|
+
/* @__PURE__ */ jsx8(Text8, { color: enabled2 ? "cyan" : "gray", children: "> " }),
|
|
15945
|
+
enabled2 ? /* @__PURE__ */ jsx8(TextInputView, { value, cursor, placeholder }) : /* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: placeholder })
|
|
15553
15946
|
] }),
|
|
15554
15947
|
hint ? /* @__PURE__ */ jsx8(Box7, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: hint }) }) : null
|
|
15555
15948
|
] })
|
|
@@ -18374,10 +18767,10 @@ function parseAgentYaml(content) {
|
|
|
18374
18767
|
}
|
|
18375
18768
|
function buildScaffoldModelBlock() {
|
|
18376
18769
|
const registry2 = getModelsRegistry();
|
|
18377
|
-
const
|
|
18770
|
+
const providers2 = registry2.providers ?? [];
|
|
18378
18771
|
const configured = config.agent.defaultProvider;
|
|
18379
|
-
const explicit = configured ?
|
|
18380
|
-
const provider = explicit ??
|
|
18772
|
+
const explicit = configured ? providers2.find((p) => p.id === configured) : void 0;
|
|
18773
|
+
const provider = explicit ?? providers2.find((p) => p.id === FALLBACK_SCAFFOLD_PROVIDER) ?? providers2[0];
|
|
18381
18774
|
if (!provider) {
|
|
18382
18775
|
return { provider: FALLBACK_SCAFFOLD_PROVIDER };
|
|
18383
18776
|
}
|
|
@@ -29771,6 +30164,7 @@ async function gracefulShutdown(signal) {
|
|
|
29771
30164
|
server.close();
|
|
29772
30165
|
}
|
|
29773
30166
|
console.log("[Server] Shutdown complete");
|
|
30167
|
+
await shutdownObservability();
|
|
29774
30168
|
process.exit(0);
|
|
29775
30169
|
}
|
|
29776
30170
|
var __dirname3, PORT, HALO_VERSION, PROJECT_ROOT, HALO_HOME4, GIT_SHA, AGENTCORE, app, allowlist, authRoutes, fileRoutes, dataPreviewRoutes, gitRoutes, agentConfigRoutes, skillRoutes, settingsRoutes, evolutionRoutes, cronRoutes, channelDb, registry, sessionRoutes, sessionArchiveRoutes, showRoutes, metricsRoutes, commandRoutes, FRONTEND_DIR, server, wss;
|
|
@@ -29810,13 +30204,14 @@ var init_dist2 = __esm({
|
|
|
29810
30204
|
init_descriptors();
|
|
29811
30205
|
init_auth();
|
|
29812
30206
|
init_logger();
|
|
30207
|
+
init_otel();
|
|
29813
30208
|
init_config();
|
|
29814
30209
|
init_sandbox();
|
|
29815
30210
|
init_init();
|
|
29816
30211
|
init_git_ssh();
|
|
29817
30212
|
__dirname3 = path67.dirname(fileURLToPath3(import.meta.url));
|
|
29818
30213
|
PORT = config.server.port;
|
|
29819
|
-
HALO_VERSION = "1.
|
|
30214
|
+
HALO_VERSION = "1.3.1";
|
|
29820
30215
|
PROJECT_ROOT = findProjectRoot();
|
|
29821
30216
|
HALO_HOME4 = path67.join(homedir24(), ".halo");
|
|
29822
30217
|
GIT_SHA = readGitSha();
|
|
@@ -29855,6 +30250,7 @@ var init_dist2 = __esm({
|
|
|
29855
30250
|
throw new Error(`[Server] Dispatch cases without a descriptor: ${orphanDispatch.join(", ")}. Add a registerDescriptor entry in commands/index.ts or remove the dispatch case.`);
|
|
29856
30251
|
}
|
|
29857
30252
|
}
|
|
30253
|
+
await initObservability();
|
|
29858
30254
|
initLogger();
|
|
29859
30255
|
await initBwrapCheck();
|
|
29860
30256
|
setSandboxHiddenPaths(config.sandbox.hiddenDirs, config.sandbox.hiddenFiles, config.sandbox.writableDirs);
|
|
@@ -30750,7 +31146,7 @@ import path68 from "node:path";
|
|
|
30750
31146
|
import fs61 from "node:fs";
|
|
30751
31147
|
import { homedir as homedir25 } from "node:os";
|
|
30752
31148
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
30753
|
-
var VERSION = "1.
|
|
31149
|
+
var VERSION = "1.3.1";
|
|
30754
31150
|
var HELP_TOP = `Usage: halo <command> [options]
|
|
30755
31151
|
|
|
30756
31152
|
Commands:
|
|
@@ -31130,8 +31526,8 @@ async function cmdSetup(argv = []) {
|
|
|
31130
31526
|
`);
|
|
31131
31527
|
}
|
|
31132
31528
|
async function stepModelProviders() {
|
|
31133
|
-
const
|
|
31134
|
-
if (
|
|
31529
|
+
const providers2 = listModelProviders2();
|
|
31530
|
+
if (providers2.length === 0) return;
|
|
31135
31531
|
const enter = await promptSelect2("Configure model provider keys now? (you can also re-run `halo setup` later)", [
|
|
31136
31532
|
{ value: "skip", label: "Skip" },
|
|
31137
31533
|
{ value: "go", label: "Configure..." }
|
|
@@ -31142,7 +31538,7 @@ async function cmdSetup(argv = []) {
|
|
|
31142
31538
|
return;
|
|
31143
31539
|
}
|
|
31144
31540
|
while (true) {
|
|
31145
|
-
const opts =
|
|
31541
|
+
const opts = providers2.map((p) => ({
|
|
31146
31542
|
value: p.id,
|
|
31147
31543
|
label: p.displayName,
|
|
31148
31544
|
hint: providerStatusHint(p.id, p.fields)
|
|
@@ -31151,7 +31547,7 @@ async function cmdSetup(argv = []) {
|
|
|
31151
31547
|
const pick = await promptSelect2("Pick a provider to configure (Done when finished):", opts);
|
|
31152
31548
|
if (pick == null) abort();
|
|
31153
31549
|
if (pick === "__done__") break;
|
|
31154
|
-
const provider =
|
|
31550
|
+
const provider = providers2.find((p) => p.id === pick);
|
|
31155
31551
|
if (!provider) continue;
|
|
31156
31552
|
await configureSecretGroup(provider.id, provider.bucket, provider.fields);
|
|
31157
31553
|
}
|