@codexhost/cli-darwin-x64 0.9.1-test.2 → 0.9.2
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 -1
- package/app/codexhost-distribution.json +1 -1
- package/app/desktop-controller.mjs +256 -179
- package/app/host-runtime.mjs +594 -298
- package/app/plugins/antigravity/plugin.mjs +123 -1
- package/app/plugins/claude-code/plugin.mjs +248 -42
- package/app/plugins/codebuddy/plugin.mjs +2166 -758
- package/app/plugins/cursor-cli/plugin.mjs +35942 -10942
- package/app/plugins/deepseek-harness/plugin.mjs +70 -0
- package/app/plugins/enabled.json +1 -0
- package/app/plugins/grok/plugin.mjs +193 -82
- package/app/plugins/hermes/plugin.mjs +2444 -94
- package/app/plugins/kiro-cli/plugin.mjs +70 -0
- package/app/plugins/omp/plugin.mjs +106 -8
- package/app/plugins/opencode/plugin.mjs +70 -0
- package/app/plugins/pi/plugin.mjs +1933 -767
- package/app/plugins/qoder/plugin.mjs +71 -9
- package/app/plugins/qoder-cn/plugin.mjs +71 -9
- package/app/plugins/workbuddy/assets/icon.svg +29 -0
- package/app/plugins/workbuddy/manifest.json +14 -0
- package/app/plugins/workbuddy/plugin.mjs +24873 -0
- package/app/renderer-extension.js +5290 -3642
- package/bin/codexhost +0 -0
- package/libexec/codexhost-shim +0 -0
- package/libexec/codexhost-updater +0 -0
- package/package.json +1 -1
|
@@ -5,9 +5,6 @@ var __export = (target, all) => {
|
|
|
5
5
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
// dist/hermes-adapter.js
|
|
9
|
-
import { createHash } from "node:crypto";
|
|
10
|
-
|
|
11
8
|
// ../../../node_modules/zod/v4/classic/external.js
|
|
12
9
|
var external_exports = {};
|
|
13
10
|
__export(external_exports, {
|
|
@@ -774,10 +771,10 @@ function mergeDefs(...defs) {
|
|
|
774
771
|
function cloneDef(schema) {
|
|
775
772
|
return mergeDefs(schema._zod.def);
|
|
776
773
|
}
|
|
777
|
-
function getElementAtPath(obj,
|
|
778
|
-
if (!
|
|
774
|
+
function getElementAtPath(obj, path7) {
|
|
775
|
+
if (!path7)
|
|
779
776
|
return obj;
|
|
780
|
-
return
|
|
777
|
+
return path7.reduce((acc, key) => acc?.[key], obj);
|
|
781
778
|
}
|
|
782
779
|
function promiseAllObject(promisesObj) {
|
|
783
780
|
const keys = Object.keys(promisesObj);
|
|
@@ -1186,11 +1183,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
1186
1183
|
}
|
|
1187
1184
|
return false;
|
|
1188
1185
|
}
|
|
1189
|
-
function prefixIssues(
|
|
1186
|
+
function prefixIssues(path7, issues) {
|
|
1190
1187
|
return issues.map((iss) => {
|
|
1191
1188
|
var _a3;
|
|
1192
1189
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
1193
|
-
iss.path.unshift(
|
|
1190
|
+
iss.path.unshift(path7);
|
|
1194
1191
|
return iss;
|
|
1195
1192
|
});
|
|
1196
1193
|
}
|
|
@@ -1337,16 +1334,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
1337
1334
|
}
|
|
1338
1335
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
1339
1336
|
const fieldErrors = { _errors: [] };
|
|
1340
|
-
const processError = (error52,
|
|
1337
|
+
const processError = (error52, path7 = []) => {
|
|
1341
1338
|
for (const issue2 of error52.issues) {
|
|
1342
1339
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
1343
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
1340
|
+
issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
|
|
1344
1341
|
} else if (issue2.code === "invalid_key") {
|
|
1345
|
-
processError({ issues: issue2.issues }, [...
|
|
1342
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
1346
1343
|
} else if (issue2.code === "invalid_element") {
|
|
1347
|
-
processError({ issues: issue2.issues }, [...
|
|
1344
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
1348
1345
|
} else {
|
|
1349
|
-
const fullpath = [...
|
|
1346
|
+
const fullpath = [...path7, ...issue2.path];
|
|
1350
1347
|
if (fullpath.length === 0) {
|
|
1351
1348
|
fieldErrors._errors.push(mapper(issue2));
|
|
1352
1349
|
} else {
|
|
@@ -1373,17 +1370,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
1373
1370
|
}
|
|
1374
1371
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
1375
1372
|
const result = { errors: [] };
|
|
1376
|
-
const processError = (error52,
|
|
1373
|
+
const processError = (error52, path7 = []) => {
|
|
1377
1374
|
var _a3, _b;
|
|
1378
1375
|
for (const issue2 of error52.issues) {
|
|
1379
1376
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
1380
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
1377
|
+
issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
|
|
1381
1378
|
} else if (issue2.code === "invalid_key") {
|
|
1382
|
-
processError({ issues: issue2.issues }, [...
|
|
1379
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
1383
1380
|
} else if (issue2.code === "invalid_element") {
|
|
1384
|
-
processError({ issues: issue2.issues }, [...
|
|
1381
|
+
processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
|
|
1385
1382
|
} else {
|
|
1386
|
-
const fullpath = [...
|
|
1383
|
+
const fullpath = [...path7, ...issue2.path];
|
|
1387
1384
|
if (fullpath.length === 0) {
|
|
1388
1385
|
result.errors.push(mapper(issue2));
|
|
1389
1386
|
continue;
|
|
@@ -1415,8 +1412,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
1415
1412
|
}
|
|
1416
1413
|
function toDotPath(_path) {
|
|
1417
1414
|
const segs = [];
|
|
1418
|
-
const
|
|
1419
|
-
for (const seg of
|
|
1415
|
+
const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
1416
|
+
for (const seg of path7) {
|
|
1420
1417
|
if (typeof seg === "number")
|
|
1421
1418
|
segs.push(`[${seg}]`);
|
|
1422
1419
|
else if (typeof seg === "symbol")
|
|
@@ -7646,8 +7643,8 @@ function ko_default() {
|
|
|
7646
7643
|
}
|
|
7647
7644
|
|
|
7648
7645
|
// ../../../node_modules/zod/v4/locales/lt.js
|
|
7649
|
-
var capitalizeFirstCharacter = (
|
|
7650
|
-
return
|
|
7646
|
+
var capitalizeFirstCharacter = (text2) => {
|
|
7647
|
+
return text2.charAt(0).toUpperCase() + text2.slice(1);
|
|
7651
7648
|
};
|
|
7652
7649
|
function getUnitTypeFromNumber(number4) {
|
|
7653
7650
|
const abs = Math.abs(number4);
|
|
@@ -14108,13 +14105,13 @@ function resolveRef(ref, ctx) {
|
|
|
14108
14105
|
if (!ref.startsWith("#")) {
|
|
14109
14106
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
14110
14107
|
}
|
|
14111
|
-
const
|
|
14112
|
-
if (
|
|
14108
|
+
const path7 = ref.slice(1).split("/").filter(Boolean);
|
|
14109
|
+
if (path7.length === 0) {
|
|
14113
14110
|
return ctx.rootSchema;
|
|
14114
14111
|
}
|
|
14115
14112
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
14116
|
-
if (
|
|
14117
|
-
const key =
|
|
14113
|
+
if (path7[0] === defsKey) {
|
|
14114
|
+
const key = path7[1];
|
|
14118
14115
|
if (!key || !ctx.defs[key]) {
|
|
14119
14116
|
throw new Error(`Reference not found: ${ref}`);
|
|
14120
14117
|
}
|
|
@@ -14522,6 +14519,61 @@ function date4(params) {
|
|
|
14522
14519
|
// ../../../node_modules/zod/v4/classic/external.js
|
|
14523
14520
|
config(en_default());
|
|
14524
14521
|
|
|
14522
|
+
// ../../shared-contracts/dist/credential-imports.js
|
|
14523
|
+
var credentialSourceSchema = external_exports.object({
|
|
14524
|
+
id: external_exports.string().min(1).max(256),
|
|
14525
|
+
harnessId: external_exports.string().min(1),
|
|
14526
|
+
label: external_exports.string().min(1).max(512),
|
|
14527
|
+
provider: external_exports.enum(["openai-codex", "xai"])
|
|
14528
|
+
}).strict();
|
|
14529
|
+
var credentialImportNameSchema = external_exports.string().regex(/^[a-z][a-z0-9-]{0,47}$/);
|
|
14530
|
+
var credentialImportRecordSchema = external_exports.object({
|
|
14531
|
+
name: credentialImportNameSchema,
|
|
14532
|
+
source: credentialSourceSchema,
|
|
14533
|
+
importedAt: external_exports.string()
|
|
14534
|
+
}).strict();
|
|
14535
|
+
var credentialImportsRequestSchema = external_exports.discriminatedUnion("action", [
|
|
14536
|
+
external_exports.object({ action: external_exports.literal("list") }).strict(),
|
|
14537
|
+
external_exports.object({
|
|
14538
|
+
action: external_exports.literal("import"),
|
|
14539
|
+
sourceId: external_exports.string().min(1).max(256),
|
|
14540
|
+
name: credentialImportNameSchema,
|
|
14541
|
+
confirmed: external_exports.literal(true)
|
|
14542
|
+
}).strict(),
|
|
14543
|
+
external_exports.object({
|
|
14544
|
+
action: external_exports.literal("reimport"),
|
|
14545
|
+
sourceId: external_exports.string().min(1).max(256),
|
|
14546
|
+
name: credentialImportNameSchema,
|
|
14547
|
+
confirmed: external_exports.literal(true)
|
|
14548
|
+
}).strict(),
|
|
14549
|
+
external_exports.object({
|
|
14550
|
+
action: external_exports.literal("remove"),
|
|
14551
|
+
name: credentialImportNameSchema,
|
|
14552
|
+
confirmed: external_exports.literal(true)
|
|
14553
|
+
}).strict()
|
|
14554
|
+
]);
|
|
14555
|
+
var credentialOtherLoginSchema = external_exports.object({
|
|
14556
|
+
provider: external_exports.string().min(1).max(128),
|
|
14557
|
+
type: external_exports.enum(["oauth", "api_key", "unknown"]),
|
|
14558
|
+
/** Best-effort account label (e.g. the email in a Codex token), derived locally when present. */
|
|
14559
|
+
label: external_exports.string().min(1).max(512).optional(),
|
|
14560
|
+
/** Recognized credential vendor, derived from the OAuth token issuer. Absent when unknown. */
|
|
14561
|
+
vendor: external_exports.enum(["openai-codex", "xai"]).optional()
|
|
14562
|
+
}).strict();
|
|
14563
|
+
var credentialImportsResultSchema = external_exports.object({
|
|
14564
|
+
sources: external_exports.array(credentialSourceSchema),
|
|
14565
|
+
targets: external_exports.array(external_exports.object({
|
|
14566
|
+
harnessId: external_exports.string(),
|
|
14567
|
+
providers: external_exports.array(external_exports.enum(["openai-codex", "xai"])),
|
|
14568
|
+
imports: external_exports.array(credentialImportRecordSchema),
|
|
14569
|
+
others: external_exports.array(credentialOtherLoginSchema).default([])
|
|
14570
|
+
}).strict())
|
|
14571
|
+
}).strict();
|
|
14572
|
+
var credentialImportsParamsSchema = external_exports.object({
|
|
14573
|
+
targetHarnessId: external_exports.string().min(1).max(128).optional(),
|
|
14574
|
+
request: credentialImportsRequestSchema
|
|
14575
|
+
}).strict();
|
|
14576
|
+
|
|
14525
14577
|
// ../../shared-contracts/dist/version.js
|
|
14526
14578
|
var WORKSPACE_CONTRACT_VERSION = 1;
|
|
14527
14579
|
|
|
@@ -15050,6 +15102,8 @@ var pluginPresentationShape = {
|
|
|
15050
15102
|
id: harnessPluginIdSchema,
|
|
15051
15103
|
name: external_exports.string().trim().min(1).max(128),
|
|
15052
15104
|
version: external_exports.string().min(1).max(128),
|
|
15105
|
+
/** The factory accepts a persisted local entrypoint through its construction context. */
|
|
15106
|
+
launchCommand: external_exports.literal(true).optional(),
|
|
15053
15107
|
links: external_exports.object({
|
|
15054
15108
|
documentation: documentationUrlSchema.optional(),
|
|
15055
15109
|
installation: documentationUrlSchema.optional()
|
|
@@ -15087,6 +15141,19 @@ var harnessPluginRouteSchema = external_exports.object({
|
|
|
15087
15141
|
permissionModeId: harnessPermissionModeIdSchema.optional()
|
|
15088
15142
|
}).strict();
|
|
15089
15143
|
|
|
15144
|
+
// ../../shared-contracts/dist/harness-launch-settings.js
|
|
15145
|
+
var harnessLaunchPathSchema = external_exports.string().trim().min(1).max(4096).refine((value) => !/[\u0000\r\n]/u.test(value), "Invalid entrypoint path");
|
|
15146
|
+
var harnessLaunchSettingsGetSchema = external_exports.object({
|
|
15147
|
+
harnessId: harnessPluginIdSchema
|
|
15148
|
+
}).strict();
|
|
15149
|
+
var harnessLaunchSettingsSetSchema = harnessLaunchSettingsGetSchema.extend({
|
|
15150
|
+
path: harnessLaunchPathSchema.nullable()
|
|
15151
|
+
});
|
|
15152
|
+
var harnessLaunchSettingsSchema = external_exports.object({
|
|
15153
|
+
path: harnessLaunchPathSchema.nullable(),
|
|
15154
|
+
restartRequired: external_exports.boolean()
|
|
15155
|
+
}).strict();
|
|
15156
|
+
|
|
15090
15157
|
// ../../shared-contracts/dist/codex-accounts.js
|
|
15091
15158
|
var accountIdSchema = external_exports.string().min(1).max(256).regex(/^[A-Za-z0-9._~-]+$/u);
|
|
15092
15159
|
var nonBlankTextSchema3 = external_exports.string().trim().min(1);
|
|
@@ -15317,6 +15384,8 @@ var nativeCheckpointRefV1RuntimeSchema = external_exports.strictObject({
|
|
|
15317
15384
|
locator: jsonValueSchema.optional(),
|
|
15318
15385
|
formatVersion: external_exports.literal(1)
|
|
15319
15386
|
}).superRefine(rejectExplicitUndefined(["locator"]));
|
|
15387
|
+
var nativeCheckpointRefV1Schema = nativeCheckpointRefV1RuntimeSchema;
|
|
15388
|
+
var nativeCheckpointRefSchema = nativeCheckpointRefV1Schema;
|
|
15320
15389
|
|
|
15321
15390
|
// ../../shared-contracts/dist/updates.js
|
|
15322
15391
|
var UPDATE_ERROR_MAX_LENGTH = 500;
|
|
@@ -15372,6 +15441,62 @@ var updateStatusResultSchema = external_exports.strictObject({
|
|
|
15372
15441
|
// ../../shared-contracts/dist/index.js
|
|
15373
15442
|
var workspaceContractVersionSchema = external_exports.literal(WORKSPACE_CONTRACT_VERSION);
|
|
15374
15443
|
|
|
15444
|
+
// dist/hermes-commands.js
|
|
15445
|
+
var SUPPORTED_COMMANDS = /* @__PURE__ */ new Set(["help", "tools", "context", "compress", "version"]);
|
|
15446
|
+
function hermesCommandCatalog(commands) {
|
|
15447
|
+
return harnessCommandCatalogSchema.parse({
|
|
15448
|
+
commands: commands.filter((command) => SUPPORTED_COMMANDS.has(command.name)).map((command) => ({
|
|
15449
|
+
id: `hermes.${command.name}`,
|
|
15450
|
+
invocation: `/${command.name}`,
|
|
15451
|
+
label: `/${command.name}`,
|
|
15452
|
+
description: command.description.slice(0, 512) || `Hermes /${command.name}`,
|
|
15453
|
+
argumentMode: command.input ? "text" : "none"
|
|
15454
|
+
}))
|
|
15455
|
+
});
|
|
15456
|
+
}
|
|
15457
|
+
var HERMES_GATEWAY_COMMANDS = [
|
|
15458
|
+
...["help", "tools", "context", "version"].map((name) => ({
|
|
15459
|
+
name,
|
|
15460
|
+
description: `Hermes /${name}`
|
|
15461
|
+
})),
|
|
15462
|
+
{
|
|
15463
|
+
name: "compress",
|
|
15464
|
+
description: "Compress conversation context",
|
|
15465
|
+
input: { hint: "Optional compression focus" }
|
|
15466
|
+
}
|
|
15467
|
+
];
|
|
15468
|
+
var HERMES_COMMAND_CATALOG = hermesCommandCatalog(HERMES_GATEWAY_COMMANDS);
|
|
15469
|
+
function hermesCommandText(command, catalog) {
|
|
15470
|
+
const descriptor = catalog.commands.find((entry) => entry.id === command.commandId);
|
|
15471
|
+
if (!descriptor)
|
|
15472
|
+
return {
|
|
15473
|
+
ok: false,
|
|
15474
|
+
error: {
|
|
15475
|
+
code: "unsupported",
|
|
15476
|
+
message: "Hermes did not advertise this command",
|
|
15477
|
+
retryable: false
|
|
15478
|
+
}
|
|
15479
|
+
};
|
|
15480
|
+
const args = command.arguments ?? {};
|
|
15481
|
+
if (Object.keys(args).some((key) => key !== "text") || args.text !== void 0 && typeof args.text !== "string" || descriptor.argumentMode === "none" && typeof args.text === "string" && args.text.trim()) {
|
|
15482
|
+
return {
|
|
15483
|
+
ok: false,
|
|
15484
|
+
error: {
|
|
15485
|
+
code: "invalidRequest",
|
|
15486
|
+
message: "Invalid Hermes command arguments",
|
|
15487
|
+
retryable: false
|
|
15488
|
+
}
|
|
15489
|
+
};
|
|
15490
|
+
}
|
|
15491
|
+
return {
|
|
15492
|
+
ok: true,
|
|
15493
|
+
value: `${descriptor.invocation}${typeof args.text === "string" && args.text.trim() ? ` ${args.text.trim()}` : ""}`
|
|
15494
|
+
};
|
|
15495
|
+
}
|
|
15496
|
+
|
|
15497
|
+
// dist/hermes-adapter.js
|
|
15498
|
+
import { createHash } from "node:crypto";
|
|
15499
|
+
|
|
15375
15500
|
// dist/acp-transport.js
|
|
15376
15501
|
import { spawn, spawnSync } from "node:child_process";
|
|
15377
15502
|
import { Readable, Writable } from "node:stream";
|
|
@@ -15384,6 +15509,42 @@ function validateHostApprovalResponse(interaction, response) {
|
|
|
15384
15509
|
return interaction.actions.some(({ id }) => id === response.actionId) ? null : invalidRequest("Approval Response contains an undeclared action ID");
|
|
15385
15510
|
}
|
|
15386
15511
|
|
|
15512
|
+
// ../../harness-adapter/dist/question.js
|
|
15513
|
+
function invalidRequest2(message) {
|
|
15514
|
+
return { code: "invalidRequest", message, retryable: false };
|
|
15515
|
+
}
|
|
15516
|
+
function validateHostQuestionResponse(interaction, response) {
|
|
15517
|
+
const questionIds = new Set(interaction.questions.map(({ id }) => id));
|
|
15518
|
+
if (response.cancelled) {
|
|
15519
|
+
return Object.keys(response.answers).length === 0 ? null : invalidRequest2("Cancelled Question Response must not contain answers");
|
|
15520
|
+
}
|
|
15521
|
+
for (const answerId of Object.keys(response.answers)) {
|
|
15522
|
+
if (!questionIds.has(answerId)) {
|
|
15523
|
+
return invalidRequest2("Question Response contains an unknown Question ID");
|
|
15524
|
+
}
|
|
15525
|
+
}
|
|
15526
|
+
for (const question2 of interaction.questions) {
|
|
15527
|
+
const answers = response.answers[question2.id] ?? [];
|
|
15528
|
+
if (!question2.optional && answers.length === 0) {
|
|
15529
|
+
return invalidRequest2("Question Response omits a required answer");
|
|
15530
|
+
}
|
|
15531
|
+
if (question2.type === "text") {
|
|
15532
|
+
if (answers.length > 1) {
|
|
15533
|
+
return invalidRequest2("Text Question accepts at most one answer");
|
|
15534
|
+
}
|
|
15535
|
+
continue;
|
|
15536
|
+
}
|
|
15537
|
+
if (!question2.multiple && answers.length > 1) {
|
|
15538
|
+
return invalidRequest2("Single-choice Question accepts at most one answer");
|
|
15539
|
+
}
|
|
15540
|
+
const declared = new Set(question2.options.map(({ value }) => value));
|
|
15541
|
+
if (!question2.allowOther && answers.some((answer) => !declared.has(answer))) {
|
|
15542
|
+
return invalidRequest2("Question Response contains an undeclared choice");
|
|
15543
|
+
}
|
|
15544
|
+
}
|
|
15545
|
+
return null;
|
|
15546
|
+
}
|
|
15547
|
+
|
|
15387
15548
|
// ../../harness-adapter/dist/output-channel.js
|
|
15388
15549
|
var HarnessOutputChannel = class {
|
|
15389
15550
|
outputs;
|
|
@@ -19960,8 +20121,8 @@ function classifyStartupError(error51) {
|
|
|
19960
20121
|
return new HermesTransportError("notInstalled", error51.message, { cause: error51 });
|
|
19961
20122
|
}
|
|
19962
20123
|
const detail = errorDetails2(error51);
|
|
19963
|
-
const
|
|
19964
|
-
if (
|
|
20124
|
+
const text2 = detail.toLowerCase();
|
|
20125
|
+
if (text2.includes("auth_required") || text2.includes("authentication") || text2.includes("not configured") || text2.includes("no provider") || text2.includes("no llm provider")) {
|
|
19965
20126
|
return new HermesTransportError("authenticationRequired", detail, {
|
|
19966
20127
|
cause: error51,
|
|
19967
20128
|
diagnostic: detail
|
|
@@ -20041,6 +20202,10 @@ var HermesAcpTransport = class {
|
|
|
20041
20202
|
#replay = null;
|
|
20042
20203
|
#sessionId = null;
|
|
20043
20204
|
#stderrTail = "";
|
|
20205
|
+
#availableCommands = [];
|
|
20206
|
+
get availableCommands() {
|
|
20207
|
+
return this.#availableCommands;
|
|
20208
|
+
}
|
|
20044
20209
|
/** Late binding: the Session registers its fault consumer on construction. */
|
|
20045
20210
|
onFault = () => void 0;
|
|
20046
20211
|
constructor(options) {
|
|
@@ -20179,7 +20344,7 @@ var HermesAcpTransport = class {
|
|
|
20179
20344
|
throw classifyStartupError(error51);
|
|
20180
20345
|
}
|
|
20181
20346
|
}
|
|
20182
|
-
async runTurn(
|
|
20347
|
+
async runTurn(text2, onEvent, onPermission) {
|
|
20183
20348
|
const connection = this.#connection;
|
|
20184
20349
|
if (!connection || !this.#sessionId || this.#closed || this.#closing) {
|
|
20185
20350
|
throw new HermesTransportError("unavailable", "Hermes ACP Session is unavailable");
|
|
@@ -20191,7 +20356,7 @@ var HermesAcpTransport = class {
|
|
|
20191
20356
|
try {
|
|
20192
20357
|
return await connection.prompt({
|
|
20193
20358
|
sessionId: this.#sessionId,
|
|
20194
|
-
prompt: [{ type: "text", text }]
|
|
20359
|
+
prompt: [{ type: "text", text: text2 }]
|
|
20195
20360
|
});
|
|
20196
20361
|
} finally {
|
|
20197
20362
|
if (this.#activePrompt === active)
|
|
@@ -20230,6 +20395,10 @@ var HermesAcpTransport = class {
|
|
|
20230
20395
|
#handleUpdate(notification) {
|
|
20231
20396
|
if (this.#sessionId && notification.sessionId !== this.#sessionId)
|
|
20232
20397
|
return;
|
|
20398
|
+
if (notification.update.sessionUpdate === "available_commands_update") {
|
|
20399
|
+
this.#availableCommands = notification.update.availableCommands;
|
|
20400
|
+
return;
|
|
20401
|
+
}
|
|
20233
20402
|
const event = transportEvent(notification.update);
|
|
20234
20403
|
if (!event)
|
|
20235
20404
|
return;
|
|
@@ -20292,9 +20461,6 @@ var HermesAcpTransport = class {
|
|
|
20292
20461
|
clientCapabilities: {},
|
|
20293
20462
|
clientInfo: { name: "codexhost", version: "0.1.0" }
|
|
20294
20463
|
}), this.#options.commandTimeoutMs, "Hermes ACP initialize");
|
|
20295
|
-
if (initialize.protocolVersion !== PROTOCOL_VERSION) {
|
|
20296
|
-
throw new HermesTransportError("protocolError", `Hermes ACP negotiated unsupported protocol version ${initialize.protocolVersion}`);
|
|
20297
|
-
}
|
|
20298
20464
|
this.#initialize = initialize;
|
|
20299
20465
|
return initialize;
|
|
20300
20466
|
}
|
|
@@ -20433,8 +20599,28 @@ for row in payload.get("providers") or []:
|
|
|
20433
20599
|
)
|
|
20434
20600
|
if slug and model_id:
|
|
20435
20601
|
available = slug.lower() != "moa" or bool(moa_availability.get(model_id, False))
|
|
20602
|
+
aliases = [
|
|
20603
|
+
alias.strip()
|
|
20604
|
+
for alias in row.get("aliases") or []
|
|
20605
|
+
if isinstance(alias, str) and alias.strip()
|
|
20606
|
+
]
|
|
20607
|
+
# Hermes names configured custom endpoints as custom:<key>.
|
|
20608
|
+
# The inventory row slug is the bare config key (pi-openai),
|
|
20609
|
+
# which is useful for display but is not a valid native model route.
|
|
20610
|
+
# Prefer the native custom identity for the actual model ref and
|
|
20611
|
+
# retain the bare slug as an alias for matching older snapshots.
|
|
20612
|
+
native_slug = next(
|
|
20613
|
+
(alias for alias in aliases if alias.lower().startswith("custom:")),
|
|
20614
|
+
slug,
|
|
20615
|
+
)
|
|
20616
|
+
native_model_id = native_slug + ":" + model_id
|
|
20436
20617
|
rows.append({
|
|
20437
|
-
"modelId":
|
|
20618
|
+
"modelId": native_model_id,
|
|
20619
|
+
"modelIdAliases": [
|
|
20620
|
+
alias + ":" + model_id
|
|
20621
|
+
for alias in aliases
|
|
20622
|
+
if alias + ":" + model_id != native_model_id
|
|
20623
|
+
],
|
|
20438
20624
|
"label": model_id,
|
|
20439
20625
|
"provider": provider,
|
|
20440
20626
|
"available": available,
|
|
@@ -20446,6 +20632,11 @@ print(json.dumps({"models": rows, "currentModelId": current_model_id}))
|
|
|
20446
20632
|
`;
|
|
20447
20633
|
var HermesInventoryError = class extends Error {
|
|
20448
20634
|
};
|
|
20635
|
+
var HermesInventoryTimeoutError = class extends HermesInventoryError {
|
|
20636
|
+
constructor() {
|
|
20637
|
+
super("Hermes model inventory probe timed out");
|
|
20638
|
+
}
|
|
20639
|
+
};
|
|
20449
20640
|
async function venvPythonFromShim(hermesExecutable, platform = process.platform) {
|
|
20450
20641
|
try {
|
|
20451
20642
|
const shim = await readFile(hermesExecutable, "utf8");
|
|
@@ -20484,7 +20675,7 @@ function runProbe(pythonExecutable, timeoutMs, environment) {
|
|
|
20484
20675
|
let stderr = "";
|
|
20485
20676
|
const timer = setTimeout(() => {
|
|
20486
20677
|
child.kill("SIGKILL");
|
|
20487
|
-
reject(new
|
|
20678
|
+
reject(new HermesInventoryTimeoutError());
|
|
20488
20679
|
}, timeoutMs);
|
|
20489
20680
|
child.stdout.on("data", (chunk) => {
|
|
20490
20681
|
stdout += chunk.toString("utf8");
|
|
@@ -20503,11 +20694,11 @@ function runProbe(pythonExecutable, timeoutMs, environment) {
|
|
|
20503
20694
|
return;
|
|
20504
20695
|
}
|
|
20505
20696
|
try {
|
|
20506
|
-
const
|
|
20507
|
-
const models = (
|
|
20697
|
+
const parsed2 = JSON.parse(stdout.trim());
|
|
20698
|
+
const models = (parsed2.models ?? []).filter((model) => typeof model?.modelId === "string" && model.modelId.length > 0);
|
|
20508
20699
|
resolve({
|
|
20509
20700
|
models,
|
|
20510
|
-
currentModelId: typeof
|
|
20701
|
+
currentModelId: typeof parsed2.currentModelId === "string" && parsed2.currentModelId.length > 0 ? parsed2.currentModelId : null
|
|
20511
20702
|
});
|
|
20512
20703
|
} catch {
|
|
20513
20704
|
reject(new HermesInventoryError("Hermes inventory probe returned malformed output"));
|
|
@@ -20541,10 +20732,11 @@ function catalogModelsFromInventory(inventory) {
|
|
|
20541
20732
|
for (const model of inventory.models) {
|
|
20542
20733
|
if (model.available === false)
|
|
20543
20734
|
continue;
|
|
20544
|
-
const
|
|
20735
|
+
const nativeModelId = model.modelIdAliases?.find((alias) => alias.toLowerCase().startsWith("custom:")) ?? model.modelId;
|
|
20736
|
+
const ref = encodeHermesModelRef(nativeModelId);
|
|
20545
20737
|
if (!ref)
|
|
20546
20738
|
continue;
|
|
20547
|
-
if (inventory.currentModelId && model.modelId === inventory.currentModelId) {
|
|
20739
|
+
if (inventory.currentModelId && (model.modelId === inventory.currentModelId || nativeModelId === inventory.currentModelId || model.modelIdAliases?.includes(inventory.currentModelId))) {
|
|
20548
20740
|
defaultModel = ref;
|
|
20549
20741
|
}
|
|
20550
20742
|
models.push({
|
|
@@ -20604,7 +20796,558 @@ async function resolveHermesSessionCandidate(input) {
|
|
|
20604
20796
|
}
|
|
20605
20797
|
|
|
20606
20798
|
// dist/hermes-session.js
|
|
20799
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
20800
|
+
|
|
20801
|
+
// dist/hermes-questions.js
|
|
20607
20802
|
import { randomUUID } from "node:crypto";
|
|
20803
|
+
var HermesQuestions = class {
|
|
20804
|
+
emit;
|
|
20805
|
+
#waiters = /* @__PURE__ */ new Map();
|
|
20806
|
+
constructor(emit) {
|
|
20807
|
+
this.emit = emit;
|
|
20808
|
+
}
|
|
20809
|
+
open(turnId, request) {
|
|
20810
|
+
const interactionId = hostInteractionIdSchema.parse(randomUUID());
|
|
20811
|
+
const interaction = {
|
|
20812
|
+
type: "question",
|
|
20813
|
+
interactionId,
|
|
20814
|
+
turnId,
|
|
20815
|
+
questions: request.questions,
|
|
20816
|
+
...request.title ? { title: request.title } : {}
|
|
20817
|
+
};
|
|
20818
|
+
const pending = new Promise((resolve) => this.#waiters.set(interactionId, { interaction, resolve }));
|
|
20819
|
+
const abort = () => this.#cancel(interactionId, request.signal?.reason === "expired" ? "expired" : "cancelled");
|
|
20820
|
+
request.signal?.addEventListener("abort", abort, { once: true });
|
|
20821
|
+
this.emit({ kind: "interaction", interaction });
|
|
20822
|
+
if (request.signal?.aborted)
|
|
20823
|
+
abort();
|
|
20824
|
+
return pending.finally(() => request.signal?.removeEventListener("abort", abort));
|
|
20825
|
+
}
|
|
20826
|
+
respond(command) {
|
|
20827
|
+
const waiter = this.#waiters.get(command.interactionId);
|
|
20828
|
+
if (!waiter || command.response.type !== "question")
|
|
20829
|
+
return {
|
|
20830
|
+
ok: false,
|
|
20831
|
+
error: { code: "invalidRequest", message: "No pending Hermes question", retryable: false }
|
|
20832
|
+
};
|
|
20833
|
+
const error51 = validateHostQuestionResponse(waiter.interaction, command.response);
|
|
20834
|
+
if (error51)
|
|
20835
|
+
return { ok: false, error: { ...error51, retryable: false } };
|
|
20836
|
+
this.#waiters.delete(command.interactionId);
|
|
20837
|
+
waiter.resolve(command.response);
|
|
20838
|
+
this.emit({
|
|
20839
|
+
kind: "event",
|
|
20840
|
+
event: {
|
|
20841
|
+
type: "interaction.closed",
|
|
20842
|
+
interactionId: command.interactionId,
|
|
20843
|
+
turnId: waiter.interaction.turnId,
|
|
20844
|
+
reason: command.response.cancelled ? "cancelled" : "responded"
|
|
20845
|
+
}
|
|
20846
|
+
});
|
|
20847
|
+
return { ok: true, value: { accepted: true } };
|
|
20848
|
+
}
|
|
20849
|
+
cancel(turnId) {
|
|
20850
|
+
for (const [id, waiter] of this.#waiters)
|
|
20851
|
+
if (!turnId || waiter.interaction.turnId === turnId)
|
|
20852
|
+
this.#cancel(id, "cancelled");
|
|
20853
|
+
}
|
|
20854
|
+
#cancel(id, reason) {
|
|
20855
|
+
const waiter = this.#waiters.get(id);
|
|
20856
|
+
if (!waiter)
|
|
20857
|
+
return;
|
|
20858
|
+
this.#waiters.delete(id);
|
|
20859
|
+
waiter.resolve({ type: "question", answers: {}, cancelled: true });
|
|
20860
|
+
this.emit({
|
|
20861
|
+
kind: "event",
|
|
20862
|
+
event: {
|
|
20863
|
+
type: "interaction.closed",
|
|
20864
|
+
interactionId: waiter.interaction.interactionId,
|
|
20865
|
+
turnId: waiter.interaction.turnId,
|
|
20866
|
+
reason
|
|
20867
|
+
}
|
|
20868
|
+
});
|
|
20869
|
+
}
|
|
20870
|
+
};
|
|
20871
|
+
|
|
20872
|
+
// ../../../node_modules/diff/libesm/diff/base.js
|
|
20873
|
+
var Diff = class {
|
|
20874
|
+
diff(oldStr, newStr, options = {}) {
|
|
20875
|
+
let callback;
|
|
20876
|
+
if (typeof options === "function") {
|
|
20877
|
+
callback = options;
|
|
20878
|
+
options = {};
|
|
20879
|
+
} else if ("callback" in options) {
|
|
20880
|
+
callback = options.callback;
|
|
20881
|
+
}
|
|
20882
|
+
const oldString = this.castInput(oldStr, options);
|
|
20883
|
+
const newString = this.castInput(newStr, options);
|
|
20884
|
+
const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
|
|
20885
|
+
const newTokens = this.removeEmpty(this.tokenize(newString, options));
|
|
20886
|
+
return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
|
|
20887
|
+
}
|
|
20888
|
+
diffWithOptionsObj(oldTokens, newTokens, options, callback) {
|
|
20889
|
+
var _a3;
|
|
20890
|
+
const done = (value) => {
|
|
20891
|
+
value = this.postProcess(value, options);
|
|
20892
|
+
if (callback) {
|
|
20893
|
+
setTimeout(function() {
|
|
20894
|
+
callback(value);
|
|
20895
|
+
}, 0);
|
|
20896
|
+
return void 0;
|
|
20897
|
+
} else {
|
|
20898
|
+
return value;
|
|
20899
|
+
}
|
|
20900
|
+
};
|
|
20901
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
20902
|
+
let editLength = 1;
|
|
20903
|
+
let maxEditLength = newLen + oldLen;
|
|
20904
|
+
if (options.maxEditLength != null) {
|
|
20905
|
+
maxEditLength = Math.min(maxEditLength, options.maxEditLength);
|
|
20906
|
+
}
|
|
20907
|
+
const maxExecutionTime = (_a3 = options.timeout) !== null && _a3 !== void 0 ? _a3 : Infinity;
|
|
20908
|
+
const abortAfterTimestamp = Date.now() + maxExecutionTime;
|
|
20909
|
+
const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
|
|
20910
|
+
let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
|
|
20911
|
+
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
20912
|
+
return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
|
|
20913
|
+
}
|
|
20914
|
+
let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
|
|
20915
|
+
const execEditLength = () => {
|
|
20916
|
+
for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
20917
|
+
let basePath;
|
|
20918
|
+
const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
|
|
20919
|
+
if (removePath) {
|
|
20920
|
+
bestPath[diagonalPath - 1] = void 0;
|
|
20921
|
+
}
|
|
20922
|
+
let canAdd = false;
|
|
20923
|
+
if (addPath) {
|
|
20924
|
+
const addPathNewPos = addPath.oldPos - diagonalPath;
|
|
20925
|
+
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
20926
|
+
}
|
|
20927
|
+
const canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
20928
|
+
if (!canAdd && !canRemove) {
|
|
20929
|
+
bestPath[diagonalPath] = void 0;
|
|
20930
|
+
continue;
|
|
20931
|
+
}
|
|
20932
|
+
if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
|
|
20933
|
+
basePath = this.addToPath(addPath, true, false, 0, options);
|
|
20934
|
+
} else {
|
|
20935
|
+
basePath = this.addToPath(removePath, false, true, 1, options);
|
|
20936
|
+
}
|
|
20937
|
+
newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
|
|
20938
|
+
if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
20939
|
+
return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
|
|
20940
|
+
} else {
|
|
20941
|
+
bestPath[diagonalPath] = basePath;
|
|
20942
|
+
if (basePath.oldPos + 1 >= oldLen) {
|
|
20943
|
+
maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
|
|
20944
|
+
}
|
|
20945
|
+
if (newPos + 1 >= newLen) {
|
|
20946
|
+
minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
|
|
20947
|
+
}
|
|
20948
|
+
}
|
|
20949
|
+
}
|
|
20950
|
+
editLength++;
|
|
20951
|
+
};
|
|
20952
|
+
if (callback) {
|
|
20953
|
+
(function exec() {
|
|
20954
|
+
setTimeout(function() {
|
|
20955
|
+
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
|
|
20956
|
+
return callback(void 0);
|
|
20957
|
+
}
|
|
20958
|
+
if (!execEditLength()) {
|
|
20959
|
+
exec();
|
|
20960
|
+
}
|
|
20961
|
+
}, 0);
|
|
20962
|
+
})();
|
|
20963
|
+
} else {
|
|
20964
|
+
while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
20965
|
+
const ret = execEditLength();
|
|
20966
|
+
if (ret) {
|
|
20967
|
+
return ret;
|
|
20968
|
+
}
|
|
20969
|
+
}
|
|
20970
|
+
}
|
|
20971
|
+
}
|
|
20972
|
+
addToPath(path7, added, removed, oldPosInc, options) {
|
|
20973
|
+
const last = path7.lastComponent;
|
|
20974
|
+
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
20975
|
+
return {
|
|
20976
|
+
oldPos: path7.oldPos + oldPosInc,
|
|
20977
|
+
lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
|
|
20978
|
+
};
|
|
20979
|
+
} else {
|
|
20980
|
+
return {
|
|
20981
|
+
oldPos: path7.oldPos + oldPosInc,
|
|
20982
|
+
lastComponent: { count: 1, added, removed, previousComponent: last }
|
|
20983
|
+
};
|
|
20984
|
+
}
|
|
20985
|
+
}
|
|
20986
|
+
extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
|
|
20987
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
20988
|
+
let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
20989
|
+
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
|
|
20990
|
+
newPos++;
|
|
20991
|
+
oldPos++;
|
|
20992
|
+
commonCount++;
|
|
20993
|
+
if (options.oneChangePerToken) {
|
|
20994
|
+
basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
20995
|
+
}
|
|
20996
|
+
}
|
|
20997
|
+
if (commonCount && !options.oneChangePerToken) {
|
|
20998
|
+
basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
20999
|
+
}
|
|
21000
|
+
basePath.oldPos = oldPos;
|
|
21001
|
+
return newPos;
|
|
21002
|
+
}
|
|
21003
|
+
equals(left, right, options) {
|
|
21004
|
+
if (options.comparator) {
|
|
21005
|
+
return options.comparator(left, right);
|
|
21006
|
+
} else {
|
|
21007
|
+
return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
21008
|
+
}
|
|
21009
|
+
}
|
|
21010
|
+
removeEmpty(array2) {
|
|
21011
|
+
const ret = [];
|
|
21012
|
+
for (let i = 0; i < array2.length; i++) {
|
|
21013
|
+
if (array2[i]) {
|
|
21014
|
+
ret.push(array2[i]);
|
|
21015
|
+
}
|
|
21016
|
+
}
|
|
21017
|
+
return ret;
|
|
21018
|
+
}
|
|
21019
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
21020
|
+
castInput(value, options) {
|
|
21021
|
+
return value;
|
|
21022
|
+
}
|
|
21023
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
21024
|
+
tokenize(value, options) {
|
|
21025
|
+
return Array.from(value);
|
|
21026
|
+
}
|
|
21027
|
+
join(chars) {
|
|
21028
|
+
return chars.join("");
|
|
21029
|
+
}
|
|
21030
|
+
postProcess(changeObjects, options) {
|
|
21031
|
+
return changeObjects;
|
|
21032
|
+
}
|
|
21033
|
+
get useLongestToken() {
|
|
21034
|
+
return false;
|
|
21035
|
+
}
|
|
21036
|
+
buildValues(lastComponent, newTokens, oldTokens) {
|
|
21037
|
+
const components = [];
|
|
21038
|
+
let nextComponent;
|
|
21039
|
+
while (lastComponent) {
|
|
21040
|
+
components.push(lastComponent);
|
|
21041
|
+
nextComponent = lastComponent.previousComponent;
|
|
21042
|
+
delete lastComponent.previousComponent;
|
|
21043
|
+
lastComponent = nextComponent;
|
|
21044
|
+
}
|
|
21045
|
+
components.reverse();
|
|
21046
|
+
const componentLen = components.length;
|
|
21047
|
+
let componentPos = 0, newPos = 0, oldPos = 0;
|
|
21048
|
+
for (; componentPos < componentLen; componentPos++) {
|
|
21049
|
+
const component = components[componentPos];
|
|
21050
|
+
if (!component.removed) {
|
|
21051
|
+
if (!component.added && this.useLongestToken) {
|
|
21052
|
+
let value = newTokens.slice(newPos, newPos + component.count);
|
|
21053
|
+
value = value.map(function(value2, i) {
|
|
21054
|
+
const oldValue = oldTokens[oldPos + i];
|
|
21055
|
+
return oldValue.length > value2.length ? oldValue : value2;
|
|
21056
|
+
});
|
|
21057
|
+
component.value = this.join(value);
|
|
21058
|
+
} else {
|
|
21059
|
+
component.value = this.join(newTokens.slice(newPos, newPos + component.count));
|
|
21060
|
+
}
|
|
21061
|
+
newPos += component.count;
|
|
21062
|
+
if (!component.added) {
|
|
21063
|
+
oldPos += component.count;
|
|
21064
|
+
}
|
|
21065
|
+
} else {
|
|
21066
|
+
component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
|
|
21067
|
+
oldPos += component.count;
|
|
21068
|
+
}
|
|
21069
|
+
}
|
|
21070
|
+
return components;
|
|
21071
|
+
}
|
|
21072
|
+
};
|
|
21073
|
+
|
|
21074
|
+
// ../../../node_modules/diff/libesm/diff/line.js
|
|
21075
|
+
var LineDiff = class extends Diff {
|
|
21076
|
+
constructor() {
|
|
21077
|
+
super(...arguments);
|
|
21078
|
+
this.tokenize = tokenize;
|
|
21079
|
+
}
|
|
21080
|
+
equals(left, right, options) {
|
|
21081
|
+
if (options.ignoreWhitespace) {
|
|
21082
|
+
if (!options.newlineIsToken || !left.includes("\n")) {
|
|
21083
|
+
left = left.trim();
|
|
21084
|
+
}
|
|
21085
|
+
if (!options.newlineIsToken || !right.includes("\n")) {
|
|
21086
|
+
right = right.trim();
|
|
21087
|
+
}
|
|
21088
|
+
} else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
|
|
21089
|
+
if (left.endsWith("\n")) {
|
|
21090
|
+
left = left.slice(0, -1);
|
|
21091
|
+
}
|
|
21092
|
+
if (right.endsWith("\n")) {
|
|
21093
|
+
right = right.slice(0, -1);
|
|
21094
|
+
}
|
|
21095
|
+
}
|
|
21096
|
+
return super.equals(left, right, options);
|
|
21097
|
+
}
|
|
21098
|
+
};
|
|
21099
|
+
var lineDiff = new LineDiff();
|
|
21100
|
+
function diffLines(oldStr, newStr, options) {
|
|
21101
|
+
return lineDiff.diff(oldStr, newStr, options);
|
|
21102
|
+
}
|
|
21103
|
+
function tokenize(value, options) {
|
|
21104
|
+
if (options.stripTrailingCr) {
|
|
21105
|
+
value = value.replace(/\r\n/g, "\n");
|
|
21106
|
+
}
|
|
21107
|
+
const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
|
|
21108
|
+
if (!linesAndNewlines[linesAndNewlines.length - 1]) {
|
|
21109
|
+
linesAndNewlines.pop();
|
|
21110
|
+
}
|
|
21111
|
+
for (let i = 0; i < linesAndNewlines.length; i++) {
|
|
21112
|
+
const line = linesAndNewlines[i];
|
|
21113
|
+
if (i % 2 && !options.newlineIsToken) {
|
|
21114
|
+
retLines[retLines.length - 1] += line;
|
|
21115
|
+
} else {
|
|
21116
|
+
retLines.push(line);
|
|
21117
|
+
}
|
|
21118
|
+
}
|
|
21119
|
+
return retLines;
|
|
21120
|
+
}
|
|
21121
|
+
|
|
21122
|
+
// ../../../node_modules/diff/libesm/patch/create.js
|
|
21123
|
+
function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
21124
|
+
let optionsObj;
|
|
21125
|
+
if (!options) {
|
|
21126
|
+
optionsObj = {};
|
|
21127
|
+
} else if (typeof options === "function") {
|
|
21128
|
+
optionsObj = { callback: options };
|
|
21129
|
+
} else {
|
|
21130
|
+
optionsObj = options;
|
|
21131
|
+
}
|
|
21132
|
+
if (typeof optionsObj.context === "undefined") {
|
|
21133
|
+
optionsObj.context = 4;
|
|
21134
|
+
}
|
|
21135
|
+
const context = optionsObj.context;
|
|
21136
|
+
if (optionsObj.newlineIsToken) {
|
|
21137
|
+
throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
|
|
21138
|
+
}
|
|
21139
|
+
if (!optionsObj.callback) {
|
|
21140
|
+
return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
|
|
21141
|
+
} else {
|
|
21142
|
+
const { callback } = optionsObj;
|
|
21143
|
+
diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
|
|
21144
|
+
const patch = diffLinesResultToPatch(diff);
|
|
21145
|
+
callback(patch);
|
|
21146
|
+
} }));
|
|
21147
|
+
}
|
|
21148
|
+
function diffLinesResultToPatch(diff) {
|
|
21149
|
+
if (!diff) {
|
|
21150
|
+
return;
|
|
21151
|
+
}
|
|
21152
|
+
diff.push({ value: "", lines: [] });
|
|
21153
|
+
function contextLines(lines) {
|
|
21154
|
+
return lines.map(function(entry) {
|
|
21155
|
+
return " " + entry;
|
|
21156
|
+
});
|
|
21157
|
+
}
|
|
21158
|
+
const hunks = [];
|
|
21159
|
+
let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
|
|
21160
|
+
for (let i = 0; i < diff.length; i++) {
|
|
21161
|
+
const current = diff[i], lines = current.lines || splitLines(current.value);
|
|
21162
|
+
current.lines = lines;
|
|
21163
|
+
if (current.added || current.removed) {
|
|
21164
|
+
if (!oldRangeStart) {
|
|
21165
|
+
const prev = diff[i - 1];
|
|
21166
|
+
oldRangeStart = oldLine;
|
|
21167
|
+
newRangeStart = newLine;
|
|
21168
|
+
if (prev) {
|
|
21169
|
+
curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
|
|
21170
|
+
oldRangeStart -= curRange.length;
|
|
21171
|
+
newRangeStart -= curRange.length;
|
|
21172
|
+
}
|
|
21173
|
+
}
|
|
21174
|
+
for (const line of lines) {
|
|
21175
|
+
curRange.push((current.added ? "+" : "-") + line);
|
|
21176
|
+
}
|
|
21177
|
+
if (current.added) {
|
|
21178
|
+
newLine += lines.length;
|
|
21179
|
+
} else {
|
|
21180
|
+
oldLine += lines.length;
|
|
21181
|
+
}
|
|
21182
|
+
} else {
|
|
21183
|
+
if (oldRangeStart) {
|
|
21184
|
+
if (lines.length <= context * 2 && i < diff.length - 2) {
|
|
21185
|
+
for (const line of contextLines(lines)) {
|
|
21186
|
+
curRange.push(line);
|
|
21187
|
+
}
|
|
21188
|
+
} else {
|
|
21189
|
+
const contextSize = Math.min(lines.length, context);
|
|
21190
|
+
for (const line of contextLines(lines.slice(0, contextSize))) {
|
|
21191
|
+
curRange.push(line);
|
|
21192
|
+
}
|
|
21193
|
+
const hunk = {
|
|
21194
|
+
oldStart: oldRangeStart,
|
|
21195
|
+
oldLines: oldLine - oldRangeStart + contextSize,
|
|
21196
|
+
newStart: newRangeStart,
|
|
21197
|
+
newLines: newLine - newRangeStart + contextSize,
|
|
21198
|
+
lines: curRange
|
|
21199
|
+
};
|
|
21200
|
+
hunks.push(hunk);
|
|
21201
|
+
oldRangeStart = 0;
|
|
21202
|
+
newRangeStart = 0;
|
|
21203
|
+
curRange = [];
|
|
21204
|
+
}
|
|
21205
|
+
}
|
|
21206
|
+
oldLine += lines.length;
|
|
21207
|
+
newLine += lines.length;
|
|
21208
|
+
}
|
|
21209
|
+
}
|
|
21210
|
+
for (const hunk of hunks) {
|
|
21211
|
+
for (let i = 0; i < hunk.lines.length; i++) {
|
|
21212
|
+
if (hunk.lines[i].endsWith("\n")) {
|
|
21213
|
+
hunk.lines[i] = hunk.lines[i].slice(0, -1);
|
|
21214
|
+
} else {
|
|
21215
|
+
hunk.lines.splice(i + 1, 0, "\");
|
|
21216
|
+
i++;
|
|
21217
|
+
}
|
|
21218
|
+
}
|
|
21219
|
+
}
|
|
21220
|
+
return {
|
|
21221
|
+
oldFileName,
|
|
21222
|
+
newFileName,
|
|
21223
|
+
oldHeader,
|
|
21224
|
+
newHeader,
|
|
21225
|
+
hunks
|
|
21226
|
+
};
|
|
21227
|
+
}
|
|
21228
|
+
}
|
|
21229
|
+
function formatPatch(patch) {
|
|
21230
|
+
if (Array.isArray(patch)) {
|
|
21231
|
+
return patch.map(formatPatch).join("\n");
|
|
21232
|
+
}
|
|
21233
|
+
const ret = [];
|
|
21234
|
+
if (patch.oldFileName == patch.newFileName) {
|
|
21235
|
+
ret.push("Index: " + patch.oldFileName);
|
|
21236
|
+
}
|
|
21237
|
+
ret.push("===================================================================");
|
|
21238
|
+
ret.push("--- " + patch.oldFileName + (typeof patch.oldHeader === "undefined" ? "" : " " + patch.oldHeader));
|
|
21239
|
+
ret.push("+++ " + patch.newFileName + (typeof patch.newHeader === "undefined" ? "" : " " + patch.newHeader));
|
|
21240
|
+
for (let i = 0; i < patch.hunks.length; i++) {
|
|
21241
|
+
const hunk = patch.hunks[i];
|
|
21242
|
+
if (hunk.oldLines === 0) {
|
|
21243
|
+
hunk.oldStart -= 1;
|
|
21244
|
+
}
|
|
21245
|
+
if (hunk.newLines === 0) {
|
|
21246
|
+
hunk.newStart -= 1;
|
|
21247
|
+
}
|
|
21248
|
+
ret.push("@@ -" + hunk.oldStart + "," + hunk.oldLines + " +" + hunk.newStart + "," + hunk.newLines + " @@");
|
|
21249
|
+
for (const line of hunk.lines) {
|
|
21250
|
+
ret.push(line);
|
|
21251
|
+
}
|
|
21252
|
+
}
|
|
21253
|
+
return ret.join("\n") + "\n";
|
|
21254
|
+
}
|
|
21255
|
+
function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
21256
|
+
if (typeof options === "function") {
|
|
21257
|
+
options = { callback: options };
|
|
21258
|
+
}
|
|
21259
|
+
if (!(options === null || options === void 0 ? void 0 : options.callback)) {
|
|
21260
|
+
const patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
|
|
21261
|
+
if (!patchObj) {
|
|
21262
|
+
return;
|
|
21263
|
+
}
|
|
21264
|
+
return formatPatch(patchObj);
|
|
21265
|
+
} else {
|
|
21266
|
+
const { callback } = options;
|
|
21267
|
+
structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: (patchObj) => {
|
|
21268
|
+
if (!patchObj) {
|
|
21269
|
+
callback(void 0);
|
|
21270
|
+
} else {
|
|
21271
|
+
callback(formatPatch(patchObj));
|
|
21272
|
+
}
|
|
21273
|
+
} }));
|
|
21274
|
+
}
|
|
21275
|
+
}
|
|
21276
|
+
function splitLines(text2) {
|
|
21277
|
+
const hasTrailingNl = text2.endsWith("\n");
|
|
21278
|
+
const result = text2.split("\n").map((line) => line + "\n");
|
|
21279
|
+
if (hasTrailingNl) {
|
|
21280
|
+
result.pop();
|
|
21281
|
+
} else {
|
|
21282
|
+
result.push(result.pop().slice(0, -1));
|
|
21283
|
+
}
|
|
21284
|
+
return result;
|
|
21285
|
+
}
|
|
21286
|
+
|
|
21287
|
+
// dist/hermes-file-changes.js
|
|
21288
|
+
var MAX_DIFF_BYTES = 1024 * 1024;
|
|
21289
|
+
var MAX_DIFFS = 32;
|
|
21290
|
+
function hermesFileChanges(update) {
|
|
21291
|
+
const changes = [];
|
|
21292
|
+
let bytes = 0;
|
|
21293
|
+
for (const block of update.content ?? []) {
|
|
21294
|
+
if (block.type !== "diff")
|
|
21295
|
+
continue;
|
|
21296
|
+
const { path: path7, oldText, newText } = block;
|
|
21297
|
+
if (!path7.trim() || /[\0\r\n]/u.test(path7) || typeof newText !== "string")
|
|
21298
|
+
continue;
|
|
21299
|
+
if (oldText === newText)
|
|
21300
|
+
continue;
|
|
21301
|
+
bytes += Buffer.byteLength(oldText ?? "") + Buffer.byteLength(newText);
|
|
21302
|
+
if (bytes > MAX_DIFF_BYTES || changes.length === MAX_DIFFS)
|
|
21303
|
+
return [];
|
|
21304
|
+
changes.push({
|
|
21305
|
+
path: path7,
|
|
21306
|
+
kind: "update",
|
|
21307
|
+
diffScope: "fragment",
|
|
21308
|
+
unifiedDiff: createTwoFilesPatch(path7, path7, oldText ?? "", newText, "", "", {
|
|
21309
|
+
context: 3
|
|
21310
|
+
})
|
|
21311
|
+
});
|
|
21312
|
+
}
|
|
21313
|
+
return changes;
|
|
21314
|
+
}
|
|
21315
|
+
function hermesToolOutput(update) {
|
|
21316
|
+
const content = [];
|
|
21317
|
+
for (const block of update.content ?? []) {
|
|
21318
|
+
if (block.type !== "content")
|
|
21319
|
+
continue;
|
|
21320
|
+
if (block.content.type === "text" && block.content.text) {
|
|
21321
|
+
content.push({ type: "text", text: block.content.text });
|
|
21322
|
+
} else if (block.content.type === "image") {
|
|
21323
|
+
content.push({
|
|
21324
|
+
type: "image",
|
|
21325
|
+
mimeType: block.content.mimeType,
|
|
21326
|
+
base64Data: block.content.data
|
|
21327
|
+
});
|
|
21328
|
+
}
|
|
21329
|
+
}
|
|
21330
|
+
if (!content.length && typeof update.rawOutput === "string" && update.rawOutput) {
|
|
21331
|
+
content.push({ type: "text", text: update.rawOutput });
|
|
21332
|
+
}
|
|
21333
|
+
return content.length ? { content } : null;
|
|
21334
|
+
}
|
|
21335
|
+
|
|
21336
|
+
// dist/hermes-compaction.js
|
|
21337
|
+
function hermesCompactionOutcome(outcome, nativeText) {
|
|
21338
|
+
if (outcome.status !== "succeeded")
|
|
21339
|
+
return outcome;
|
|
21340
|
+
const text2 = nativeText.trim();
|
|
21341
|
+
if (/^Context compressed: \d+ -> \d+ messages\n~[\d,]+ -> ~[\d,]+ tokens$/u.test(text2)) {
|
|
21342
|
+
return { status: "succeeded" };
|
|
21343
|
+
}
|
|
21344
|
+
if (/^(?:Compression failed:|Error executing \/compress:|Context compression not available)/u.test(text2)) {
|
|
21345
|
+
return { status: "failed", error: { code: "nativeFailure", message: text2, retryable: false } };
|
|
21346
|
+
}
|
|
21347
|
+
return { status: "cancelled", reason: text2 || "Hermes did not confirm context compression" };
|
|
21348
|
+
}
|
|
21349
|
+
|
|
21350
|
+
// dist/hermes-session.js
|
|
20608
21351
|
var HOST_ERROR_CODES = {
|
|
20609
21352
|
notInstalled: "notInstalled",
|
|
20610
21353
|
authenticationRequired: "authenticationRequired",
|
|
@@ -20647,7 +21390,7 @@ function usageFromContext(used, size) {
|
|
|
20647
21390
|
...used !== void 0 ? { contextUsedTokens: used } : {}
|
|
20648
21391
|
};
|
|
20649
21392
|
}
|
|
20650
|
-
function projectPermissionOptions(options) {
|
|
21393
|
+
function projectPermissionOptions(options, effects) {
|
|
20651
21394
|
const actionEffects = {
|
|
20652
21395
|
allow_once: "allowOnce",
|
|
20653
21396
|
allow_always: "allowAlways",
|
|
@@ -20663,7 +21406,7 @@ function projectPermissionOptions(options) {
|
|
|
20663
21406
|
const actions = [];
|
|
20664
21407
|
const optionIdByAction = /* @__PURE__ */ new Map();
|
|
20665
21408
|
for (const option of options) {
|
|
20666
|
-
const effect = actionEffects[option.kind];
|
|
21409
|
+
const effect = effects?.[option.optionId] ?? actionEffects[option.kind];
|
|
20667
21410
|
if (!effect)
|
|
20668
21411
|
continue;
|
|
20669
21412
|
actions.push({
|
|
@@ -20679,18 +21422,47 @@ var ActiveTurn = class {
|
|
|
20679
21422
|
turnId;
|
|
20680
21423
|
turnKey;
|
|
20681
21424
|
input;
|
|
21425
|
+
persistsHistory;
|
|
21426
|
+
compactionItem;
|
|
21427
|
+
compactionText = "";
|
|
21428
|
+
nativeCompactionOutcome;
|
|
21429
|
+
nativeTurnSnapshot;
|
|
20682
21430
|
#currentText = null;
|
|
20683
21431
|
#toolItems = /* @__PURE__ */ new Map();
|
|
20684
21432
|
#finishedItems = [];
|
|
21433
|
+
#toolChanges = /* @__PURE__ */ new Map();
|
|
20685
21434
|
#emittedTerminalItemIds = /* @__PURE__ */ new Set();
|
|
20686
|
-
|
|
21435
|
+
rememberFileChanges(toolCallId, update) {
|
|
21436
|
+
const changes = hermesFileChanges(update);
|
|
21437
|
+
if (update.content?.some((block) => block.type === "diff"))
|
|
21438
|
+
this.#toolChanges.set(toolCallId, changes);
|
|
21439
|
+
}
|
|
21440
|
+
completeFileChanges(toolCallId, sourceItemId) {
|
|
21441
|
+
const changes = this.#toolChanges.get(toolCallId);
|
|
21442
|
+
this.#toolChanges.delete(toolCallId);
|
|
21443
|
+
if (!changes?.length)
|
|
21444
|
+
return null;
|
|
21445
|
+
const item = {
|
|
21446
|
+
type: "fileChange",
|
|
21447
|
+
itemId: hostItemIdSchema.parse(randomUUID2()),
|
|
21448
|
+
changes,
|
|
21449
|
+
sourceItemIds: [sourceItemId]
|
|
21450
|
+
};
|
|
21451
|
+
const snapshot = { item, outcome: { status: "succeeded" } };
|
|
21452
|
+
this.#finishedItems.push(snapshot);
|
|
21453
|
+
this.#emittedTerminalItemIds.add(item.itemId);
|
|
21454
|
+
return snapshot;
|
|
21455
|
+
}
|
|
21456
|
+
constructor(turnId, turnKey, input, persistsHistory, compaction) {
|
|
20687
21457
|
this.turnId = hostTurnIdSchema.parse(turnId);
|
|
20688
21458
|
this.turnKey = turnKey;
|
|
20689
21459
|
this.input = input;
|
|
21460
|
+
this.persistsHistory = persistsHistory;
|
|
21461
|
+
this.compactionItem = compaction ? { type: "contextCompaction", itemId: hostItemIdSchema.parse(randomUUID2()) } : null;
|
|
20690
21462
|
}
|
|
20691
|
-
appendText(kind,
|
|
21463
|
+
appendText(kind, text2) {
|
|
20692
21464
|
if (this.#currentText && this.#currentText.kind === kind) {
|
|
20693
|
-
const item2 = { ...this.#currentText.item, text: this.#currentText.item.text +
|
|
21465
|
+
const item2 = { ...this.#currentText.item, text: this.#currentText.item.text + text2 };
|
|
20694
21466
|
this.#currentText = { kind, item: item2 };
|
|
20695
21467
|
return { startedItem: null, item: item2 };
|
|
20696
21468
|
}
|
|
@@ -20700,8 +21472,8 @@ var ActiveTurn = class {
|
|
|
20700
21472
|
outcome: { status: "succeeded" }
|
|
20701
21473
|
});
|
|
20702
21474
|
}
|
|
20703
|
-
const item = kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(
|
|
20704
|
-
const appended = { ...item, text };
|
|
21475
|
+
const item = kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(randomUUID2()), text: "" } : { type: "agentMessage", itemId: hostItemIdSchema.parse(randomUUID2()), text: "" };
|
|
21476
|
+
const appended = { ...item, text: text2 };
|
|
20705
21477
|
this.#currentText = { kind, item: appended };
|
|
20706
21478
|
return { startedItem: item, item: appended };
|
|
20707
21479
|
}
|
|
@@ -20759,6 +21531,13 @@ var ActiveTurn = class {
|
|
|
20759
21531
|
}
|
|
20760
21532
|
this.#toolItems.clear();
|
|
20761
21533
|
}
|
|
21534
|
+
finishCompaction(outcome) {
|
|
21535
|
+
if (!this.compactionItem)
|
|
21536
|
+
return null;
|
|
21537
|
+
const itemOutcome = outcome.status === "succeeded" && this.nativeCompactionOutcome ? this.nativeCompactionOutcome : hermesCompactionOutcome(outcome, this.compactionText);
|
|
21538
|
+
this.#finishedItems.push({ item: this.compactionItem, outcome: itemOutcome });
|
|
21539
|
+
return itemOutcome;
|
|
21540
|
+
}
|
|
20762
21541
|
drainPendingItems() {
|
|
20763
21542
|
const pending = this.#finishedItems.filter((snapshot) => !this.#emittedTerminalItemIds.has(snapshot.item.itemId));
|
|
20764
21543
|
this.#finishedItems = [];
|
|
@@ -20777,25 +21556,30 @@ var ActiveTurn = class {
|
|
|
20777
21556
|
function isPlainObjectOrArray(value) {
|
|
20778
21557
|
return typeof value === "object" && value !== null;
|
|
20779
21558
|
}
|
|
20780
|
-
function toolOutputFromUpdate(update) {
|
|
20781
|
-
const content = [];
|
|
20782
|
-
if (Array.isArray(update.content)) {
|
|
20783
|
-
for (const block of update.content) {
|
|
20784
|
-
const candidate = block;
|
|
20785
|
-
if (candidate?.type === "text" && typeof candidate.text === "string" && candidate.text.length > 0) {
|
|
20786
|
-
content.push({ type: "text", text: candidate.text });
|
|
20787
|
-
}
|
|
20788
|
-
}
|
|
20789
|
-
}
|
|
20790
|
-
if (content.length === 0 && typeof update.rawOutput === "string" && update.rawOutput.length > 0) {
|
|
20791
|
-
content.push({ type: "text", text: update.rawOutput });
|
|
20792
|
-
}
|
|
20793
|
-
return content.length > 0 ? { content } : null;
|
|
20794
|
-
}
|
|
20795
21559
|
function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
20796
21560
|
const turns = [];
|
|
20797
21561
|
let current = null;
|
|
20798
21562
|
let toolItemIndexes = /* @__PURE__ */ new Map();
|
|
21563
|
+
const toolChanges = /* @__PURE__ */ new Map();
|
|
21564
|
+
const appendFileChanges = (toolCallId, update, sourceItemId) => {
|
|
21565
|
+
const changes = hermesFileChanges(update);
|
|
21566
|
+
if (update.content?.some((block) => block.type === "diff"))
|
|
21567
|
+
toolChanges.set(toolCallId, changes);
|
|
21568
|
+
const confirmedChanges = toolChanges.get(toolCallId);
|
|
21569
|
+
if (current && update.status === "completed" && confirmedChanges?.length) {
|
|
21570
|
+
current.items.push({
|
|
21571
|
+
item: {
|
|
21572
|
+
type: "fileChange",
|
|
21573
|
+
itemId: hostItemIdSchema.parse(randomUUID2()),
|
|
21574
|
+
changes: confirmedChanges,
|
|
21575
|
+
sourceItemIds: [sourceItemId]
|
|
21576
|
+
},
|
|
21577
|
+
outcome: { status: "succeeded" }
|
|
21578
|
+
});
|
|
21579
|
+
}
|
|
21580
|
+
if (update.status === "completed" || update.status === "failed")
|
|
21581
|
+
toolChanges.delete(toolCallId);
|
|
21582
|
+
};
|
|
20799
21583
|
const closeTurn = () => {
|
|
20800
21584
|
if (!current || current.items.length === 0) {
|
|
20801
21585
|
current = null;
|
|
@@ -20819,21 +21603,22 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
|
20819
21603
|
});
|
|
20820
21604
|
current = null;
|
|
20821
21605
|
toolItemIndexes = /* @__PURE__ */ new Map();
|
|
21606
|
+
toolChanges.clear();
|
|
20822
21607
|
};
|
|
20823
|
-
const pushText = (kind,
|
|
21608
|
+
const pushText = (kind, text2) => {
|
|
20824
21609
|
if (!current)
|
|
20825
21610
|
current = { inputText: "(resumed)", items: [], turnKey: `history-${turns.length + 1}` };
|
|
20826
21611
|
const last = current.items.at(-1);
|
|
20827
21612
|
if (last && last.item.type === kind) {
|
|
20828
21613
|
if (kind === "reasoning") {
|
|
20829
|
-
last.item.text +=
|
|
21614
|
+
last.item.text += text2;
|
|
20830
21615
|
} else {
|
|
20831
|
-
last.item.text +=
|
|
21616
|
+
last.item.text += text2;
|
|
20832
21617
|
}
|
|
20833
21618
|
return;
|
|
20834
21619
|
}
|
|
20835
21620
|
current.items.push({
|
|
20836
|
-
item: kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(
|
|
21621
|
+
item: kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(randomUUID2()), text: text2 } : { type: "agentMessage", itemId: hostItemIdSchema.parse(randomUUID2()), text: text2 },
|
|
20837
21622
|
outcome: { status: "succeeded" }
|
|
20838
21623
|
});
|
|
20839
21624
|
};
|
|
@@ -20858,12 +21643,12 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
|
20858
21643
|
if (!current)
|
|
20859
21644
|
current = { inputText: "(resumed)", items: [], turnKey: `history-${turns.length + 1}` };
|
|
20860
21645
|
const update = event.update;
|
|
20861
|
-
const output =
|
|
21646
|
+
const output = hermesToolOutput(update);
|
|
20862
21647
|
const toolName = typeof update.title === "string" && update.title.trim() || typeof update.name === "string" && update.name.trim() || event.toolCallId;
|
|
20863
21648
|
current.items.push({
|
|
20864
21649
|
item: {
|
|
20865
21650
|
type: "toolExecution",
|
|
20866
|
-
itemId: hostItemIdSchema.parse(
|
|
21651
|
+
itemId: hostItemIdSchema.parse(randomUUID2()),
|
|
20867
21652
|
toolName,
|
|
20868
21653
|
arguments: isPlainObjectOrArray(update.rawInput) ? update.rawInput : null,
|
|
20869
21654
|
...output ? { output } : {}
|
|
@@ -20874,6 +21659,9 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
|
20874
21659
|
} : { status: "cancelled", reason: "Turn ended before terminal tool update" }
|
|
20875
21660
|
});
|
|
20876
21661
|
toolItemIndexes.set(event.toolCallId, current.items.length - 1);
|
|
21662
|
+
const sourceItemId = current.items.at(-1)?.item.itemId;
|
|
21663
|
+
if (sourceItemId)
|
|
21664
|
+
appendFileChanges(event.toolCallId, update, sourceItemId);
|
|
20877
21665
|
break;
|
|
20878
21666
|
}
|
|
20879
21667
|
case "tool.update": {
|
|
@@ -20886,7 +21674,7 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
|
20886
21674
|
if (!prior || prior.item.type !== "toolExecution")
|
|
20887
21675
|
break;
|
|
20888
21676
|
const update = event.update;
|
|
20889
|
-
const output =
|
|
21677
|
+
const output = hermesToolOutput(update);
|
|
20890
21678
|
current.items[itemIndex] = {
|
|
20891
21679
|
item: { ...prior.item, ...output ? { output } : {} },
|
|
20892
21680
|
outcome: update.status === "completed" ? { status: "succeeded" } : update.status === "failed" ? {
|
|
@@ -20894,6 +21682,7 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
|
20894
21682
|
error: harnessError("nativeFailure", "Tool execution failed")
|
|
20895
21683
|
} : prior.outcome
|
|
20896
21684
|
};
|
|
21685
|
+
appendFileChanges(event.toolCallId, update, prior.item.itemId);
|
|
20897
21686
|
break;
|
|
20898
21687
|
}
|
|
20899
21688
|
default:
|
|
@@ -20932,6 +21721,7 @@ var HermesSession = class {
|
|
|
20932
21721
|
initialState;
|
|
20933
21722
|
initialUsage;
|
|
20934
21723
|
outputs;
|
|
21724
|
+
commands;
|
|
20935
21725
|
#channel = new HarnessOutputChannel();
|
|
20936
21726
|
#transport;
|
|
20937
21727
|
#nativeRef;
|
|
@@ -20945,16 +21735,37 @@ var HermesSession = class {
|
|
|
20945
21735
|
#completedTurns = [];
|
|
20946
21736
|
#historyTurns;
|
|
20947
21737
|
#latestUsage;
|
|
21738
|
+
#questions = new HermesQuestions((output) => this.#channel.emit(output));
|
|
20948
21739
|
#approvalWaiters = /* @__PURE__ */ new Map();
|
|
20949
21740
|
constructor(options) {
|
|
20950
21741
|
this.#transport = options.transport;
|
|
21742
|
+
this.capabilities.history.fork = options.supportsDerivation === true;
|
|
21743
|
+
this.capabilities.history.rollbackLastTurn = options.supportsDerivation === true;
|
|
21744
|
+
this.commands = {
|
|
21745
|
+
list: async () => this.#closed || this.#faulted ? err("invalidState", "Hermes Session is unavailable") : ok(hermesCommandCatalog(this.#transport.availableCommands ?? [])),
|
|
21746
|
+
execute: async (command) => {
|
|
21747
|
+
const text2 = hermesCommandText(command, hermesCommandCatalog(this.#transport.availableCommands ?? []));
|
|
21748
|
+
if (!text2.ok)
|
|
21749
|
+
return text2;
|
|
21750
|
+
return this.execute({
|
|
21751
|
+
type: "turn.start",
|
|
21752
|
+
turnId: command.turnId,
|
|
21753
|
+
input: [{ type: "text", text: text2.value }]
|
|
21754
|
+
});
|
|
21755
|
+
}
|
|
21756
|
+
};
|
|
20951
21757
|
this.#nativeRef = options.nativeRef;
|
|
20952
21758
|
this.#onSettle = options.onSettle;
|
|
20953
21759
|
const projected = projectHermesModelState(options.open.session.models);
|
|
20954
21760
|
const modes = options.open.session.modes;
|
|
20955
21761
|
this.#availableModels = options.open.session.models?.availableModels ?? [];
|
|
21762
|
+
this.capabilities.configuration.selectThinkingOption = !!this.#transport.setThinking;
|
|
20956
21763
|
this.#state = {
|
|
20957
21764
|
nativeRef: this.#nativeRef,
|
|
21765
|
+
...options.open.session.thinkingOptions ? { availableThinkingOptions: options.open.session.thinkingOptions } : {},
|
|
21766
|
+
...options.open.session.currentThinkingOptionId ? {
|
|
21767
|
+
effectiveThinkingOptionId: harnessThinkingOptionIdSchema.parse(options.open.session.currentThinkingOptionId)
|
|
21768
|
+
} : {},
|
|
20958
21769
|
...projected.effectiveModel ? { effectiveModel: projected.effectiveModel } : {},
|
|
20959
21770
|
...projected.resolvedModelLabel ? { resolvedModelLabel: projected.resolvedModelLabel } : {},
|
|
20960
21771
|
...modes ? { effectivePermissionModeId: harnessPermissionModeIdSchema.parse(modes.currentModeId) } : {}
|
|
@@ -20966,9 +21777,19 @@ var HermesSession = class {
|
|
|
20966
21777
|
this.outputs = this.#channel.outputs;
|
|
20967
21778
|
this.#transport.onFault = (error51) => this.#fault(transportErrorToHarness(error51));
|
|
20968
21779
|
}
|
|
21780
|
+
get busy() {
|
|
21781
|
+
return this.#activeTurn !== null;
|
|
21782
|
+
}
|
|
20969
21783
|
async readSnapshot() {
|
|
20970
21784
|
if (this.#closed)
|
|
20971
21785
|
return err("invalidState", "Hermes Session is closed");
|
|
21786
|
+
if (this.#transport.readNativeSnapshot) {
|
|
21787
|
+
try {
|
|
21788
|
+
return ok({ ...await this.#transport.readNativeSnapshot(), state: { ...this.#state } });
|
|
21789
|
+
} catch (error51) {
|
|
21790
|
+
return err("nativeFailure", error51 instanceof Error ? error51.message : String(error51));
|
|
21791
|
+
}
|
|
21792
|
+
}
|
|
20972
21793
|
return ok({
|
|
20973
21794
|
turns: [...this.#historyTurns, ...this.#completedTurns],
|
|
20974
21795
|
state: { ...this.#state }
|
|
@@ -20984,11 +21805,11 @@ var HermesSession = class {
|
|
|
20984
21805
|
case "turn.cancel":
|
|
20985
21806
|
return this.#cancelTurn(command);
|
|
20986
21807
|
case "interaction.respond":
|
|
20987
|
-
return Promise.resolve(this.#respondToApproval(command));
|
|
21808
|
+
return Promise.resolve(command.response.type === "question" ? this.#questions.respond(command) : this.#respondToApproval(command));
|
|
20988
21809
|
case "model.select":
|
|
20989
21810
|
return this.#selectModel(command);
|
|
20990
21811
|
case "thinking.select":
|
|
20991
|
-
return
|
|
21812
|
+
return this.#selectThinking(command);
|
|
20992
21813
|
case "permissionMode.select":
|
|
20993
21814
|
return this.#selectPermissionMode(command);
|
|
20994
21815
|
default: {
|
|
@@ -21002,13 +21823,14 @@ var HermesSession = class {
|
|
|
21002
21823
|
return;
|
|
21003
21824
|
this.#closed = true;
|
|
21004
21825
|
this.#cancelApprovalWaiters();
|
|
21005
|
-
this.#activeTurn
|
|
21006
|
-
|
|
21826
|
+
if (this.#activeTurn)
|
|
21827
|
+
this.#completeActiveTurn(this.#activeTurn, { status: "cancelled", reason: "Session closed" });
|
|
21007
21828
|
await this.#transport.close().catch(() => void 0);
|
|
21008
21829
|
this.#channel.end();
|
|
21009
21830
|
this.#onSettle(this);
|
|
21010
21831
|
}
|
|
21011
21832
|
#cancelApprovalWaiters() {
|
|
21833
|
+
this.#questions.cancel();
|
|
21012
21834
|
for (const [interactionId, waiter] of this.#approvalWaiters) {
|
|
21013
21835
|
waiter.resolve({ outcome: { outcome: "cancelled" } });
|
|
21014
21836
|
this.#emit({
|
|
@@ -21042,23 +21864,31 @@ var HermesSession = class {
|
|
|
21042
21864
|
if (this.#activeTurn) {
|
|
21043
21865
|
return err("sessionBusy", "Hermes Session already has an active Turn", true);
|
|
21044
21866
|
}
|
|
21045
|
-
const
|
|
21046
|
-
if (
|
|
21867
|
+
const text2 = command.input.map((chunk) => chunk.text).join("\n");
|
|
21868
|
+
if (text2.trim().length === 0) {
|
|
21047
21869
|
return err("invalidRequest", "turn.start requires non-empty text input");
|
|
21048
21870
|
}
|
|
21049
|
-
const turnKey =
|
|
21050
|
-
const
|
|
21871
|
+
const turnKey = randomUUID2();
|
|
21872
|
+
const firstToken = text2.trim().split(/\s/u)[0]?.replace(/^\/+/u, "").toLowerCase();
|
|
21873
|
+
const nativeCommand = this.#transport.nativeCommandName ? this.#transport.nativeCommandName(text2) : text2.trimStart().startsWith("/") && (this.#transport.availableCommands ?? []).some((entry) => entry.name === firstToken) ? firstToken : null;
|
|
21874
|
+
const isNativeCommand = nativeCommand != null;
|
|
21875
|
+
const active = new ActiveTurn(command.turnId, turnKey, command.input, !isNativeCommand, isNativeCommand && nativeCommand === "compress");
|
|
21051
21876
|
this.#activeTurn = active;
|
|
21052
21877
|
this.#activeTurnId = active.turnId;
|
|
21053
|
-
void this.#runTurn(active,
|
|
21878
|
+
void this.#runTurn(active, text2);
|
|
21054
21879
|
return ok({ turnId: active.turnId });
|
|
21055
21880
|
}
|
|
21056
|
-
async #runTurn(active,
|
|
21881
|
+
async #runTurn(active, text2) {
|
|
21057
21882
|
this.#emit({ type: "turn.started", turnId: active.turnId });
|
|
21883
|
+
if (active.compactionItem)
|
|
21884
|
+
this.#emit({ type: "item.started", turnId: active.turnId, item: active.compactionItem });
|
|
21058
21885
|
let promptResponse = null;
|
|
21059
21886
|
let failure2 = null;
|
|
21887
|
+
let previousNativeTurnKey;
|
|
21060
21888
|
try {
|
|
21061
|
-
|
|
21889
|
+
if (active.persistsHistory && this.#transport.readNativeSnapshot)
|
|
21890
|
+
previousNativeTurnKey = (await this.#transport.readNativeSnapshot()).turns.at(-1)?.nativeTurnRef.nativeTurnKey;
|
|
21891
|
+
promptResponse = await this.#transport.runTurn(text2, (event) => this.#handleTransportEvent(active, event), (request) => this.#handlePermissionRequest(active, request), (request) => this.#handleQuestionRequest(active, request));
|
|
21062
21892
|
} catch (error51) {
|
|
21063
21893
|
failure2 = error51 instanceof HermesTransportError ? transportErrorToHarness(error51) : harnessError("nativeFailure", error51 instanceof Error ? error51.message : String(error51));
|
|
21064
21894
|
}
|
|
@@ -21078,6 +21908,19 @@ var HermesSession = class {
|
|
|
21078
21908
|
} else {
|
|
21079
21909
|
outcome = { status: "succeeded" };
|
|
21080
21910
|
}
|
|
21911
|
+
active.nativeCompactionOutcome = promptResponse?.compactionOutcome;
|
|
21912
|
+
if (active.persistsHistory && this.#transport.readNativeSnapshot) {
|
|
21913
|
+
try {
|
|
21914
|
+
const latest = (await this.#transport.readNativeSnapshot()).turns.at(-1);
|
|
21915
|
+
if (latest?.nativeTurnRef.nativeTurnKey !== previousNativeTurnKey)
|
|
21916
|
+
active.nativeTurnSnapshot = latest;
|
|
21917
|
+
} catch (error51) {
|
|
21918
|
+
outcome = {
|
|
21919
|
+
status: "failed",
|
|
21920
|
+
error: harnessError("nativeFailure", error51 instanceof Error ? error51.message : String(error51))
|
|
21921
|
+
};
|
|
21922
|
+
}
|
|
21923
|
+
}
|
|
21081
21924
|
const terminalUsage = promptResponse ? usageFromPromptResponse(promptResponse.usage) : null;
|
|
21082
21925
|
const usage = terminalUsage ? this.#mergeUsage(terminalUsage) : null;
|
|
21083
21926
|
this.#completeActiveTurn(active, outcome, usage);
|
|
@@ -21085,10 +21928,17 @@ var HermesSession = class {
|
|
|
21085
21928
|
#completeActiveTurn(active, outcome, usage = null) {
|
|
21086
21929
|
if (this.#activeTurn !== active)
|
|
21087
21930
|
return;
|
|
21931
|
+
this.#cancelApprovalWaiters();
|
|
21088
21932
|
this.#activeTurn = null;
|
|
21089
21933
|
this.#activeTurnId = null;
|
|
21090
21934
|
active.finish();
|
|
21091
|
-
const
|
|
21935
|
+
const compactionOutcome = active.finishCompaction(outcome);
|
|
21936
|
+
if (outcome.status === "succeeded" && compactionOutcome?.status === "failed") {
|
|
21937
|
+
outcome = { status: "failed", error: compactionOutcome.error };
|
|
21938
|
+
}
|
|
21939
|
+
if (active.nativeTurnSnapshot?.checkpoint)
|
|
21940
|
+
outcome = { ...outcome, checkpoint: active.nativeTurnSnapshot.checkpoint };
|
|
21941
|
+
const nativeTurnRef = active.nativeTurnSnapshot?.nativeTurnRef ?? nativeTurnRefSchema.parse({
|
|
21092
21942
|
harnessId: this.#nativeRef.harnessId,
|
|
21093
21943
|
nativeSessionId: this.#nativeRef.nativeSessionId,
|
|
21094
21944
|
nativeTurnKey: active.turnKey,
|
|
@@ -21101,10 +21951,18 @@ var HermesSession = class {
|
|
|
21101
21951
|
if (usage) {
|
|
21102
21952
|
this.#emit({ type: "session.usage.changed", usage, observedForTurnId: active.turnId });
|
|
21103
21953
|
}
|
|
21104
|
-
this.#emit({
|
|
21105
|
-
|
|
21954
|
+
this.#emit({
|
|
21955
|
+
type: "turn.completed",
|
|
21956
|
+
turnId: active.turnId,
|
|
21957
|
+
...active.persistsHistory && (!this.#transport.readNativeSnapshot || active.nativeTurnSnapshot) ? { nativeTurnRef } : {},
|
|
21958
|
+
outcome
|
|
21959
|
+
});
|
|
21960
|
+
if (active.persistsHistory)
|
|
21961
|
+
this.#completedTurns.push(turnSnapshot);
|
|
21106
21962
|
}
|
|
21107
21963
|
#handleTransportEvent(active, event) {
|
|
21964
|
+
if (this.#activeTurn !== active)
|
|
21965
|
+
return;
|
|
21108
21966
|
switch (event.type) {
|
|
21109
21967
|
case "usage": {
|
|
21110
21968
|
const usage = usageFromContext(event.used, event.size);
|
|
@@ -21122,6 +21980,8 @@ var HermesSession = class {
|
|
|
21122
21980
|
this.#appendTextItem(active, "reasoning", event.text);
|
|
21123
21981
|
return;
|
|
21124
21982
|
case "agent.text":
|
|
21983
|
+
if (active.compactionItem)
|
|
21984
|
+
active.compactionText += event.text;
|
|
21125
21985
|
this.#appendTextItem(active, "agentMessage", event.text);
|
|
21126
21986
|
return;
|
|
21127
21987
|
case "tool.call": {
|
|
@@ -21137,8 +21997,8 @@ var HermesSession = class {
|
|
|
21137
21997
|
return;
|
|
21138
21998
|
}
|
|
21139
21999
|
}
|
|
21140
|
-
#appendTextItem(active, kind,
|
|
21141
|
-
const appended = active.appendText(kind,
|
|
22000
|
+
#appendTextItem(active, kind, text2) {
|
|
22001
|
+
const appended = active.appendText(kind, text2);
|
|
21142
22002
|
if (!appended)
|
|
21143
22003
|
return;
|
|
21144
22004
|
if (appended.startedItem) {
|
|
@@ -21148,7 +22008,7 @@ var HermesSession = class {
|
|
|
21148
22008
|
type: "item.updated",
|
|
21149
22009
|
turnId: active.turnId,
|
|
21150
22010
|
itemId: appended.item.itemId,
|
|
21151
|
-
update: { type: "text.append", text }
|
|
22011
|
+
update: { type: "text.append", text: text2 }
|
|
21152
22012
|
});
|
|
21153
22013
|
}
|
|
21154
22014
|
#startToolItem(active, toolCallId, title, name, rawInput) {
|
|
@@ -21158,7 +22018,7 @@ var HermesSession = class {
|
|
|
21158
22018
|
}
|
|
21159
22019
|
const item = {
|
|
21160
22020
|
type: "toolExecution",
|
|
21161
|
-
itemId: hostItemIdSchema.parse(
|
|
22021
|
+
itemId: hostItemIdSchema.parse(randomUUID2()),
|
|
21162
22022
|
toolName: (name ?? title ?? "").trim() || toolCallId,
|
|
21163
22023
|
arguments: isPlainObjectOrArray(rawInput) ? rawInput : null
|
|
21164
22024
|
};
|
|
@@ -21174,7 +22034,8 @@ var HermesSession = class {
|
|
|
21174
22034
|
const entry = active.getToolItem(update.toolCallId);
|
|
21175
22035
|
if (!entry)
|
|
21176
22036
|
return;
|
|
21177
|
-
|
|
22037
|
+
active.rememberFileChanges(update.toolCallId, update);
|
|
22038
|
+
const output = hermesToolOutput(update);
|
|
21178
22039
|
if (output && output.content.length > 0) {
|
|
21179
22040
|
active.updateToolOutput(update.toolCallId, output);
|
|
21180
22041
|
this.#emit({
|
|
@@ -21188,32 +22049,57 @@ var HermesSession = class {
|
|
|
21188
22049
|
const completed = active.completeToolItem(update.toolCallId, output, update.status === "failed");
|
|
21189
22050
|
if (completed) {
|
|
21190
22051
|
this.#emit({ type: "item.completed", turnId: active.turnId, snapshot: completed });
|
|
22052
|
+
if (update.status === "completed") {
|
|
22053
|
+
const fileChange = active.completeFileChanges(update.toolCallId, completed.item.itemId);
|
|
22054
|
+
if (fileChange) {
|
|
22055
|
+
this.#emit({ type: "item.started", turnId: active.turnId, item: fileChange.item });
|
|
22056
|
+
this.#emit({ type: "item.completed", turnId: active.turnId, snapshot: fileChange });
|
|
22057
|
+
}
|
|
22058
|
+
}
|
|
21191
22059
|
}
|
|
21192
22060
|
}
|
|
21193
22061
|
}
|
|
21194
22062
|
#handlePermissionRequest(active, request) {
|
|
21195
|
-
const projected = projectPermissionOptions(request.options);
|
|
22063
|
+
const projected = projectPermissionOptions(request.options, request.effects);
|
|
21196
22064
|
if (projected.actions.length === 0) {
|
|
21197
22065
|
return Promise.resolve({ outcome: { outcome: "cancelled" } });
|
|
21198
22066
|
}
|
|
21199
|
-
const interactionId = hostInteractionIdSchema.parse(
|
|
22067
|
+
const interactionId = hostInteractionIdSchema.parse(randomUUID2());
|
|
21200
22068
|
const interaction = {
|
|
21201
22069
|
type: "approval",
|
|
21202
22070
|
interactionId,
|
|
21203
22071
|
turnId: active.turnId,
|
|
21204
|
-
title: request.request.
|
|
22072
|
+
title: request.request.toolCall.title?.slice(0, 200) ?? "Hermes 请求批准",
|
|
22073
|
+
...request.description ? { description: request.description } : {},
|
|
21205
22074
|
subject: { type: "nativeAction" },
|
|
21206
22075
|
actions: projected.actions
|
|
21207
22076
|
};
|
|
21208
22077
|
this.#emitInteraction(interaction);
|
|
21209
|
-
|
|
22078
|
+
let abort = () => {
|
|
22079
|
+
};
|
|
22080
|
+
const pending = new Promise((resolve) => {
|
|
21210
22081
|
this.#approvalWaiters.set(interactionId, {
|
|
21211
22082
|
interaction,
|
|
21212
22083
|
turnId: active.turnId,
|
|
21213
22084
|
optionIdByAction: projected.optionIdByAction,
|
|
21214
22085
|
resolve
|
|
21215
22086
|
});
|
|
22087
|
+
abort = () => {
|
|
22088
|
+
if (!this.#approvalWaiters.delete(interactionId))
|
|
22089
|
+
return;
|
|
22090
|
+
resolve({ outcome: { outcome: "cancelled" } });
|
|
22091
|
+
this.#emit({
|
|
22092
|
+
type: "interaction.closed",
|
|
22093
|
+
interactionId,
|
|
22094
|
+
turnId: active.turnId,
|
|
22095
|
+
reason: request.signal?.reason === "expired" ? "expired" : "cancelled"
|
|
22096
|
+
});
|
|
22097
|
+
};
|
|
22098
|
+
request.signal?.addEventListener("abort", abort, { once: true });
|
|
22099
|
+
if (request.signal?.aborted)
|
|
22100
|
+
abort();
|
|
21216
22101
|
});
|
|
22102
|
+
return pending.finally(() => request.signal?.removeEventListener("abort", abort));
|
|
21217
22103
|
}
|
|
21218
22104
|
#respondToApproval(command) {
|
|
21219
22105
|
if (this.#faulted) {
|
|
@@ -21254,6 +22140,7 @@ var HermesSession = class {
|
|
|
21254
22140
|
}
|
|
21255
22141
|
try {
|
|
21256
22142
|
await this.#transport.cancel();
|
|
22143
|
+
this.#questions.cancel(command.turnId);
|
|
21257
22144
|
for (const [interactionId, waiter] of this.#approvalWaiters) {
|
|
21258
22145
|
if (waiter.turnId !== command.turnId)
|
|
21259
22146
|
continue;
|
|
@@ -21272,6 +22159,28 @@ var HermesSession = class {
|
|
|
21272
22159
|
return { ok: false, error: failure2 };
|
|
21273
22160
|
}
|
|
21274
22161
|
}
|
|
22162
|
+
#handleQuestionRequest(active, request) {
|
|
22163
|
+
if (this.#activeTurn !== active)
|
|
22164
|
+
return Promise.resolve({ type: "question", answers: {}, cancelled: true });
|
|
22165
|
+
return this.#questions.open(active.turnId, request);
|
|
22166
|
+
}
|
|
22167
|
+
async #selectThinking(command) {
|
|
22168
|
+
if (!this.#transport.setThinking)
|
|
22169
|
+
return err("unsupported", "Hermes does not expose Thinking options");
|
|
22170
|
+
if (!this.#state.availableThinkingOptions?.some(({ id }) => id === command.thinkingOptionId))
|
|
22171
|
+
return err("invalidRequest", "Unknown Hermes Thinking option");
|
|
22172
|
+
try {
|
|
22173
|
+
const selected = await this.#transport.setThinking(command.thinkingOptionId);
|
|
22174
|
+
this.#state = {
|
|
22175
|
+
...this.#state,
|
|
22176
|
+
effectiveThinkingOptionId: harnessThinkingOptionIdSchema.parse(selected)
|
|
22177
|
+
};
|
|
22178
|
+
this.#emit({ type: "session.state.changed", state: { ...this.#state } });
|
|
22179
|
+
return ok({ completed: true });
|
|
22180
|
+
} catch (error51) {
|
|
22181
|
+
return err("nativeFailure", error51 instanceof Error ? error51.message : String(error51));
|
|
22182
|
+
}
|
|
22183
|
+
}
|
|
21275
22184
|
async #selectModel(command) {
|
|
21276
22185
|
const native = decodeHermesModelRefId(command.model.id);
|
|
21277
22186
|
if (!native) {
|
|
@@ -21312,8 +22221,16 @@ var HermesSession = class {
|
|
|
21312
22221
|
}
|
|
21313
22222
|
return err("nativeFailure", error51 instanceof Error ? error51.message : "Hermes rejected Permission Mode selection");
|
|
21314
22223
|
}
|
|
22224
|
+
const locator = this.#nativeRef.locator;
|
|
22225
|
+
if (locator && typeof locator === "object" && !Array.isArray(locator) && locator.transport === "gateway") {
|
|
22226
|
+
this.#nativeRef = {
|
|
22227
|
+
...this.#nativeRef,
|
|
22228
|
+
locator: { ...locator, permissionModeId: command.permissionModeId }
|
|
22229
|
+
};
|
|
22230
|
+
}
|
|
21315
22231
|
this.#state = {
|
|
21316
22232
|
...this.#state,
|
|
22233
|
+
nativeRef: this.#nativeRef,
|
|
21317
22234
|
effectivePermissionModeId: harnessPermissionModeIdSchema.parse(command.permissionModeId)
|
|
21318
22235
|
};
|
|
21319
22236
|
this.#emit({ type: "session.state.changed", state: { ...this.#state } });
|
|
@@ -21321,11 +22238,1323 @@ var HermesSession = class {
|
|
|
21321
22238
|
}
|
|
21322
22239
|
};
|
|
21323
22240
|
|
|
22241
|
+
// dist/gateway-transport.js
|
|
22242
|
+
import { spawn as spawn3, spawnSync as spawnSync2 } from "node:child_process";
|
|
22243
|
+
import { access as access2 } from "node:fs/promises";
|
|
22244
|
+
import { createInterface } from "node:readline";
|
|
22245
|
+
|
|
22246
|
+
// dist/gateway-delegation.js
|
|
22247
|
+
import { mkdtemp, writeFile, rm } from "node:fs/promises";
|
|
22248
|
+
import os2 from "node:os";
|
|
22249
|
+
import path5 from "node:path";
|
|
22250
|
+
var HERMES_DELEGATION_GUIDANCE = `This Session runs inside codexhost.
|
|
22251
|
+
When the user authorizes cross-Harness delegation, discover the executable named by CODEXHOST_CLI_PATH through your native terminal tool. Read its --help and harness list, then use delegate start and thread send/read/wait/cancel as documented. Prefer --format compact. Preserve CODEXHOST_RUNTIME_ENDPOINT, CODEXHOST_RUNTIME_TOKEN and CODEXHOST_THREAD_ID in these calls: they identify the Runtime and parent Thread. Never print their values or substitute another executable. Native Hermes delegate_task remains available for Hermes subagents.`;
|
|
22252
|
+
var required2 = [
|
|
22253
|
+
"CODEXHOST_CLI_PATH",
|
|
22254
|
+
"CODEXHOST_RUNTIME_ENDPOINT",
|
|
22255
|
+
"CODEXHOST_RUNTIME_TOKEN",
|
|
22256
|
+
"CODEXHOST_THREAD_ID"
|
|
22257
|
+
];
|
|
22258
|
+
async function prepareGatewayDelegation(environment) {
|
|
22259
|
+
if (!required2.every((key) => environment[key]))
|
|
22260
|
+
return { environment, bootstrap: "", dispose: async () => {
|
|
22261
|
+
} };
|
|
22262
|
+
const directory = await mkdtemp(path5.join(os2.tmpdir(), "codexhost-hermes-delegation-"));
|
|
22263
|
+
const file2 = path5.join(directory, "SKILL.md");
|
|
22264
|
+
try {
|
|
22265
|
+
await writeFile(file2, `---
|
|
22266
|
+
name: delegation
|
|
22267
|
+
description: Discover authorized codexhost agent collaboration.
|
|
22268
|
+
---
|
|
22269
|
+
|
|
22270
|
+
${HERMES_DELEGATION_GUIDANCE}
|
|
22271
|
+
`, { mode: 384 });
|
|
22272
|
+
return {
|
|
22273
|
+
environment: {
|
|
22274
|
+
...environment,
|
|
22275
|
+
HERMES_TUI_SKILLS: [environment.HERMES_TUI_SKILLS, "codexhost-runtime:delegation"].filter(Boolean).join(",")
|
|
22276
|
+
},
|
|
22277
|
+
// Reserve RPC stdout before native plugin imports. The registration is
|
|
22278
|
+
// process-local; no plugin is installed and no user config is changed.
|
|
22279
|
+
bootstrap: `from tui_gateway import server
|
|
22280
|
+
from pathlib import Path
|
|
22281
|
+
from hermes_cli.plugins import PluginContext, PluginManifest, get_plugin_manager
|
|
22282
|
+
PluginContext(PluginManifest(name="codexhost-runtime"), get_plugin_manager()).register_skill(
|
|
22283
|
+
"delegation", Path(${JSON.stringify(file2)}))
|
|
22284
|
+
`,
|
|
22285
|
+
dispose: () => rm(directory, { recursive: true, force: true })
|
|
22286
|
+
};
|
|
22287
|
+
} catch (error51) {
|
|
22288
|
+
await rm(directory, { recursive: true, force: true });
|
|
22289
|
+
throw error51;
|
|
22290
|
+
}
|
|
22291
|
+
}
|
|
22292
|
+
|
|
22293
|
+
// dist/gateway-transport.js
|
|
22294
|
+
function gatewayRecord(value) {
|
|
22295
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
22296
|
+
}
|
|
22297
|
+
function gatewayString(value) {
|
|
22298
|
+
return typeof value === "string" ? value : "";
|
|
22299
|
+
}
|
|
22300
|
+
var GATEWAY_LAUNCH = `import runpy
|
|
22301
|
+
runpy.run_module('tui_gateway.entry', run_name='__main__')`;
|
|
22302
|
+
var HermesGatewayTransport = class _HermesGatewayTransport {
|
|
22303
|
+
python;
|
|
22304
|
+
cwd;
|
|
22305
|
+
environment;
|
|
22306
|
+
timeoutMs;
|
|
22307
|
+
onEvent = () => void 0;
|
|
22308
|
+
onRequest = (id) => this.rejectRequest(id);
|
|
22309
|
+
onFault = () => void 0;
|
|
22310
|
+
#child = null;
|
|
22311
|
+
#nextId = 0;
|
|
22312
|
+
#pending = /* @__PURE__ */ new Map();
|
|
22313
|
+
#closed = false;
|
|
22314
|
+
#closePromise = null;
|
|
22315
|
+
#processClosed = Promise.resolve();
|
|
22316
|
+
#sessionErrors = /* @__PURE__ */ new Map();
|
|
22317
|
+
#sessionReady = /* @__PURE__ */ new Map();
|
|
22318
|
+
#sessionWaiters = /* @__PURE__ */ new Map();
|
|
22319
|
+
#stderr = "";
|
|
22320
|
+
#delegation;
|
|
22321
|
+
constructor(python, cwd, environment, timeoutMs = 3e4) {
|
|
22322
|
+
this.python = python;
|
|
22323
|
+
this.cwd = cwd;
|
|
22324
|
+
this.environment = environment;
|
|
22325
|
+
this.timeoutMs = timeoutMs;
|
|
22326
|
+
}
|
|
22327
|
+
static async probe(executable, cwd, environment) {
|
|
22328
|
+
const shim = await venvPythonFromShim(executable);
|
|
22329
|
+
const candidates = [
|
|
22330
|
+
...new Set([
|
|
22331
|
+
environment.CODEXHOST_HERMES_GATEWAY_PYTHON,
|
|
22332
|
+
shim,
|
|
22333
|
+
...inventoryPythonCandidates(executable)
|
|
22334
|
+
].filter((candidate) => !!candidate))
|
|
22335
|
+
];
|
|
22336
|
+
for (const candidate of candidates) {
|
|
22337
|
+
try {
|
|
22338
|
+
await access2(candidate);
|
|
22339
|
+
} catch {
|
|
22340
|
+
continue;
|
|
22341
|
+
}
|
|
22342
|
+
const transport = new _HermesGatewayTransport(candidate, cwd, environment, 2e4);
|
|
22343
|
+
try {
|
|
22344
|
+
await transport.start();
|
|
22345
|
+
return candidate;
|
|
22346
|
+
} catch {
|
|
22347
|
+
} finally {
|
|
22348
|
+
await transport.close();
|
|
22349
|
+
}
|
|
22350
|
+
}
|
|
22351
|
+
return null;
|
|
22352
|
+
}
|
|
22353
|
+
async prepareSession() {
|
|
22354
|
+
if (this.#child || this.#closed)
|
|
22355
|
+
throw new Error("Hermes gateway Session cannot be prepared after start");
|
|
22356
|
+
const prepared = await prepareGatewayDelegation(this.environment);
|
|
22357
|
+
if (this.#closed) {
|
|
22358
|
+
await prepared.dispose();
|
|
22359
|
+
throw new Error("Hermes gateway closed during Session preparation");
|
|
22360
|
+
}
|
|
22361
|
+
this.#delegation = prepared;
|
|
22362
|
+
}
|
|
22363
|
+
async start() {
|
|
22364
|
+
if (this.#child || this.#closed)
|
|
22365
|
+
throw new Error("Hermes gateway cannot be started twice");
|
|
22366
|
+
const child = spawn3(this.python, ["-I", "-u", "-c", (this.#delegation?.bootstrap ?? "") + GATEWAY_LAUNCH], {
|
|
22367
|
+
cwd: this.cwd,
|
|
22368
|
+
env: {
|
|
22369
|
+
...process.env,
|
|
22370
|
+
...this.#delegation?.environment ?? this.environment,
|
|
22371
|
+
HERMES_TUI_TOOL_PROGRESS: "all"
|
|
22372
|
+
},
|
|
22373
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
22374
|
+
windowsHide: true,
|
|
22375
|
+
detached: process.platform !== "win32"
|
|
22376
|
+
});
|
|
22377
|
+
this.#child = child;
|
|
22378
|
+
this.#processClosed = new Promise((resolve) => child.once("close", () => resolve()));
|
|
22379
|
+
child.stderr.on("data", (data) => {
|
|
22380
|
+
this.#stderr = sanitizeDiagnosticTail(this.#stderr + data.toString());
|
|
22381
|
+
});
|
|
22382
|
+
child.stdin.on("error", (error51) => this.#fault(error51));
|
|
22383
|
+
child.once("error", (error51) => this.#fault(error51));
|
|
22384
|
+
child.once("exit", () => {
|
|
22385
|
+
if (!this.#closed)
|
|
22386
|
+
this.#fault(new Error(`Hermes gateway exited: ${this.#stderr}`));
|
|
22387
|
+
});
|
|
22388
|
+
createInterface({ input: child.stdout }).on("line", (line) => {
|
|
22389
|
+
try {
|
|
22390
|
+
this.#frame(gatewayRecord(JSON.parse(line)));
|
|
22391
|
+
} catch (error51) {
|
|
22392
|
+
this.#fault(error51 instanceof Error ? error51 : new Error(String(error51)));
|
|
22393
|
+
}
|
|
22394
|
+
});
|
|
22395
|
+
const capabilities = await this.request("gateway.capabilities", {});
|
|
22396
|
+
if (capabilities.per_session_exclusive_submit !== true)
|
|
22397
|
+
throw new Error("Hermes gateway does not enforce exclusive turns");
|
|
22398
|
+
}
|
|
22399
|
+
async waitForSession(sessionId) {
|
|
22400
|
+
const error51 = this.#sessionErrors.get(sessionId);
|
|
22401
|
+
if (error51)
|
|
22402
|
+
throw error51;
|
|
22403
|
+
const ready = this.#sessionReady.get(sessionId);
|
|
22404
|
+
if (ready)
|
|
22405
|
+
return ready;
|
|
22406
|
+
const result = new Promise((resolve, reject) => this.#sessionWaiters.set(sessionId, { resolve, reject }));
|
|
22407
|
+
try {
|
|
22408
|
+
return await withTimeout(result, this.timeoutMs, "Hermes gateway agent initialization");
|
|
22409
|
+
} finally {
|
|
22410
|
+
this.#sessionWaiters.delete(sessionId);
|
|
22411
|
+
}
|
|
22412
|
+
}
|
|
22413
|
+
async request(method, params, timeoutMs = this.timeoutMs) {
|
|
22414
|
+
if (this.#closed || !this.#child)
|
|
22415
|
+
throw new Error("Hermes gateway is closed");
|
|
22416
|
+
const id = `codexhost-${++this.#nextId}`;
|
|
22417
|
+
const response = new Promise((resolve, reject) => {
|
|
22418
|
+
this.#pending.set(id, { resolve, reject });
|
|
22419
|
+
this.#send({ jsonrpc: "2.0", id, method, params });
|
|
22420
|
+
});
|
|
22421
|
+
try {
|
|
22422
|
+
return await withTimeout(response, timeoutMs, `Hermes ${method}`);
|
|
22423
|
+
} catch (error51) {
|
|
22424
|
+
if (error51 instanceof HermesTransportError)
|
|
22425
|
+
this.#fault(error51);
|
|
22426
|
+
throw error51;
|
|
22427
|
+
} finally {
|
|
22428
|
+
this.#pending.delete(id);
|
|
22429
|
+
}
|
|
22430
|
+
}
|
|
22431
|
+
respond(id, result) {
|
|
22432
|
+
this.#send({ jsonrpc: "2.0", id, result });
|
|
22433
|
+
}
|
|
22434
|
+
rejectRequest(id) {
|
|
22435
|
+
this.#send({
|
|
22436
|
+
jsonrpc: "2.0",
|
|
22437
|
+
id,
|
|
22438
|
+
error: { code: -32601, message: "Unsupported Host interaction" }
|
|
22439
|
+
});
|
|
22440
|
+
}
|
|
22441
|
+
#send(frame) {
|
|
22442
|
+
if (!this.#child?.stdin.writable)
|
|
22443
|
+
throw new Error("Hermes gateway input is closed");
|
|
22444
|
+
this.#child.stdin.write(JSON.stringify(frame) + "\n");
|
|
22445
|
+
}
|
|
22446
|
+
#frame(frame) {
|
|
22447
|
+
if (frame.method === "event") {
|
|
22448
|
+
const event = gatewayRecord(frame.params);
|
|
22449
|
+
const sessionId = gatewayString(event.session_id);
|
|
22450
|
+
const payload = gatewayRecord(event.payload);
|
|
22451
|
+
if (event.type === "session.info" && payload.lazy !== true) {
|
|
22452
|
+
this.#sessionReady.set(sessionId, payload);
|
|
22453
|
+
this.#sessionWaiters.get(sessionId)?.resolve(payload);
|
|
22454
|
+
} else if (event.type === "error") {
|
|
22455
|
+
const error51 = new Error(gatewayString(payload.message));
|
|
22456
|
+
this.#sessionErrors.set(sessionId, error51);
|
|
22457
|
+
this.#sessionWaiters.get(sessionId)?.reject(error51);
|
|
22458
|
+
}
|
|
22459
|
+
this.onEvent(event);
|
|
22460
|
+
return;
|
|
22461
|
+
}
|
|
22462
|
+
if (typeof frame.method === "string" && typeof frame.id === "string") {
|
|
22463
|
+
this.onRequest(frame.id, frame.method, gatewayRecord(frame.params));
|
|
22464
|
+
return;
|
|
22465
|
+
}
|
|
22466
|
+
const id = gatewayString(frame.id);
|
|
22467
|
+
const pending = this.#pending.get(id);
|
|
22468
|
+
if (!pending)
|
|
22469
|
+
return;
|
|
22470
|
+
this.#pending.delete(id);
|
|
22471
|
+
if (frame.error)
|
|
22472
|
+
pending.reject(new Error(gatewayString(gatewayRecord(frame.error).message) || "Hermes gateway request failed"));
|
|
22473
|
+
else
|
|
22474
|
+
pending.resolve(gatewayRecord(frame.result));
|
|
22475
|
+
}
|
|
22476
|
+
#fault(error51) {
|
|
22477
|
+
for (const pending of this.#pending.values())
|
|
22478
|
+
pending.reject(error51);
|
|
22479
|
+
for (const waiter of this.#sessionWaiters.values())
|
|
22480
|
+
waiter.reject(error51);
|
|
22481
|
+
this.#sessionWaiters.clear();
|
|
22482
|
+
this.#pending.clear();
|
|
22483
|
+
if (!this.#closed) {
|
|
22484
|
+
this.onFault(error51);
|
|
22485
|
+
void this.close();
|
|
22486
|
+
}
|
|
22487
|
+
}
|
|
22488
|
+
#signal(signal) {
|
|
22489
|
+
const pid = this.#child?.pid;
|
|
22490
|
+
if (!pid)
|
|
22491
|
+
return;
|
|
22492
|
+
if (process.platform === "win32") {
|
|
22493
|
+
spawnSync2("taskkill.exe", ["/pid", String(pid), "/t", "/f"], {
|
|
22494
|
+
stdio: "ignore",
|
|
22495
|
+
windowsHide: true
|
|
22496
|
+
});
|
|
22497
|
+
return;
|
|
22498
|
+
}
|
|
22499
|
+
try {
|
|
22500
|
+
process.kill(-pid, signal);
|
|
22501
|
+
} catch (error51) {
|
|
22502
|
+
if (gatewayRecord(error51).code !== "ESRCH")
|
|
22503
|
+
throw error51;
|
|
22504
|
+
}
|
|
22505
|
+
}
|
|
22506
|
+
close() {
|
|
22507
|
+
return this.#closePromise ??= this.#close().finally(() => this.#delegation?.dispose());
|
|
22508
|
+
}
|
|
22509
|
+
async #close() {
|
|
22510
|
+
if (this.#closed)
|
|
22511
|
+
return;
|
|
22512
|
+
this.#closed = true;
|
|
22513
|
+
this.#fault(new Error("Hermes gateway closed"));
|
|
22514
|
+
const child = this.#child;
|
|
22515
|
+
if (!child)
|
|
22516
|
+
return;
|
|
22517
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
22518
|
+
this.#signal("SIGKILL");
|
|
22519
|
+
return;
|
|
22520
|
+
}
|
|
22521
|
+
const exit = this.#processClosed;
|
|
22522
|
+
child.stdin.end();
|
|
22523
|
+
try {
|
|
22524
|
+
await withTimeout(exit, 2e3, "Hermes gateway close");
|
|
22525
|
+
} catch {
|
|
22526
|
+
this.#signal("SIGTERM");
|
|
22527
|
+
try {
|
|
22528
|
+
await withTimeout(exit, 2e3, "Hermes gateway exit");
|
|
22529
|
+
} catch {
|
|
22530
|
+
this.#signal("SIGKILL");
|
|
22531
|
+
await withTimeout(exit, 2e3, "Hermes gateway killed").catch(() => void 0);
|
|
22532
|
+
}
|
|
22533
|
+
}
|
|
22534
|
+
}
|
|
22535
|
+
};
|
|
22536
|
+
|
|
22537
|
+
// dist/gateway-history.js
|
|
22538
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
22539
|
+
|
|
22540
|
+
// dist/gateway-history-script.js
|
|
22541
|
+
var GATEWAY_HISTORY_SCRIPT = String.raw`
|
|
22542
|
+
import sys
|
|
22543
|
+
output = sys.stdout
|
|
22544
|
+
sys.stdout = sys.stderr
|
|
22545
|
+
import hashlib, json, sqlite3
|
|
22546
|
+
from pathlib import Path
|
|
22547
|
+
from urllib.parse import quote
|
|
22548
|
+
from tui_gateway.server import _history_to_messages
|
|
22549
|
+
from agent.context_compressor import user_originated_turn_view
|
|
22550
|
+
from hermes_state import SessionDB
|
|
22551
|
+
from hermes_state_ids import new_session_id
|
|
22552
|
+
p = json.load(sys.stdin)
|
|
22553
|
+
sid = p['sessionId']
|
|
22554
|
+
public_sid = sid
|
|
22555
|
+
|
|
22556
|
+
class HistoryFailure(Exception):
|
|
22557
|
+
def __init__(self, code, message):
|
|
22558
|
+
super().__init__(message)
|
|
22559
|
+
self.code = code
|
|
22560
|
+
|
|
22561
|
+
def digest(rows):
|
|
22562
|
+
return hashlib.sha256(json.dumps(rows, sort_keys=True, ensure_ascii=True, separators=(',', ':')).encode()).hexdigest()
|
|
22563
|
+
|
|
22564
|
+
def config(value):
|
|
22565
|
+
return json.loads(value) if isinstance(value, str) else dict(value or {})
|
|
22566
|
+
|
|
22567
|
+
def user_text(row):
|
|
22568
|
+
view = user_originated_turn_view(row)
|
|
22569
|
+
if view is None: return None
|
|
22570
|
+
# Use the original display kind for native /steer and skill rendering; the
|
|
22571
|
+
# origin predicate above separately excludes synthetic continuation turns.
|
|
22572
|
+
projected = _history_to_messages([row])
|
|
22573
|
+
if not projected: return None
|
|
22574
|
+
return projected[0].get('text')
|
|
22575
|
+
|
|
22576
|
+
def capture(db):
|
|
22577
|
+
exported = db.export_session(sid)
|
|
22578
|
+
if exported is None:
|
|
22579
|
+
raise RuntimeError('Hermes session is not persisted')
|
|
22580
|
+
active = exported['messages']
|
|
22581
|
+
lineage = db.get_compression_lineage(sid)
|
|
22582
|
+
if not lineage: raise RuntimeError('Hermes session lineage is missing')
|
|
22583
|
+
_, display = db.get_resume_conversations(sid)
|
|
22584
|
+
# get_resume_conversations preserves tool relations but sanitizes display text.
|
|
22585
|
+
# Read the same native rows verbatim and native display identities through a
|
|
22586
|
+
# separate mode=ro connection; MIN(id) preserves identity across tail cloning.
|
|
22587
|
+
uri = 'file:' + quote(str(Path(db.db_path).resolve()), safe='/') + '?mode=ro'
|
|
22588
|
+
with sqlite3.connect(uri, uri=True) as conn:
|
|
22589
|
+
conn.row_factory = sqlite3.Row
|
|
22590
|
+
rows = []
|
|
22591
|
+
native_rows = {}
|
|
22592
|
+
for msg in display:
|
|
22593
|
+
row = conn.execute('SELECT * FROM messages WHERE id = ?', (msg['_row_id'],)).fetchone()
|
|
22594
|
+
if row is None:
|
|
22595
|
+
raise RuntimeError('Hermes history changed while reading')
|
|
22596
|
+
owner = row['session_id']
|
|
22597
|
+
if owner not in native_rows:
|
|
22598
|
+
native_rows[owner] = {r['id']: r for r in db.get_messages(owner, include_inactive=True)}
|
|
22599
|
+
raw = dict(native_rows[owner][row['id']])
|
|
22600
|
+
original = row['id']
|
|
22601
|
+
if row['display_identity'] is not None:
|
|
22602
|
+
original = conn.execute('SELECT MIN(id) FROM messages WHERE session_id IN (' + ','.join('?' for _ in lineage) + ') AND display_identity = ?',
|
|
22603
|
+
(*lineage, row['display_identity'])).fetchone()[0] or row['id']
|
|
22604
|
+
raw['original_id'] = original
|
|
22605
|
+
if raw['role'] == 'user': raw['user_text'] = user_text(raw)
|
|
22606
|
+
elif raw['role'] == 'assistant':
|
|
22607
|
+
visible = _history_to_messages([raw])
|
|
22608
|
+
raw['display_text'] = visible[0].get('text', '') if visible else ''
|
|
22609
|
+
raw['display_visible'] = bool(visible)
|
|
22610
|
+
rows.append(raw)
|
|
22611
|
+
compressed = conn.execute('SELECT 1 FROM messages WHERE session_id = ? AND (compacted = 1 OR _compressed_summary = 1) LIMIT 1', (sid,)).fetchone() is not None
|
|
22612
|
+
current = db.export_session(sid)
|
|
22613
|
+
if current is None or digest(current['messages']) != digest(active):
|
|
22614
|
+
raise RuntimeError('Hermes history changed while reading')
|
|
22615
|
+
derivable = sid == public_sid and len(lineage) == 1 and not compressed and [r['id'] for r in rows] == [r['id'] for r in active]
|
|
22616
|
+
boundaries = {}
|
|
22617
|
+
if derivable:
|
|
22618
|
+
for i, row in enumerate(active):
|
|
22619
|
+
if user_text(row) is None: continue
|
|
22620
|
+
end = next((j for j in range(i + 1, len(active)) if user_text(active[j]) is not None), len(active))
|
|
22621
|
+
boundaries[str(row['id'])] = {'count': end, 'digest': digest(active[:end])}
|
|
22622
|
+
return exported, {'rows': rows, 'derivable': derivable, 'boundaries': boundaries, 'physicalSessionId': sid}
|
|
22623
|
+
|
|
22624
|
+
try:
|
|
22625
|
+
if p['operation'] == 'ensure':
|
|
22626
|
+
db = SessionDB()
|
|
22627
|
+
try:
|
|
22628
|
+
if db.get_session(sid) is None:
|
|
22629
|
+
from hermes_constants import parse_reasoning_effort
|
|
22630
|
+
initial = {}
|
|
22631
|
+
if isinstance(p.get('provider'), str) and p['provider']: initial['provider'] = p['provider']
|
|
22632
|
+
if isinstance(p.get('model'), str) and p['model']: initial['model'] = p['model']
|
|
22633
|
+
if isinstance(p.get('reasoningEffort'), str):
|
|
22634
|
+
reasoning = parse_reasoning_effort(p['reasoningEffort'])
|
|
22635
|
+
if reasoning is not None: initial['reasoning_config'] = reasoning
|
|
22636
|
+
if isinstance(p.get('yolo'), bool): initial['yolo_mode'] = p['yolo']
|
|
22637
|
+
db.create_session(session_id=sid, source='cli', cwd=p['cwd'], model=p.get('model'), model_config=initial)
|
|
22638
|
+
result = {'created': True}
|
|
22639
|
+
finally: db.close()
|
|
22640
|
+
elif p['operation'] == 'discard':
|
|
22641
|
+
child = p['derivedSessionId']
|
|
22642
|
+
result = {'deleted': False}
|
|
22643
|
+
if child == sid: raise HistoryFailure('invalidState', 'Cannot delete the source Hermes session')
|
|
22644
|
+
db = SessionDB()
|
|
22645
|
+
try:
|
|
22646
|
+
row = db.get_session(child)
|
|
22647
|
+
if (row is not None and row.get('parent_session_id') == sid
|
|
22648
|
+
and config(row.get('model_config')).get('_branched_from') == sid
|
|
22649
|
+
and digest(db.get_messages(child)) == p['expectedDigest']):
|
|
22650
|
+
result['deleted'] = db.delete_session(child, expected_delete_ids=[child])
|
|
22651
|
+
finally: db.close()
|
|
22652
|
+
else:
|
|
22653
|
+
db = SessionDB(read_only=True)
|
|
22654
|
+
try:
|
|
22655
|
+
sid = db.resolve_resume_session_id(public_sid)
|
|
22656
|
+
exported, snapshot = capture(db)
|
|
22657
|
+
finally: db.close()
|
|
22658
|
+
if p['operation'] in ('read', 'resolve'):
|
|
22659
|
+
result = snapshot
|
|
22660
|
+
elif p['operation'] == 'derive':
|
|
22661
|
+
if not snapshot['derivable']:
|
|
22662
|
+
raise HistoryFailure('unsupported', 'Hermes cannot losslessly derive compacted or inherited history')
|
|
22663
|
+
messages = exported['messages']
|
|
22664
|
+
if p.get('rollbackLastTurn'):
|
|
22665
|
+
users = [i for i, row in enumerate(messages) if user_text(row) is not None]
|
|
22666
|
+
if not users: raise HistoryFailure('invalidState', 'Hermes session has no user turn to roll back')
|
|
22667
|
+
messages = messages[:users[-1]]
|
|
22668
|
+
elif p.get('checkpoint'):
|
|
22669
|
+
checkpoint = p['checkpoint']
|
|
22670
|
+
boundary = snapshot['boundaries'].get(checkpoint['checkpointId'])
|
|
22671
|
+
if boundary is None or boundary != checkpoint.get('locator'):
|
|
22672
|
+
raise HistoryFailure('checkpointNotFound', 'Hermes checkpoint no longer matches native history')
|
|
22673
|
+
messages = messages[:boundary['count']]
|
|
22674
|
+
child = new_session_id()
|
|
22675
|
+
model_config = config(exported.get('model_config'))
|
|
22676
|
+
model_config['_branched_from'] = sid
|
|
22677
|
+
payload = {**exported, 'id': child, 'parent_session_id': sid, 'model_config': model_config,
|
|
22678
|
+
'messages': messages, 'title': None, 'ended_at': None, 'end_reason': None, 'archived': False}
|
|
22679
|
+
db = SessionDB()
|
|
22680
|
+
imported = False
|
|
22681
|
+
try:
|
|
22682
|
+
result = db.import_sessions([payload])
|
|
22683
|
+
if result.get('imported_ids') != [child]:
|
|
22684
|
+
raise RuntimeError('Hermes native import did not create the derived session')
|
|
22685
|
+
imported = True
|
|
22686
|
+
# Native import resets ownership. Fill this NEW row only through the
|
|
22687
|
+
# native COALESCE upsert, preserving source profile and working dir.
|
|
22688
|
+
db.create_session(session_id=child, source=exported.get('source') or 'cli',
|
|
22689
|
+
cwd=exported.get('cwd'), profile_name=exported.get('profile_name'), parent_session_id=sid)
|
|
22690
|
+
tools = exported.get('tool_names')
|
|
22691
|
+
if isinstance(tools, str): tools = json.loads(tools)
|
|
22692
|
+
if tools is not None: db.update_session_tool_names(child, tools)
|
|
22693
|
+
copied = db.get_messages(child)
|
|
22694
|
+
def payload_rows(rows):
|
|
22695
|
+
ignored = {'id', 'session_id', 'active', 'compacted', '_row_id'}
|
|
22696
|
+
result = []
|
|
22697
|
+
for row in rows:
|
|
22698
|
+
row = {k: v for k, v in row.items() if k not in ignored}
|
|
22699
|
+
for key in ('reasoning_details', 'codex_reasoning_items', 'codex_message_items'):
|
|
22700
|
+
if isinstance(row.get(key), str): row[key] = json.loads(row[key])
|
|
22701
|
+
result.append(row)
|
|
22702
|
+
return result
|
|
22703
|
+
if payload_rows(copied) != payload_rows(messages):
|
|
22704
|
+
raise RuntimeError('Hermes native import did not preserve the transcript')
|
|
22705
|
+
result = {'sessionId': child, 'digest': digest(copied)}
|
|
22706
|
+
except Exception:
|
|
22707
|
+
if imported: db.delete_session(child, expected_delete_ids=[child])
|
|
22708
|
+
raise
|
|
22709
|
+
finally: db.close()
|
|
22710
|
+
else: raise RuntimeError('Unknown Hermes history operation')
|
|
22711
|
+
except HistoryFailure as exc:
|
|
22712
|
+
result = {'error': {'code': exc.code, 'message': str(exc)}}
|
|
22713
|
+
print(json.dumps(result, ensure_ascii=True), file=output)
|
|
22714
|
+
`;
|
|
22715
|
+
|
|
22716
|
+
// dist/gateway-history.js
|
|
22717
|
+
function record2(value) {
|
|
22718
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
22719
|
+
}
|
|
22720
|
+
function text(value) {
|
|
22721
|
+
if (typeof value === "string")
|
|
22722
|
+
return value;
|
|
22723
|
+
if (Array.isArray(value))
|
|
22724
|
+
return value.map((part) => text(record2(part).text)).join("\n");
|
|
22725
|
+
return "";
|
|
22726
|
+
}
|
|
22727
|
+
function parsed(value) {
|
|
22728
|
+
if (typeof value === "string") {
|
|
22729
|
+
try {
|
|
22730
|
+
return JSON.parse(value);
|
|
22731
|
+
} catch {
|
|
22732
|
+
return value;
|
|
22733
|
+
}
|
|
22734
|
+
}
|
|
22735
|
+
return value ?? null;
|
|
22736
|
+
}
|
|
22737
|
+
function projectGatewayHistory(sessionId, data) {
|
|
22738
|
+
const turns = [];
|
|
22739
|
+
let turn;
|
|
22740
|
+
const tools = /* @__PURE__ */ new Map();
|
|
22741
|
+
for (const row of data.rows) {
|
|
22742
|
+
if (!Number.isSafeInteger(row.id) || !Number.isSafeInteger(row.original_id))
|
|
22743
|
+
throw new Error("Hermes history has no durable message identity");
|
|
22744
|
+
const id = String(row.original_id);
|
|
22745
|
+
if (row.role === "user") {
|
|
22746
|
+
if (typeof row.user_text !== "string")
|
|
22747
|
+
continue;
|
|
22748
|
+
tools.clear();
|
|
22749
|
+
const boundary = data.derivable ? data.boundaries[String(row.id)] : void 0;
|
|
22750
|
+
turn = {
|
|
22751
|
+
nativeTurnRef: nativeTurnRefSchema.parse({
|
|
22752
|
+
harnessId: "hermes",
|
|
22753
|
+
nativeSessionId: sessionId,
|
|
22754
|
+
nativeTurnKey: id,
|
|
22755
|
+
formatVersion: 1
|
|
22756
|
+
}),
|
|
22757
|
+
...boundary ? {
|
|
22758
|
+
checkpoint: nativeCheckpointRefSchema.parse({
|
|
22759
|
+
harnessId: "hermes",
|
|
22760
|
+
nativeSessionId: sessionId,
|
|
22761
|
+
checkpointId: String(row.id),
|
|
22762
|
+
formatVersion: 1,
|
|
22763
|
+
locator: boundary
|
|
22764
|
+
})
|
|
22765
|
+
} : {},
|
|
22766
|
+
input: [{ type: "text", text: row.user_text }],
|
|
22767
|
+
items: [],
|
|
22768
|
+
outcome: {
|
|
22769
|
+
status: "unknown",
|
|
22770
|
+
reason: "Hermes persisted history does not record a turn outcome"
|
|
22771
|
+
},
|
|
22772
|
+
...typeof row.timestamp === "number" ? { startedAtMs: Math.trunc(row.timestamp * 1e3) } : {}
|
|
22773
|
+
};
|
|
22774
|
+
turns.push(turn);
|
|
22775
|
+
continue;
|
|
22776
|
+
}
|
|
22777
|
+
if (!turn)
|
|
22778
|
+
continue;
|
|
22779
|
+
const itemId = (suffix) => hostItemIdSchema.parse(`hermes:${id}:${suffix}`);
|
|
22780
|
+
if (row.role === "assistant") {
|
|
22781
|
+
const reasoning = row.display_visible === false ? "" : text(row.reasoning_content) || text(row.reasoning);
|
|
22782
|
+
if (reasoning)
|
|
22783
|
+
turn.items.push({
|
|
22784
|
+
item: { type: "reasoning", itemId: itemId("reasoning"), text: reasoning },
|
|
22785
|
+
outcome: { status: "succeeded" }
|
|
22786
|
+
});
|
|
22787
|
+
const content = typeof row.display_text === "string" ? row.display_text : text(row.content);
|
|
22788
|
+
if (content)
|
|
22789
|
+
turn.items.push({
|
|
22790
|
+
item: { type: "agentMessage", itemId: itemId("message"), text: content },
|
|
22791
|
+
outcome: { status: "succeeded" }
|
|
22792
|
+
});
|
|
22793
|
+
if (Array.isArray(row.tool_calls))
|
|
22794
|
+
for (const value of row.tool_calls) {
|
|
22795
|
+
const call = record2(value);
|
|
22796
|
+
const fn = record2(call.function);
|
|
22797
|
+
if (typeof call.id !== "string" || typeof fn.name !== "string")
|
|
22798
|
+
continue;
|
|
22799
|
+
tools.set(call.id, {
|
|
22800
|
+
item: {
|
|
22801
|
+
type: "toolExecution",
|
|
22802
|
+
itemId: itemId(`tool:${call.id}`),
|
|
22803
|
+
toolName: fn.name,
|
|
22804
|
+
arguments: parsed(fn.arguments)
|
|
22805
|
+
},
|
|
22806
|
+
outcome: { status: "succeeded" }
|
|
22807
|
+
});
|
|
22808
|
+
}
|
|
22809
|
+
} else if (row.role === "tool" && typeof row.tool_call_id === "string") {
|
|
22810
|
+
const tool = tools.get(row.tool_call_id);
|
|
22811
|
+
if (!tool || tool.item.type !== "toolExecution")
|
|
22812
|
+
continue;
|
|
22813
|
+
const output = typeof row.content === "string" ? row.content : JSON.stringify(row.content ?? "");
|
|
22814
|
+
tool.item.output = { content: [{ type: "text", text: output }] };
|
|
22815
|
+
const result = record2(parsed(row.content));
|
|
22816
|
+
if (result.is_error === true || result.isError === true || result.error)
|
|
22817
|
+
tool.outcome = {
|
|
22818
|
+
status: "failed",
|
|
22819
|
+
error: {
|
|
22820
|
+
code: "nativeFailure",
|
|
22821
|
+
message: typeof result.error === "string" ? result.error : "Hermes tool reported an error",
|
|
22822
|
+
retryable: false
|
|
22823
|
+
}
|
|
22824
|
+
};
|
|
22825
|
+
turn.items.push(tool);
|
|
22826
|
+
tools.delete(row.tool_call_id);
|
|
22827
|
+
}
|
|
22828
|
+
}
|
|
22829
|
+
return { turns };
|
|
22830
|
+
}
|
|
22831
|
+
var HermesGatewayHistoryError = class extends Error {
|
|
22832
|
+
code;
|
|
22833
|
+
constructor(code, message) {
|
|
22834
|
+
super(message);
|
|
22835
|
+
this.code = code;
|
|
22836
|
+
}
|
|
22837
|
+
};
|
|
22838
|
+
var HermesGatewayHistory = class {
|
|
22839
|
+
options;
|
|
22840
|
+
#derived = /* @__PURE__ */ new Map();
|
|
22841
|
+
constructor(options) {
|
|
22842
|
+
this.options = options;
|
|
22843
|
+
}
|
|
22844
|
+
async readSnapshot() {
|
|
22845
|
+
const data = await this.#run({ operation: "read" });
|
|
22846
|
+
if (!Array.isArray(data.rows) || typeof data.derivable !== "boolean" || !data.boundaries)
|
|
22847
|
+
throw new Error("Malformed Hermes native history response");
|
|
22848
|
+
return projectGatewayHistory(this.options.nativeSessionId, data);
|
|
22849
|
+
}
|
|
22850
|
+
async resolvePhysicalSessionId() {
|
|
22851
|
+
const data = await this.#run({ operation: "resolve" });
|
|
22852
|
+
if (typeof data.physicalSessionId !== "string")
|
|
22853
|
+
throw new Error("Missing Hermes storage identity");
|
|
22854
|
+
return data.physicalSessionId;
|
|
22855
|
+
}
|
|
22856
|
+
async ensureCreated(input) {
|
|
22857
|
+
await this.#run({ operation: "ensure", ...input });
|
|
22858
|
+
}
|
|
22859
|
+
async derive(input = {}) {
|
|
22860
|
+
if (input.checkpoint && input.rollbackLastTurn)
|
|
22861
|
+
throw new Error("Conflicting Hermes derivation boundaries");
|
|
22862
|
+
if (input.checkpoint && (input.checkpoint.harnessId !== "hermes" || input.checkpoint.nativeSessionId !== this.options.nativeSessionId))
|
|
22863
|
+
throw new Error("Hermes checkpoint belongs to another session");
|
|
22864
|
+
const data = await this.#run({ operation: "derive", ...input });
|
|
22865
|
+
const ref = nativeSessionRefSchema.parse({
|
|
22866
|
+
harnessId: "hermes",
|
|
22867
|
+
nativeSessionId: data.sessionId,
|
|
22868
|
+
formatVersion: 1,
|
|
22869
|
+
locator: { transport: "gateway" }
|
|
22870
|
+
});
|
|
22871
|
+
if (typeof data.digest !== "string")
|
|
22872
|
+
throw new Error("Missing Hermes derived-history proof");
|
|
22873
|
+
this.#derived.set(ref.nativeSessionId, data.digest);
|
|
22874
|
+
return ref;
|
|
22875
|
+
}
|
|
22876
|
+
async discardDerived(ref) {
|
|
22877
|
+
const expectedDigest = this.#derived.get(ref.nativeSessionId);
|
|
22878
|
+
if (ref.harnessId !== "hermes" || ref.nativeSessionId === this.options.nativeSessionId || !expectedDigest)
|
|
22879
|
+
throw new HermesGatewayHistoryError("invalidRequest", "This history reader did not create the derived Hermes session");
|
|
22880
|
+
const result = await this.#run({
|
|
22881
|
+
operation: "discard",
|
|
22882
|
+
derivedSessionId: ref.nativeSessionId,
|
|
22883
|
+
expectedDigest
|
|
22884
|
+
});
|
|
22885
|
+
if (result.deleted === true)
|
|
22886
|
+
this.#derived.delete(ref.nativeSessionId);
|
|
22887
|
+
return result.deleted === true;
|
|
22888
|
+
}
|
|
22889
|
+
#run(input) {
|
|
22890
|
+
return new Promise((resolve, reject) => {
|
|
22891
|
+
const child = spawn4(this.options.python, ["-I", "-u", "-c", GATEWAY_HISTORY_SCRIPT], {
|
|
22892
|
+
cwd: this.options.cwd,
|
|
22893
|
+
env: { ...process.env, ...this.options.environment },
|
|
22894
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
22895
|
+
windowsHide: true
|
|
22896
|
+
});
|
|
22897
|
+
let stdout = "";
|
|
22898
|
+
const timer = setTimeout(() => {
|
|
22899
|
+
child.kill();
|
|
22900
|
+
reject(new Error("Hermes history operation timed out"));
|
|
22901
|
+
}, 3e4);
|
|
22902
|
+
child.on("error", () => {
|
|
22903
|
+
clearTimeout(timer);
|
|
22904
|
+
reject(new Error("Cannot start Hermes native history reader"));
|
|
22905
|
+
});
|
|
22906
|
+
child.stdout.on("data", (chunk) => {
|
|
22907
|
+
stdout += chunk.toString();
|
|
22908
|
+
if (stdout.length > 32 * 1024 * 1024) {
|
|
22909
|
+
child.kill();
|
|
22910
|
+
reject(new Error("Hermes native history exceeds the supported size"));
|
|
22911
|
+
}
|
|
22912
|
+
});
|
|
22913
|
+
child.stderr.resume();
|
|
22914
|
+
child.stdin.on("error", () => void 0);
|
|
22915
|
+
child.on("close", (code) => {
|
|
22916
|
+
clearTimeout(timer);
|
|
22917
|
+
if (code !== 0)
|
|
22918
|
+
return reject(new Error("Hermes native history operation failed; source history was not modified"));
|
|
22919
|
+
try {
|
|
22920
|
+
const value = record2(JSON.parse(stdout.trim()));
|
|
22921
|
+
const error51 = record2(value.error);
|
|
22922
|
+
if (["unsupported", "checkpointNotFound", "invalidState"].includes(String(error51.code))) {
|
|
22923
|
+
reject(new HermesGatewayHistoryError(error51.code, String(error51.message)));
|
|
22924
|
+
} else
|
|
22925
|
+
resolve(value);
|
|
22926
|
+
} catch {
|
|
22927
|
+
reject(new Error("Malformed Hermes native history response"));
|
|
22928
|
+
}
|
|
22929
|
+
});
|
|
22930
|
+
child.stdin.end(JSON.stringify({ ...input, sessionId: this.options.nativeSessionId }));
|
|
22931
|
+
});
|
|
22932
|
+
}
|
|
22933
|
+
};
|
|
22934
|
+
|
|
22935
|
+
// dist/gateway-session-transport.js
|
|
22936
|
+
var hermesGatewayThinkingOptions = [
|
|
22937
|
+
"none",
|
|
22938
|
+
"minimal",
|
|
22939
|
+
"low",
|
|
22940
|
+
"medium",
|
|
22941
|
+
"high",
|
|
22942
|
+
"xhigh",
|
|
22943
|
+
"max",
|
|
22944
|
+
"ultra"
|
|
22945
|
+
].map((id) => ({ id: harnessThinkingOptionIdSchema.parse(id), label: id }));
|
|
22946
|
+
function question(entry, fallbackId) {
|
|
22947
|
+
const id = gatewayString(entry.qid) || fallbackId;
|
|
22948
|
+
const prompt = gatewayString(entry.question);
|
|
22949
|
+
if (!prompt)
|
|
22950
|
+
throw new Error("Hermes clarify request has no question");
|
|
22951
|
+
const choices = Array.isArray(entry.choices) ? entry.choices.filter((choice) => typeof choice === "string") : [];
|
|
22952
|
+
return choices.length ? {
|
|
22953
|
+
id,
|
|
22954
|
+
type: "choice",
|
|
22955
|
+
prompt,
|
|
22956
|
+
options: choices.map((value) => ({ value, label: value })),
|
|
22957
|
+
multiple: entry.multi_select === true,
|
|
22958
|
+
allowOther: true,
|
|
22959
|
+
optional: false
|
|
22960
|
+
} : { id, type: "text", prompt, multiline: true, secret: false, optional: false };
|
|
22961
|
+
}
|
|
22962
|
+
function gatewayDiff(text2) {
|
|
22963
|
+
if (!text2 || text2.length > 1024 * 1024)
|
|
22964
|
+
return [];
|
|
22965
|
+
const result = [];
|
|
22966
|
+
let path7 = "";
|
|
22967
|
+
let inHunk = false;
|
|
22968
|
+
let oldLines = [], newLines = [];
|
|
22969
|
+
const flush = () => {
|
|
22970
|
+
if (path7)
|
|
22971
|
+
result.push({
|
|
22972
|
+
type: "diff",
|
|
22973
|
+
path: path7,
|
|
22974
|
+
oldText: oldLines.join("\n"),
|
|
22975
|
+
newText: newLines.join("\n")
|
|
22976
|
+
});
|
|
22977
|
+
oldLines = [];
|
|
22978
|
+
newLines = [];
|
|
22979
|
+
};
|
|
22980
|
+
for (const raw of text2.split("\n")) {
|
|
22981
|
+
const line = raw.replace(/\u001b\[[0-9;]*m/gu, "");
|
|
22982
|
+
const header = /^(a\/.+|\/dev\/null) → (b\/.+|\/dev\/null)$/u.exec(line);
|
|
22983
|
+
if (header) {
|
|
22984
|
+
flush();
|
|
22985
|
+
inHunk = false;
|
|
22986
|
+
path7 = (header[2] === "/dev/null" ? header[1] ?? "" : header[2] ?? "").replace(/^[ab]\//u, "");
|
|
22987
|
+
continue;
|
|
22988
|
+
}
|
|
22989
|
+
if (line.startsWith("--- ") && !inHunk) {
|
|
22990
|
+
flush();
|
|
22991
|
+
path7 = line.slice(4).replace(/^a\//u, "");
|
|
22992
|
+
} else if (line.startsWith("+++ ") && !inHunk) {
|
|
22993
|
+
const next = line.slice(4);
|
|
22994
|
+
if (next !== "/dev/null")
|
|
22995
|
+
path7 = next.replace(/^b\//u, "");
|
|
22996
|
+
} else if (line.startsWith("@@")) {
|
|
22997
|
+
inHunk = true;
|
|
22998
|
+
continue;
|
|
22999
|
+
} else if (path7 && line.startsWith("-"))
|
|
23000
|
+
oldLines.push(line.slice(1));
|
|
23001
|
+
else if (path7 && line.startsWith("+"))
|
|
23002
|
+
newLines.push(line.slice(1));
|
|
23003
|
+
else if (path7 && line.startsWith(" ")) {
|
|
23004
|
+
oldLines.push(line.slice(1));
|
|
23005
|
+
newLines.push(line.slice(1));
|
|
23006
|
+
}
|
|
23007
|
+
}
|
|
23008
|
+
flush();
|
|
23009
|
+
return result;
|
|
23010
|
+
}
|
|
23011
|
+
var HermesGatewaySessionTransport = class {
|
|
23012
|
+
transport;
|
|
23013
|
+
sessionId;
|
|
23014
|
+
nativeSessionId;
|
|
23015
|
+
onFault = () => void 0;
|
|
23016
|
+
availableCommands = HERMES_GATEWAY_COMMANDS;
|
|
23017
|
+
history;
|
|
23018
|
+
#active = null;
|
|
23019
|
+
#requests = /* @__PURE__ */ new Map();
|
|
23020
|
+
#closed = false;
|
|
23021
|
+
#info;
|
|
23022
|
+
constructor(transport, sessionId, nativeSessionId, info) {
|
|
23023
|
+
this.transport = transport;
|
|
23024
|
+
this.sessionId = sessionId;
|
|
23025
|
+
this.nativeSessionId = nativeSessionId;
|
|
23026
|
+
this.#info = info;
|
|
23027
|
+
this.history = new HermesGatewayHistory({
|
|
23028
|
+
python: transport.python,
|
|
23029
|
+
cwd: transport.cwd,
|
|
23030
|
+
environment: transport.environment,
|
|
23031
|
+
nativeSessionId
|
|
23032
|
+
});
|
|
23033
|
+
transport.onEvent = (event) => this.#event(event);
|
|
23034
|
+
transport.onRequest = (id, method, params) => {
|
|
23035
|
+
void this.#request(id, method, params).catch((error51) => this.#fault(error51));
|
|
23036
|
+
};
|
|
23037
|
+
transport.onFault = (error51) => this.#fault(error51);
|
|
23038
|
+
}
|
|
23039
|
+
async openResult() {
|
|
23040
|
+
const reasoning = await this.transport.request("config.get", {
|
|
23041
|
+
key: "reasoning",
|
|
23042
|
+
session_id: this.sessionId
|
|
23043
|
+
});
|
|
23044
|
+
const currentThinkingOptionId = gatewayString(reasoning.value);
|
|
23045
|
+
const model = gatewayString(this.#info.model);
|
|
23046
|
+
const provider = gatewayString(this.#info.provider);
|
|
23047
|
+
const currentModelId = provider ? `${provider}:${model}` : model;
|
|
23048
|
+
return {
|
|
23049
|
+
initialize: { protocolVersion: 1 },
|
|
23050
|
+
sessionId: this.nativeSessionId,
|
|
23051
|
+
replay: [],
|
|
23052
|
+
session: {
|
|
23053
|
+
sessionId: this.nativeSessionId,
|
|
23054
|
+
models: currentModelId ? {
|
|
23055
|
+
currentModelId,
|
|
23056
|
+
availableModels: [
|
|
23057
|
+
{ modelId: currentModelId, name: provider ? `${provider} / ${model}` : model }
|
|
23058
|
+
]
|
|
23059
|
+
} : null,
|
|
23060
|
+
modes: {
|
|
23061
|
+
currentModeId: this.#info.yolo === true ? "dont_ask" : "default",
|
|
23062
|
+
availableModes: [
|
|
23063
|
+
{ id: "default", name: "Native approval policy" },
|
|
23064
|
+
{ id: "dont_ask", name: "Session YOLO" }
|
|
23065
|
+
]
|
|
23066
|
+
},
|
|
23067
|
+
thinkingOptions: hermesGatewayThinkingOptions,
|
|
23068
|
+
...hermesGatewayThinkingOptions.some(({ id }) => id === currentThinkingOptionId) ? { currentThinkingOptionId } : {}
|
|
23069
|
+
}
|
|
23070
|
+
};
|
|
23071
|
+
}
|
|
23072
|
+
async readNativeSnapshot() {
|
|
23073
|
+
return this.history.readSnapshot();
|
|
23074
|
+
}
|
|
23075
|
+
async setThinking(optionId) {
|
|
23076
|
+
if (!hermesGatewayThinkingOptions.some(({ id }) => id === optionId))
|
|
23077
|
+
throw new Error("Unsupported Hermes reasoning effort");
|
|
23078
|
+
const result = await this.transport.request("config.set", {
|
|
23079
|
+
key: "reasoning",
|
|
23080
|
+
value: optionId,
|
|
23081
|
+
scope: "session",
|
|
23082
|
+
session_id: this.sessionId
|
|
23083
|
+
});
|
|
23084
|
+
if (result.value !== optionId || (await this.transport.request("config.get", { key: "reasoning", session_id: this.sessionId })).value !== optionId)
|
|
23085
|
+
throw new Error("Hermes did not confirm reasoning selection");
|
|
23086
|
+
return optionId;
|
|
23087
|
+
}
|
|
23088
|
+
async setModel(modelId) {
|
|
23089
|
+
if (/\s/u.test(modelId) || modelId.startsWith("-"))
|
|
23090
|
+
throw new Error("Invalid Hermes Model identifier");
|
|
23091
|
+
const result = await this.transport.request("config.set", {
|
|
23092
|
+
key: "model",
|
|
23093
|
+
value: modelId,
|
|
23094
|
+
scope: "session",
|
|
23095
|
+
session_id: this.sessionId
|
|
23096
|
+
});
|
|
23097
|
+
if (result.confirm_required === true)
|
|
23098
|
+
throw new Error(gatewayString(result.confirm_message) || "Hermes requires confirmation for this Model");
|
|
23099
|
+
const actualModel = gatewayString(this.#info.model);
|
|
23100
|
+
const actualProvider = gatewayString(this.#info.provider);
|
|
23101
|
+
if (modelId !== actualModel && modelId !== `${actualProvider}:${actualModel}`)
|
|
23102
|
+
throw new Error("Hermes did not confirm the requested Model");
|
|
23103
|
+
}
|
|
23104
|
+
async setPermissionMode(modeId) {
|
|
23105
|
+
if (modeId !== "default" && modeId !== "dont_ask")
|
|
23106
|
+
throw new Error("Hermes gateway has no accept_edits mode");
|
|
23107
|
+
const result = await this.transport.request("config.set", {
|
|
23108
|
+
key: "yolo",
|
|
23109
|
+
value: modeId === "dont_ask" ? "on" : "off",
|
|
23110
|
+
scope: "session",
|
|
23111
|
+
session_id: this.sessionId
|
|
23112
|
+
});
|
|
23113
|
+
if (result.value !== (modeId === "dont_ask" ? "1" : "0") || this.#info.yolo !== (modeId === "dont_ask"))
|
|
23114
|
+
throw new Error("Hermes effective approval policy differs from the requested mode; check native global approvals policy");
|
|
23115
|
+
}
|
|
23116
|
+
nativeCommandName(text2) {
|
|
23117
|
+
const commandText = text2.trim();
|
|
23118
|
+
if (/^\/compress(?:\s|$)/iu.test(commandText))
|
|
23119
|
+
return "compress";
|
|
23120
|
+
return /^\/(help|tools|context|version)$/iu.exec(commandText)?.[1]?.toLowerCase() ?? null;
|
|
23121
|
+
}
|
|
23122
|
+
async runTurn(text2, emit, permission, question2) {
|
|
23123
|
+
if (this.#active || this.#closed)
|
|
23124
|
+
throw new Error("Hermes gateway Session is unavailable");
|
|
23125
|
+
let resolveResult = () => void 0;
|
|
23126
|
+
let rejectResult = () => void 0;
|
|
23127
|
+
const result = new Promise((resolve, reject) => {
|
|
23128
|
+
resolveResult = resolve;
|
|
23129
|
+
rejectResult = reject;
|
|
23130
|
+
});
|
|
23131
|
+
void result.catch(() => void 0);
|
|
23132
|
+
const active = {
|
|
23133
|
+
emit,
|
|
23134
|
+
permission,
|
|
23135
|
+
...question2 ? { question: question2 } : {},
|
|
23136
|
+
resolve: resolveResult,
|
|
23137
|
+
reject: rejectResult,
|
|
23138
|
+
streamed: "",
|
|
23139
|
+
reasoning: "",
|
|
23140
|
+
toolIds: /* @__PURE__ */ new Set()
|
|
23141
|
+
};
|
|
23142
|
+
this.#active = active;
|
|
23143
|
+
const commandText = text2.trim();
|
|
23144
|
+
const nativeCommand = this.nativeCommandName(text2);
|
|
23145
|
+
try {
|
|
23146
|
+
if (nativeCommand === "compress") {
|
|
23147
|
+
const compact = await this.transport.request("session.compress", { session_id: this.sessionId, focus_topic: commandText.slice(9).trim() }, 3e5);
|
|
23148
|
+
const summary = gatewayRecord(compact.summary);
|
|
23149
|
+
const explanation = [summary.headline, summary.token_line, summary.note, compact.message].filter((value) => typeof value === "string" && !!value).join("\n");
|
|
23150
|
+
if (explanation)
|
|
23151
|
+
emit({ type: "agent.text", text: explanation });
|
|
23152
|
+
if (compact.status === "pending") {
|
|
23153
|
+
this.#fault(new Error("Hermes compression remains pending; Session closed to prevent overlapping native work"));
|
|
23154
|
+
return await result;
|
|
23155
|
+
}
|
|
23156
|
+
if (!["compressed", "aborted"].includes(String(compact.status)) && compact.lock_held !== true)
|
|
23157
|
+
throw new Error("Hermes returned an unrecognized compression outcome");
|
|
23158
|
+
this.#usage(gatewayRecord(compact.usage));
|
|
23159
|
+
const succeeded = compact.status === "compressed" && summary.noop !== true;
|
|
23160
|
+
active.resolve({
|
|
23161
|
+
stopReason: "end_turn",
|
|
23162
|
+
compactionOutcome: compact.status === "aborted" || summary.aborted === true ? {
|
|
23163
|
+
status: "failed",
|
|
23164
|
+
error: {
|
|
23165
|
+
code: "nativeFailure",
|
|
23166
|
+
message: explanation || "Hermes summary generation failed",
|
|
23167
|
+
retryable: true
|
|
23168
|
+
}
|
|
23169
|
+
} : succeeded ? { status: "succeeded" } : { status: "cancelled", reason: explanation || "Hermes did not compact context" }
|
|
23170
|
+
});
|
|
23171
|
+
} else if (nativeCommand) {
|
|
23172
|
+
const response = await this.transport.request("slash.exec", {
|
|
23173
|
+
session_id: this.sessionId,
|
|
23174
|
+
command: `/${nativeCommand}`
|
|
23175
|
+
});
|
|
23176
|
+
emit({ type: "agent.text", text: gatewayString(response.output) });
|
|
23177
|
+
active.resolve({ stopReason: "end_turn" });
|
|
23178
|
+
} else {
|
|
23179
|
+
const submitted = await this.transport.request("prompt.submit", {
|
|
23180
|
+
session_id: this.sessionId,
|
|
23181
|
+
text: text2
|
|
23182
|
+
});
|
|
23183
|
+
if (submitted.status !== "streaming")
|
|
23184
|
+
this.#fault(new Error(`Hermes did not start an exclusive turn (${String(submitted.status)})`));
|
|
23185
|
+
}
|
|
23186
|
+
} catch (error51) {
|
|
23187
|
+
active.reject(error51 instanceof Error ? error51 : new Error(String(error51)));
|
|
23188
|
+
}
|
|
23189
|
+
try {
|
|
23190
|
+
return await result;
|
|
23191
|
+
} finally {
|
|
23192
|
+
if (this.#active === active)
|
|
23193
|
+
this.#active = null;
|
|
23194
|
+
for (const controller of this.#requests.values())
|
|
23195
|
+
controller.abort("cancelled");
|
|
23196
|
+
this.#requests.clear();
|
|
23197
|
+
}
|
|
23198
|
+
}
|
|
23199
|
+
async cancel() {
|
|
23200
|
+
await this.transport.request("session.interrupt", { session_id: this.sessionId });
|
|
23201
|
+
}
|
|
23202
|
+
async close() {
|
|
23203
|
+
if (this.#closed)
|
|
23204
|
+
return;
|
|
23205
|
+
this.#closed = true;
|
|
23206
|
+
for (const controller of this.#requests.values())
|
|
23207
|
+
controller.abort("cancelled");
|
|
23208
|
+
this.#requests.clear();
|
|
23209
|
+
this.#active?.reject(new Error("Hermes gateway Session closed"));
|
|
23210
|
+
try {
|
|
23211
|
+
await this.transport.request("session.close", { session_id: this.sessionId });
|
|
23212
|
+
} catch {
|
|
23213
|
+
}
|
|
23214
|
+
await this.transport.close();
|
|
23215
|
+
}
|
|
23216
|
+
#fault(error51) {
|
|
23217
|
+
if (this.#closed)
|
|
23218
|
+
return;
|
|
23219
|
+
this.#closed = true;
|
|
23220
|
+
for (const controller of this.#requests.values())
|
|
23221
|
+
controller.abort("cancelled");
|
|
23222
|
+
this.#requests.clear();
|
|
23223
|
+
const fault = new HermesTransportError("protocolError", error51 instanceof Error ? error51.message : String(error51));
|
|
23224
|
+
this.#active?.reject(fault);
|
|
23225
|
+
this.onFault(fault);
|
|
23226
|
+
void this.transport.close();
|
|
23227
|
+
}
|
|
23228
|
+
#event(event) {
|
|
23229
|
+
if (event.session_id !== this.sessionId || this.#closed)
|
|
23230
|
+
return;
|
|
23231
|
+
const payload = gatewayRecord(event.payload);
|
|
23232
|
+
if (event.type === "session.info") {
|
|
23233
|
+
this.#info = payload;
|
|
23234
|
+
return;
|
|
23235
|
+
}
|
|
23236
|
+
if (event.type === "request.cancel") {
|
|
23237
|
+
const controller = this.#requests.get(gatewayString(payload.id));
|
|
23238
|
+
controller?.abort(payload.reason === "timeout" ? "expired" : "cancelled");
|
|
23239
|
+
return;
|
|
23240
|
+
}
|
|
23241
|
+
const active = this.#active;
|
|
23242
|
+
if (!active) {
|
|
23243
|
+
if (event.type === "error")
|
|
23244
|
+
this.#fault(new Error(gatewayString(payload.message)));
|
|
23245
|
+
return;
|
|
23246
|
+
}
|
|
23247
|
+
const text2 = gatewayString(payload.text);
|
|
23248
|
+
if (event.type === "message.delta") {
|
|
23249
|
+
active.streamed += text2;
|
|
23250
|
+
active.emit({ type: "agent.text", text: text2 });
|
|
23251
|
+
} else if (event.type === "reasoning.delta" || event.type === "reasoning.available") {
|
|
23252
|
+
if (event.type === "reasoning.delta" || !active.reasoning) {
|
|
23253
|
+
active.reasoning += text2;
|
|
23254
|
+
active.emit({ type: "agent.thought", text: text2 });
|
|
23255
|
+
}
|
|
23256
|
+
} else if (event.type === "message.interim") {
|
|
23257
|
+
if (payload.already_streamed !== true)
|
|
23258
|
+
active.emit({ type: "agent.text", text: text2 });
|
|
23259
|
+
active.streamed = "";
|
|
23260
|
+
active.reasoning = "";
|
|
23261
|
+
} else if (event.type === "session.usage")
|
|
23262
|
+
this.#usage(gatewayRecord(payload.usage));
|
|
23263
|
+
else if (event.type === "tool.start" || event.type === "tool.complete") {
|
|
23264
|
+
const toolCallId = gatewayString(payload.tool_id);
|
|
23265
|
+
if (!toolCallId)
|
|
23266
|
+
return;
|
|
23267
|
+
const result = gatewayRecord(payload.result);
|
|
23268
|
+
const output = payload.result !== void 0 ? typeof payload.result === "string" ? payload.result : JSON.stringify(payload.result) : gatewayString(payload.result_text) || gatewayString(payload.summary);
|
|
23269
|
+
const content = [
|
|
23270
|
+
...output ? [{ type: "content", content: { type: "text", text: output } }] : [],
|
|
23271
|
+
...gatewayDiff(gatewayString(payload.inline_diff))
|
|
23272
|
+
];
|
|
23273
|
+
if (event.type === "tool.complete" && !active.toolIds.has(toolCallId))
|
|
23274
|
+
active.emit({
|
|
23275
|
+
type: "tool.call",
|
|
23276
|
+
toolCallId,
|
|
23277
|
+
update: {
|
|
23278
|
+
sessionUpdate: "tool_call",
|
|
23279
|
+
toolCallId,
|
|
23280
|
+
title: gatewayString(payload.name),
|
|
23281
|
+
rawInput: payload.args,
|
|
23282
|
+
status: "in_progress"
|
|
23283
|
+
}
|
|
23284
|
+
});
|
|
23285
|
+
active.toolIds.add(toolCallId);
|
|
23286
|
+
active.emit(event.type === "tool.start" ? {
|
|
23287
|
+
type: "tool.call",
|
|
23288
|
+
toolCallId,
|
|
23289
|
+
update: {
|
|
23290
|
+
sessionUpdate: "tool_call",
|
|
23291
|
+
toolCallId,
|
|
23292
|
+
title: gatewayString(payload.name),
|
|
23293
|
+
rawInput: payload.args,
|
|
23294
|
+
status: "in_progress"
|
|
23295
|
+
}
|
|
23296
|
+
} : {
|
|
23297
|
+
type: "tool.update",
|
|
23298
|
+
toolCallId,
|
|
23299
|
+
update: {
|
|
23300
|
+
sessionUpdate: "tool_call_update",
|
|
23301
|
+
toolCallId,
|
|
23302
|
+
status: result.error || result.is_error === true || result.success === false ? "failed" : "completed",
|
|
23303
|
+
content
|
|
23304
|
+
}
|
|
23305
|
+
});
|
|
23306
|
+
} else if (event.type === "message.complete") {
|
|
23307
|
+
if (!active.streamed)
|
|
23308
|
+
active.emit({ type: "agent.text", text: text2 });
|
|
23309
|
+
else if (text2.startsWith(active.streamed) && text2.length > active.streamed.length)
|
|
23310
|
+
active.emit({ type: "agent.text", text: text2.slice(active.streamed.length) });
|
|
23311
|
+
const reasoning = gatewayString(payload.reasoning);
|
|
23312
|
+
if (reasoning && !active.reasoning)
|
|
23313
|
+
active.emit({ type: "agent.thought", text: reasoning });
|
|
23314
|
+
else if (reasoning.startsWith(active.reasoning) && reasoning.length > active.reasoning.length)
|
|
23315
|
+
active.emit({ type: "agent.thought", text: reasoning.slice(active.reasoning.length) });
|
|
23316
|
+
const usage = gatewayRecord(payload.usage);
|
|
23317
|
+
this.#usage(usage);
|
|
23318
|
+
if (payload.status === "error")
|
|
23319
|
+
active.reject(new Error(gatewayString(payload.error) || gatewayString(payload.failure_reason) || "Hermes turn failed"));
|
|
23320
|
+
else
|
|
23321
|
+
active.resolve({
|
|
23322
|
+
stopReason: payload.status === "interrupted" ? "cancelled" : "end_turn",
|
|
23323
|
+
usage: {
|
|
23324
|
+
inputTokens: Number(usage.input) || 0,
|
|
23325
|
+
outputTokens: Number(usage.output) || 0,
|
|
23326
|
+
totalTokens: Number(usage.total) || 0,
|
|
23327
|
+
...typeof usage.reasoning === "number" ? { thoughtTokens: usage.reasoning } : {}
|
|
23328
|
+
}
|
|
23329
|
+
});
|
|
23330
|
+
} else if (event.type === "error")
|
|
23331
|
+
active.reject(new Error(gatewayString(payload.message) || "Hermes gateway failed"));
|
|
23332
|
+
}
|
|
23333
|
+
#usage(usage) {
|
|
23334
|
+
this.#active?.emit({
|
|
23335
|
+
type: "usage",
|
|
23336
|
+
...typeof usage.context_used === "number" ? { used: usage.context_used } : {},
|
|
23337
|
+
...typeof usage.context_max === "number" ? { size: usage.context_max } : {}
|
|
23338
|
+
});
|
|
23339
|
+
}
|
|
23340
|
+
async #request(id, method, params) {
|
|
23341
|
+
const active = this.#active;
|
|
23342
|
+
if (!active || params.session_id !== this.sessionId || !["clarify", "approval"].includes(method)) {
|
|
23343
|
+
this.transport.rejectRequest(id);
|
|
23344
|
+
return;
|
|
23345
|
+
}
|
|
23346
|
+
const controller = new AbortController();
|
|
23347
|
+
this.#requests.set(id, controller);
|
|
23348
|
+
try {
|
|
23349
|
+
if (method === "clarify" && active.question) {
|
|
23350
|
+
const batch = Array.isArray(params.questions);
|
|
23351
|
+
const questions = batch ? params.questions.map((value, index) => question(gatewayRecord(value), String(index))) : [question(params, "answer")];
|
|
23352
|
+
const response = await active.question({
|
|
23353
|
+
title: "Hermes",
|
|
23354
|
+
questions,
|
|
23355
|
+
signal: controller.signal
|
|
23356
|
+
});
|
|
23357
|
+
if (!controller.signal.aborted) {
|
|
23358
|
+
const answers = Object.fromEntries(Object.entries(response.answers).map(([key, values]) => {
|
|
23359
|
+
const q = questions.find((entry) => entry.id === key);
|
|
23360
|
+
return [
|
|
23361
|
+
key,
|
|
23362
|
+
q?.type === "choice" && q.multiple ? JSON.stringify(values) : values[0] ?? ""
|
|
23363
|
+
];
|
|
23364
|
+
}));
|
|
23365
|
+
this.transport.respond(id, response.cancelled ? {} : batch ? { answers } : { answer: answers.answer ?? "" });
|
|
23366
|
+
}
|
|
23367
|
+
} else if (method === "approval") {
|
|
23368
|
+
const choices = Array.isArray(params.choices) ? params.choices : ["once", "deny"];
|
|
23369
|
+
const options = choices.flatMap((choice) => choice === "once" ? [{ optionId: "once", kind: "allow_once", name: "Allow once" }] : choice === "session" || choice === "always" ? [
|
|
23370
|
+
{
|
|
23371
|
+
optionId: choice,
|
|
23372
|
+
kind: "allow_always",
|
|
23373
|
+
name: choice === "session" ? "Allow for this session" : "Always allow"
|
|
23374
|
+
}
|
|
23375
|
+
] : choice === "deny" ? [{ optionId: "deny", kind: "reject_once", name: "Deny" }] : []);
|
|
23376
|
+
const response = await active.permission({
|
|
23377
|
+
signal: controller.signal,
|
|
23378
|
+
effects: { session: "allowForSession", always: "allowAlways" },
|
|
23379
|
+
description: gatewayString(params.command) || gatewayString(params.description),
|
|
23380
|
+
request: {
|
|
23381
|
+
sessionId: this.nativeSessionId,
|
|
23382
|
+
toolCall: {
|
|
23383
|
+
toolCallId: gatewayString(params.request_id),
|
|
23384
|
+
title: gatewayString(params.description) || gatewayString(params.command)
|
|
23385
|
+
},
|
|
23386
|
+
options
|
|
23387
|
+
},
|
|
23388
|
+
options
|
|
23389
|
+
});
|
|
23390
|
+
if (!controller.signal.aborted)
|
|
23391
|
+
this.transport.respond(id, {
|
|
23392
|
+
choice: response.outcome.outcome === "selected" ? response.outcome.optionId : "deny"
|
|
23393
|
+
});
|
|
23394
|
+
} else
|
|
23395
|
+
this.transport.rejectRequest(id);
|
|
23396
|
+
} finally {
|
|
23397
|
+
this.#requests.delete(id);
|
|
23398
|
+
}
|
|
23399
|
+
}
|
|
23400
|
+
};
|
|
23401
|
+
|
|
23402
|
+
// dist/gateway-open.js
|
|
23403
|
+
import path6 from "node:path";
|
|
23404
|
+
import { realpath } from "node:fs/promises";
|
|
23405
|
+
|
|
23406
|
+
// dist/gateway-configuration.js
|
|
23407
|
+
import { execFile } from "node:child_process";
|
|
23408
|
+
import { promisify } from "node:util";
|
|
23409
|
+
async function resolveGatewayModel(transport, modelId) {
|
|
23410
|
+
if (/\s/u.test(modelId) || modelId.startsWith("-"))
|
|
23411
|
+
throw new Error("Invalid Hermes Model identifier");
|
|
23412
|
+
const result = await promisify(execFile)(transport.python, [
|
|
23413
|
+
"-I",
|
|
23414
|
+
"-c",
|
|
23415
|
+
"import json,sys\nfrom hermes_cli.models import parse_model_input\np,m=parse_model_input(sys.argv[1], '')\nprint(json.dumps({'provider':p,'model':m}))",
|
|
23416
|
+
modelId
|
|
23417
|
+
], {
|
|
23418
|
+
cwd: transport.cwd,
|
|
23419
|
+
env: { ...process.env, ...transport.environment },
|
|
23420
|
+
timeout: 2e4,
|
|
23421
|
+
maxBuffer: 1024 * 1024
|
|
23422
|
+
});
|
|
23423
|
+
const value = JSON.parse(result.stdout.trim());
|
|
23424
|
+
if (!value || typeof value !== "object" || !("provider" in value) || !("model" in value) || typeof value.provider !== "string" || typeof value.model !== "string")
|
|
23425
|
+
throw new Error("Hermes returned an invalid Model choice");
|
|
23426
|
+
return { provider: value.provider, model: value.model };
|
|
23427
|
+
}
|
|
23428
|
+
|
|
23429
|
+
// dist/gateway-open.js
|
|
23430
|
+
async function sameDirectory(left, right) {
|
|
23431
|
+
return await realpath(left).catch(() => path6.resolve(left)) === await realpath(right).catch(() => path6.resolve(right));
|
|
23432
|
+
}
|
|
23433
|
+
var gatewayCapabilities = {
|
|
23434
|
+
configuration: {
|
|
23435
|
+
selectModel: true,
|
|
23436
|
+
selectThinkingOption: true,
|
|
23437
|
+
selectPermissionMode: true,
|
|
23438
|
+
permissionModeScope: "live"
|
|
23439
|
+
},
|
|
23440
|
+
history: { fork: true, forkAcrossCwd: false, rollbackLastTurn: true }
|
|
23441
|
+
};
|
|
23442
|
+
function gatewayPermissionModes() {
|
|
23443
|
+
return harnessPermissionModeCatalogSchema.parse({
|
|
23444
|
+
modes: [
|
|
23445
|
+
{
|
|
23446
|
+
id: "default",
|
|
23447
|
+
label: "原生审批策略",
|
|
23448
|
+
description: "遵循 Hermes 全局审批策略,关闭本会话 YOLO。"
|
|
23449
|
+
},
|
|
23450
|
+
{
|
|
23451
|
+
id: "dont_ask",
|
|
23452
|
+
label: "会话 YOLO",
|
|
23453
|
+
description: "本会话自动批准 Hermes 工具操作。",
|
|
23454
|
+
dangerous: true
|
|
23455
|
+
}
|
|
23456
|
+
],
|
|
23457
|
+
defaultModeId: "default"
|
|
23458
|
+
});
|
|
23459
|
+
}
|
|
23460
|
+
function isGatewayRef(ref) {
|
|
23461
|
+
return ref.harnessId === "hermes" && gatewayRecord(ref.locator).transport === "gateway";
|
|
23462
|
+
}
|
|
23463
|
+
async function openGatewaySession(input, transport, onSettle, inheritedPermissionMode) {
|
|
23464
|
+
const source = input.kind === "create" ? null : input.kind === "resume" ? input.nativeRef : input.sourceRef;
|
|
23465
|
+
if (source && !isGatewayRef(source))
|
|
23466
|
+
throw new HermesGatewayHistoryError("unsupported", "This Hermes native reference is not a supported gateway Session");
|
|
23467
|
+
const savedMode = source ? gatewayRecord(source.locator).permissionModeId : void 0;
|
|
23468
|
+
const mode = (input.kind === "fork" ? inheritedPermissionMode : input.permissionModeId) ?? (typeof savedMode === "string" ? savedMode : void 0);
|
|
23469
|
+
if (mode && mode !== "default" && mode !== "dont_ask")
|
|
23470
|
+
throw new HermesGatewayHistoryError("unsupported", "Hermes gateway supports native policy and session YOLO; accept_edits remains ACP-only");
|
|
23471
|
+
if (input.kind === "create" && input.executionPolicy === "unattended-full-access" && mode && mode !== "dont_ask")
|
|
23472
|
+
throw new HermesGatewayHistoryError("invalidRequest", "unattended-full-access requires dont_ask");
|
|
23473
|
+
const model = input.kind !== "fork" && input.model ? decodeHermesModelRefId(input.model.id) : null;
|
|
23474
|
+
if (input.kind !== "fork" && input.model && !model)
|
|
23475
|
+
throw new HermesGatewayHistoryError("invalidRequest", "Model Ref does not belong to Hermes");
|
|
23476
|
+
if (source && gatewayRecord(source.locator).cwd && !await sameDirectory(String(gatewayRecord(source?.locator).cwd), input.cwd))
|
|
23477
|
+
throw new HermesGatewayHistoryError("unsupported", "Hermes gateway preserves the native Session working directory");
|
|
23478
|
+
let nativeRef = source;
|
|
23479
|
+
let sourceHistory;
|
|
23480
|
+
try {
|
|
23481
|
+
await transport.prepareSession();
|
|
23482
|
+
await transport.start();
|
|
23483
|
+
if (source && (input.kind === "fork" || input.kind === "rollbackLastTurn")) {
|
|
23484
|
+
sourceHistory = new HermesGatewayHistory({
|
|
23485
|
+
python: transport.python,
|
|
23486
|
+
cwd: transport.cwd,
|
|
23487
|
+
environment: transport.environment,
|
|
23488
|
+
nativeSessionId: source.nativeSessionId
|
|
23489
|
+
});
|
|
23490
|
+
nativeRef = await sourceHistory.derive(input.kind === "fork" ? { checkpoint: input.checkpoint } : { rollbackLastTurn: true });
|
|
23491
|
+
}
|
|
23492
|
+
const choice = model ? await resolveGatewayModel(transport, model) : null;
|
|
23493
|
+
const response = await transport.request(nativeRef ? "session.resume" : "session.create", nativeRef ? { session_id: nativeRef.nativeSessionId, eager_build: true, omit_messages: true } : {
|
|
23494
|
+
cwd: input.cwd,
|
|
23495
|
+
close_on_disconnect: true,
|
|
23496
|
+
...choice ? choice : {},
|
|
23497
|
+
...input.kind === "create" && input.thinkingOptionId ? { reasoning_effort: input.thinkingOptionId } : {}
|
|
23498
|
+
});
|
|
23499
|
+
const runtimeId = gatewayString(response.session_id);
|
|
23500
|
+
const storedId = nativeRef?.nativeSessionId || gatewayString(response.stored_session_id);
|
|
23501
|
+
if (!runtimeId || !storedId)
|
|
23502
|
+
throw new Error("Hermes gateway did not return Session identity");
|
|
23503
|
+
let info = gatewayRecord(response.info);
|
|
23504
|
+
if (info.lazy === true)
|
|
23505
|
+
info = await transport.waitForSession(runtimeId);
|
|
23506
|
+
const nativeCwd = gatewayString(info.cwd);
|
|
23507
|
+
if (nativeCwd && !await sameDirectory(nativeCwd, input.cwd))
|
|
23508
|
+
throw new HermesGatewayHistoryError("unsupported", "Hermes resumed a different native working directory");
|
|
23509
|
+
const bridge = new HermesGatewaySessionTransport(transport, runtimeId, storedId, info);
|
|
23510
|
+
if (!nativeRef)
|
|
23511
|
+
await bridge.history.ensureCreated({
|
|
23512
|
+
cwd: input.cwd,
|
|
23513
|
+
model: gatewayString(info.model),
|
|
23514
|
+
provider: gatewayString(info.provider),
|
|
23515
|
+
reasoningEffort: gatewayString(info.reasoning_effort)
|
|
23516
|
+
});
|
|
23517
|
+
const physicalId = await bridge.history.resolvePhysicalSessionId();
|
|
23518
|
+
if (gatewayString(info.stored_session_id) !== physicalId)
|
|
23519
|
+
throw new Error("Hermes live Session and persisted history identities differ");
|
|
23520
|
+
if (model)
|
|
23521
|
+
await bridge.setModel(model);
|
|
23522
|
+
if (input.kind !== "fork" && input.thinkingOptionId)
|
|
23523
|
+
await bridge.setThinking(input.thinkingOptionId);
|
|
23524
|
+
const desiredMode = input.kind === "create" && input.executionPolicy === "unattended-full-access" ? "dont_ask" : mode;
|
|
23525
|
+
if (desiredMode)
|
|
23526
|
+
await bridge.setPermissionMode(desiredMode);
|
|
23527
|
+
const open = await bridge.openResult();
|
|
23528
|
+
return new HermesSession({
|
|
23529
|
+
nativeRef: nativeSessionRefSchema.parse({
|
|
23530
|
+
harnessId: "hermes",
|
|
23531
|
+
nativeSessionId: storedId,
|
|
23532
|
+
formatVersion: 1,
|
|
23533
|
+
locator: {
|
|
23534
|
+
transport: "gateway",
|
|
23535
|
+
cwd: input.cwd,
|
|
23536
|
+
permissionModeId: open.session.modes?.currentModeId
|
|
23537
|
+
}
|
|
23538
|
+
}),
|
|
23539
|
+
transport: bridge,
|
|
23540
|
+
open,
|
|
23541
|
+
supportsDerivation: true,
|
|
23542
|
+
onSettle
|
|
23543
|
+
});
|
|
23544
|
+
} catch (error51) {
|
|
23545
|
+
await transport.close().catch(() => void 0);
|
|
23546
|
+
if (sourceHistory && nativeRef)
|
|
23547
|
+
await sourceHistory.discardDerived(nativeRef).catch(() => false);
|
|
23548
|
+
throw error51;
|
|
23549
|
+
}
|
|
23550
|
+
}
|
|
23551
|
+
|
|
21324
23552
|
// dist/hermes-adapter.js
|
|
21325
23553
|
var hermesHarnessId = harnessIdSchema.parse("hermes");
|
|
21326
23554
|
var IMPORT_TIMEOUT_MS = 2e4;
|
|
21327
23555
|
var HERMES_THREAD_ID_ENV = "CODEXHOST_THREAD_ID";
|
|
21328
23556
|
var HermesAdapter = class {
|
|
23557
|
+
commandCatalog = HERMES_COMMAND_CATALOG;
|
|
21329
23558
|
harnessId = hermesHarnessId;
|
|
21330
23559
|
sessionImport = {
|
|
21331
23560
|
listCandidates: () => this.#listImportCandidates(),
|
|
@@ -21333,11 +23562,16 @@ var HermesAdapter = class {
|
|
|
21333
23562
|
};
|
|
21334
23563
|
#options;
|
|
21335
23564
|
#inspectionCache = null;
|
|
23565
|
+
#lastInventory = null;
|
|
23566
|
+
#inventoryRead = null;
|
|
21336
23567
|
#inspectionCacheScope = null;
|
|
21337
23568
|
#sessions = /* @__PURE__ */ new Set();
|
|
21338
23569
|
#warmTransports = /* @__PURE__ */ new Map();
|
|
21339
23570
|
#transports = /* @__PURE__ */ new Set();
|
|
21340
23571
|
#closed = false;
|
|
23572
|
+
#gatewayTransports = /* @__PURE__ */ new Set();
|
|
23573
|
+
#gatewayProbes = /* @__PURE__ */ new Map();
|
|
23574
|
+
#openingNativeIds = /* @__PURE__ */ new Set();
|
|
21341
23575
|
constructor(options = {}) {
|
|
21342
23576
|
this.#options = options;
|
|
21343
23577
|
}
|
|
@@ -21347,6 +23581,29 @@ var HermesAdapter = class {
|
|
|
21347
23581
|
return this.#inspectionCache;
|
|
21348
23582
|
}
|
|
21349
23583
|
const environment = this.#effectiveEnvironment();
|
|
23584
|
+
if (input.refresh)
|
|
23585
|
+
this.#gatewayProbes.clear();
|
|
23586
|
+
const gatewayPython = await this.#gatewayPython(cwd, environment);
|
|
23587
|
+
if (gatewayPython) {
|
|
23588
|
+
try {
|
|
23589
|
+
const catalog = catalogModelsFromInventory(await this.#readInventory());
|
|
23590
|
+
const inspection = {
|
|
23591
|
+
status: "ready",
|
|
23592
|
+
catalog: {
|
|
23593
|
+
models: catalog.models.map(({ ref, label }) => ({ ref, label })),
|
|
23594
|
+
thinkingOptions: hermesGatewayThinkingOptions,
|
|
23595
|
+
...catalog.defaultModel ? { defaultModel: catalog.defaultModel } : {}
|
|
23596
|
+
},
|
|
23597
|
+
permissionModes: gatewayPermissionModes(),
|
|
23598
|
+
capabilities: gatewayCapabilities
|
|
23599
|
+
};
|
|
23600
|
+
this.#inspectionCache = inspection;
|
|
23601
|
+
this.#inspectionCacheScope = cwd;
|
|
23602
|
+
return inspection;
|
|
23603
|
+
} catch (error51) {
|
|
23604
|
+
return inspectionFromTransportError(error51);
|
|
23605
|
+
}
|
|
23606
|
+
}
|
|
21350
23607
|
const transport = await this.#takeTransport(cwd, environment);
|
|
21351
23608
|
let retainedForOpen = false;
|
|
21352
23609
|
try {
|
|
@@ -21391,13 +23648,26 @@ var HermesAdapter = class {
|
|
|
21391
23648
|
}
|
|
21392
23649
|
}
|
|
21393
23650
|
async #readInventory() {
|
|
23651
|
+
if (this.#inventoryRead)
|
|
23652
|
+
return this.#inventoryRead;
|
|
21394
23653
|
const executable = resolveHermesExecutable({
|
|
21395
23654
|
...this.#options.command ? { command: this.#options.command } : {},
|
|
21396
23655
|
...this.#options.environment ? { environment: this.#options.environment } : {}
|
|
21397
23656
|
});
|
|
21398
|
-
|
|
23657
|
+
this.#inventoryRead = readHermesModelInventory(executable, 2e4, {
|
|
21399
23658
|
...this.#options.environment ? { environment: this.#options.environment } : {}
|
|
23659
|
+
}).then((inventory) => {
|
|
23660
|
+
if (!this.#closed)
|
|
23661
|
+
this.#lastInventory = inventory;
|
|
23662
|
+
return inventory;
|
|
23663
|
+
}).catch((error51) => {
|
|
23664
|
+
if (error51 instanceof HermesInventoryTimeoutError && this.#lastInventory)
|
|
23665
|
+
return this.#lastInventory;
|
|
23666
|
+
throw error51;
|
|
23667
|
+
}).finally(() => {
|
|
23668
|
+
this.#inventoryRead = null;
|
|
21400
23669
|
});
|
|
23670
|
+
return this.#inventoryRead;
|
|
21401
23671
|
}
|
|
21402
23672
|
async open(input) {
|
|
21403
23673
|
if (this.#closed) {
|
|
@@ -21407,6 +23677,23 @@ var HermesAdapter = class {
|
|
|
21407
23677
|
if (typeof cwd !== "string" || cwd.trim().length === 0) {
|
|
21408
23678
|
return failure("invalidRequest", "open requires a cwd");
|
|
21409
23679
|
}
|
|
23680
|
+
const nativeRef = input.kind === "create" ? null : input.kind === "resume" ? input.nativeRef : input.sourceRef;
|
|
23681
|
+
const gatewayEnvironment = {
|
|
23682
|
+
...this.#options.environment ?? process.env,
|
|
23683
|
+
...input.environment ?? {}
|
|
23684
|
+
};
|
|
23685
|
+
const environment = this.#effectiveEnvironment(input.environment);
|
|
23686
|
+
if (input.kind === "resume" && (this.#openingNativeIds.has(input.nativeRef.nativeSessionId) || [...this.#sessions].some((s) => s.initialState.nativeRef?.nativeSessionId === input.nativeRef.nativeSessionId)))
|
|
23687
|
+
return failure("sessionBusy", "This Hermes Session already has an owner", true);
|
|
23688
|
+
if (!nativeRef || isGatewayRef(nativeRef)) {
|
|
23689
|
+
const python = await this.#gatewayPython(cwd, gatewayEnvironment);
|
|
23690
|
+
if (this.#closed)
|
|
23691
|
+
return failure("invalidState", "Hermes Adapter is closed");
|
|
23692
|
+
if (python)
|
|
23693
|
+
return this.#openGateway(input, python, gatewayEnvironment);
|
|
23694
|
+
if (nativeRef)
|
|
23695
|
+
return failure("unavailable", "Hermes gateway Session requires an available gateway with exclusive turn support");
|
|
23696
|
+
}
|
|
21410
23697
|
let transportOpen;
|
|
21411
23698
|
let permissionModeId;
|
|
21412
23699
|
if (input.kind === "create") {
|
|
@@ -21430,7 +23717,6 @@ var HermesAdapter = class {
|
|
|
21430
23717
|
if (permissionModeId && !isHermesModeId(permissionModeId)) {
|
|
21431
23718
|
return failure("invalidRequest", "Permission Mode does not belong to Hermes");
|
|
21432
23719
|
}
|
|
21433
|
-
const environment = this.#effectiveEnvironment(input.environment);
|
|
21434
23720
|
const transport = await this.#takeTransport(cwd, environment);
|
|
21435
23721
|
if (this.#closed) {
|
|
21436
23722
|
await this.#releaseTransport(transport);
|
|
@@ -21506,16 +23792,80 @@ var HermesAdapter = class {
|
|
|
21506
23792
|
return failure("nativeFailure", error51 instanceof Error ? error51.message : "Hermes Session open failed");
|
|
21507
23793
|
}
|
|
21508
23794
|
}
|
|
23795
|
+
async #gatewayPython(cwd, environment) {
|
|
23796
|
+
const key = `${cwd}:${this.#transportScope(cwd, environment)}`;
|
|
23797
|
+
let pending = this.#gatewayProbes.get(key);
|
|
23798
|
+
if (!pending) {
|
|
23799
|
+
pending = (async () => {
|
|
23800
|
+
try {
|
|
23801
|
+
return await HermesGatewayTransport.probe(resolveHermesExecutable({
|
|
23802
|
+
...this.#options.command ? { command: this.#options.command } : {},
|
|
23803
|
+
environment
|
|
23804
|
+
}), cwd, environment);
|
|
23805
|
+
} catch {
|
|
23806
|
+
return null;
|
|
23807
|
+
}
|
|
23808
|
+
})();
|
|
23809
|
+
this.#gatewayProbes.set(key, pending);
|
|
23810
|
+
}
|
|
23811
|
+
return pending;
|
|
23812
|
+
}
|
|
23813
|
+
async #openGateway(input, python, environment) {
|
|
23814
|
+
if (this.#closed)
|
|
23815
|
+
return failure("invalidState", "Hermes Adapter is closed");
|
|
23816
|
+
const ref = input.kind === "resume" ? input.nativeRef : null;
|
|
23817
|
+
if (input.kind === "fork" || input.kind === "rollbackLastTurn") {
|
|
23818
|
+
const source = [...this.#sessions].find((s) => s.initialState.nativeRef?.nativeSessionId === input.sourceRef.nativeSessionId);
|
|
23819
|
+
if (source?.busy)
|
|
23820
|
+
return failure("sessionBusy", "Cannot derive a Hermes Session while its source Turn is active", true);
|
|
23821
|
+
}
|
|
23822
|
+
const owned = (id) => this.#openingNativeIds.has(id) || [...this.#sessions].some((session) => session.initialState.nativeRef?.nativeSessionId === id);
|
|
23823
|
+
if (ref && owned(ref.nativeSessionId))
|
|
23824
|
+
return failure("sessionBusy", "This Hermes Session already has an owner", true);
|
|
23825
|
+
if (ref)
|
|
23826
|
+
this.#openingNativeIds.add(ref.nativeSessionId);
|
|
23827
|
+
const transport = new HermesGatewayTransport(python, input.cwd, environment, this.#options.commandTimeoutMs);
|
|
23828
|
+
this.#gatewayTransports.add(transport);
|
|
23829
|
+
try {
|
|
23830
|
+
let inheritedMode;
|
|
23831
|
+
if (input.kind === "fork") {
|
|
23832
|
+
const source = [...this.#sessions].find((s) => s.initialState.nativeRef?.nativeSessionId === input.sourceRef.nativeSessionId);
|
|
23833
|
+
const snapshot = await source?.readSnapshot();
|
|
23834
|
+
if (snapshot?.ok)
|
|
23835
|
+
inheritedMode = snapshot.value.state?.effectivePermissionModeId;
|
|
23836
|
+
}
|
|
23837
|
+
const session = await openGatewaySession(input, transport, (settled) => {
|
|
23838
|
+
this.#sessions.delete(settled);
|
|
23839
|
+
this.#gatewayTransports.delete(transport);
|
|
23840
|
+
}, inheritedMode);
|
|
23841
|
+
if (this.#closed) {
|
|
23842
|
+
await session.close();
|
|
23843
|
+
return failure("invalidState", "Hermes Adapter is closed");
|
|
23844
|
+
}
|
|
23845
|
+
this.#sessions.add(session);
|
|
23846
|
+
return { ok: true, value: session };
|
|
23847
|
+
} catch (error51) {
|
|
23848
|
+
await transport.close().catch(() => void 0);
|
|
23849
|
+
this.#gatewayTransports.delete(transport);
|
|
23850
|
+
return failure(error51 instanceof HermesGatewayHistoryError ? error51.code : "nativeFailure", error51 instanceof Error ? error51.message : "Hermes gateway open failed");
|
|
23851
|
+
} finally {
|
|
23852
|
+
if (ref)
|
|
23853
|
+
this.#openingNativeIds.delete(ref.nativeSessionId);
|
|
23854
|
+
}
|
|
23855
|
+
}
|
|
21509
23856
|
async close() {
|
|
21510
23857
|
if (this.#closed)
|
|
21511
23858
|
return;
|
|
21512
23859
|
this.#closed = true;
|
|
21513
23860
|
this.#inspectionCache = null;
|
|
23861
|
+
this.#lastInventory = null;
|
|
21514
23862
|
const sessions = [...this.#sessions];
|
|
21515
23863
|
this.#sessions.clear();
|
|
21516
23864
|
this.#warmTransports.clear();
|
|
21517
23865
|
await Promise.all(sessions.map((session) => session.close().catch(() => void 0)));
|
|
21518
23866
|
await Promise.all([...this.#transports].map((transport) => this.#releaseTransport(transport)));
|
|
23867
|
+
await Promise.all([...this.#gatewayTransports].map((transport) => transport.close()));
|
|
23868
|
+
this.#gatewayTransports.clear();
|
|
21519
23869
|
}
|
|
21520
23870
|
#effectiveEnvironment(environment) {
|
|
21521
23871
|
const merged = { ...this.#options.environment ?? process.env, ...environment ?? {} };
|