@codexhost/cli-darwin-x64 0.8.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THIRD_PARTY_NOTICES.txt +12 -0
- package/app/codexhost-distribution.json +1 -1
- package/app/desktop-controller.mjs +67 -26
- package/app/host-runtime.mjs +3774 -6483
- package/app/plugins/antigravity/plugin.mjs +114 -93
- package/app/plugins/claude-code/plugin.mjs +480 -239
- package/app/plugins/codebuddy/plugin.mjs +2177 -846
- package/app/plugins/cursor-cli/plugin.mjs +35065 -10156
- package/app/plugins/deepseek-harness/plugin.mjs +72 -84
- package/app/plugins/enabled.json +4 -1
- package/app/plugins/grok/plugin.mjs +60 -78
- package/app/plugins/hermes/plugin.mjs +2431 -174
- package/app/plugins/kiro-cli/plugin.mjs +67 -80
- package/app/plugins/omp/plugin.mjs +106 -91
- package/app/plugins/opencode/plugin.mjs +109 -123
- package/app/plugins/pi/plugin.mjs +71 -84
- package/app/plugins/qoder/assets/icon.svg +1 -0
- package/app/plugins/qoder/manifest.json +12 -0
- package/app/plugins/qoder/plugin.mjs +52267 -0
- package/app/plugins/qoder-cn/assets/icon.svg +1 -0
- package/app/plugins/qoder-cn/manifest.json +12 -0
- package/app/plugins/qoder-cn/plugin.mjs +52265 -0
- package/app/plugins/workbuddy/assets/icon.svg +29 -0
- package/app/plugins/workbuddy/manifest.json +14 -0
- package/app/plugins/workbuddy/plugin.mjs +24818 -0
- package/app/renderer-extension.js +3182 -960
- package/bin/codexhost +0 -0
- package/libexec/codexhost-shim +0 -0
- package/libexec/codexhost-updater +0 -0
- package/licenses/Qoder-Agent-SDK-LICENSE.txt +7 -0
- package/licenses/QoderCN-Agent-SDK-LICENSE.txt +7 -0
- package/licenses/tailwindcss-LICENSE.txt +21 -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
|
}
|
|
@@ -14525,6 +14522,46 @@ config(en_default());
|
|
|
14525
14522
|
// ../../shared-contracts/dist/version.js
|
|
14526
14523
|
var WORKSPACE_CONTRACT_VERSION = 1;
|
|
14527
14524
|
|
|
14525
|
+
// ../../shared-contracts/dist/idle-release.js
|
|
14526
|
+
var IDLE_RELEASE_TIMEOUT_MINUTES_MIN = 5;
|
|
14527
|
+
var IDLE_RELEASE_TIMEOUT_MINUTES_MAX = 1440;
|
|
14528
|
+
var idleReleaseSettingsSchema = external_exports.strictObject({
|
|
14529
|
+
enabled: external_exports.boolean(),
|
|
14530
|
+
timeoutMinutes: external_exports.number().int().min(IDLE_RELEASE_TIMEOUT_MINUTES_MIN).max(IDLE_RELEASE_TIMEOUT_MINUTES_MAX)
|
|
14531
|
+
});
|
|
14532
|
+
var DEFAULT_IDLE_RELEASE_SETTINGS = Object.freeze({
|
|
14533
|
+
enabled: false,
|
|
14534
|
+
timeoutMinutes: 30
|
|
14535
|
+
});
|
|
14536
|
+
|
|
14537
|
+
// ../../shared-contracts/dist/loaded-sessions.js
|
|
14538
|
+
var loadedSessionStateSchema = external_exports.enum([
|
|
14539
|
+
"idle",
|
|
14540
|
+
"running",
|
|
14541
|
+
"busy",
|
|
14542
|
+
"closing",
|
|
14543
|
+
"failed",
|
|
14544
|
+
"blocked"
|
|
14545
|
+
]);
|
|
14546
|
+
var loadedSessionReasonSchema = external_exports.enum([
|
|
14547
|
+
"none",
|
|
14548
|
+
"disabled",
|
|
14549
|
+
"timeout",
|
|
14550
|
+
"operation",
|
|
14551
|
+
"background",
|
|
14552
|
+
"identity",
|
|
14553
|
+
"persistence",
|
|
14554
|
+
"closeFailed"
|
|
14555
|
+
]);
|
|
14556
|
+
var loadedSessionsSchema = external_exports.array(external_exports.strictObject({
|
|
14557
|
+
threadId: external_exports.string(),
|
|
14558
|
+
title: external_exports.string(),
|
|
14559
|
+
harnessId: external_exports.string(),
|
|
14560
|
+
state: loadedSessionStateSchema,
|
|
14561
|
+
reason: loadedSessionReasonSchema,
|
|
14562
|
+
inactiveMs: external_exports.number().nonnegative()
|
|
14563
|
+
}));
|
|
14564
|
+
|
|
14528
14565
|
// ../../shared-contracts/dist/ids.js
|
|
14529
14566
|
var opaqueIdSchema = external_exports.string().refine((value) => value.trim().length > 0, {
|
|
14530
14567
|
message: "Identifier must not be empty or whitespace"
|
|
@@ -15010,6 +15047,8 @@ var pluginPresentationShape = {
|
|
|
15010
15047
|
id: harnessPluginIdSchema,
|
|
15011
15048
|
name: external_exports.string().trim().min(1).max(128),
|
|
15012
15049
|
version: external_exports.string().min(1).max(128),
|
|
15050
|
+
/** The factory accepts a persisted local entrypoint through its construction context. */
|
|
15051
|
+
launchCommand: external_exports.literal(true).optional(),
|
|
15013
15052
|
links: external_exports.object({
|
|
15014
15053
|
documentation: documentationUrlSchema.optional(),
|
|
15015
15054
|
installation: documentationUrlSchema.optional()
|
|
@@ -15047,6 +15086,19 @@ var harnessPluginRouteSchema = external_exports.object({
|
|
|
15047
15086
|
permissionModeId: harnessPermissionModeIdSchema.optional()
|
|
15048
15087
|
}).strict();
|
|
15049
15088
|
|
|
15089
|
+
// ../../shared-contracts/dist/harness-launch-settings.js
|
|
15090
|
+
var harnessLaunchPathSchema = external_exports.string().trim().min(1).max(4096).refine((value) => !/[\u0000\r\n]/u.test(value), "Invalid entrypoint path");
|
|
15091
|
+
var harnessLaunchSettingsGetSchema = external_exports.object({
|
|
15092
|
+
harnessId: harnessPluginIdSchema
|
|
15093
|
+
}).strict();
|
|
15094
|
+
var harnessLaunchSettingsSetSchema = harnessLaunchSettingsGetSchema.extend({
|
|
15095
|
+
path: harnessLaunchPathSchema.nullable()
|
|
15096
|
+
});
|
|
15097
|
+
var harnessLaunchSettingsSchema = external_exports.object({
|
|
15098
|
+
path: harnessLaunchPathSchema.nullable(),
|
|
15099
|
+
restartRequired: external_exports.boolean()
|
|
15100
|
+
}).strict();
|
|
15101
|
+
|
|
15050
15102
|
// ../../shared-contracts/dist/codex-accounts.js
|
|
15051
15103
|
var accountIdSchema = external_exports.string().min(1).max(256).regex(/^[A-Za-z0-9._~-]+$/u);
|
|
15052
15104
|
var nonBlankTextSchema3 = external_exports.string().trim().min(1);
|
|
@@ -15075,75 +15127,15 @@ var codexAccountSchema = external_exports.object({
|
|
|
15075
15127
|
email: external_exports.string().email().max(320).optional(),
|
|
15076
15128
|
planType: codexAccountPlanTypeSchema.optional()
|
|
15077
15129
|
}).strict();
|
|
15078
|
-
var codexAccountPhaseSchema = external_exports.enum(["ready", "
|
|
15079
|
-
var codexAccountPendingOperationSchema = external_exports.object({
|
|
15080
|
-
operationId: nonBlankTextSchema3.max(1024),
|
|
15081
|
-
kind: external_exports.enum(["login", "switch", "logout", "recovery"])
|
|
15082
|
-
}).strict();
|
|
15083
|
-
var codexAccountCapabilitiesSchema = external_exports.object({
|
|
15084
|
-
manage: external_exports.boolean(),
|
|
15085
|
-
switch: external_exports.boolean(),
|
|
15086
|
-
login: external_exports.boolean(),
|
|
15087
|
-
delete: external_exports.boolean(),
|
|
15088
|
-
recover: external_exports.boolean().optional(),
|
|
15089
|
-
logout: external_exports.boolean().optional(),
|
|
15090
|
-
reason: external_exports.enum([
|
|
15091
|
-
"ssh-single-account",
|
|
15092
|
-
"unsupported-storage",
|
|
15093
|
-
"unsupported-version",
|
|
15094
|
-
"recovery-required",
|
|
15095
|
-
"keyring-unavailable",
|
|
15096
|
-
"migration-required"
|
|
15097
|
-
]).optional()
|
|
15098
|
-
}).strict();
|
|
15130
|
+
var codexAccountPhaseSchema = external_exports.enum(["ready", "unavailable"]);
|
|
15099
15131
|
var codexAccountListResultSchema = external_exports.object({
|
|
15100
15132
|
version: external_exports.literal(2),
|
|
15101
15133
|
currentAccountId: accountIdSchema.nullable(),
|
|
15102
15134
|
phase: codexAccountPhaseSchema,
|
|
15103
15135
|
revision: external_exports.number().int().nonnegative(),
|
|
15104
15136
|
instanceId: nonBlankTextSchema3.max(1024).optional(),
|
|
15105
|
-
|
|
15106
|
-
/** Credentials were adopted; other native homes/history remain unmerged. */
|
|
15107
|
-
legacyHistoryPreserved: external_exports.boolean().optional(),
|
|
15108
|
-
pendingOperation: codexAccountPendingOperationSchema.optional(),
|
|
15109
|
-
capabilities: codexAccountCapabilitiesSchema,
|
|
15110
|
-
accounts: external_exports.array(codexAccountSchema).max(128)
|
|
15137
|
+
accounts: external_exports.array(codexAccountSchema).max(1)
|
|
15111
15138
|
}).strict();
|
|
15112
|
-
var codexAccountActivateParamsSchema = external_exports.object({ accountId: accountIdSchema }).strict();
|
|
15113
|
-
var codexAccountSwitchParamsSchema = external_exports.object({ accountId: accountIdSchema }).strict();
|
|
15114
|
-
var codexAccountReadyMutationResultShape = {
|
|
15115
|
-
phase: external_exports.literal("ready"),
|
|
15116
|
-
revision: external_exports.number().int().nonnegative()
|
|
15117
|
-
};
|
|
15118
|
-
var codexAccountSwitchResultSchema = external_exports.object({
|
|
15119
|
-
currentAccountId: accountIdSchema,
|
|
15120
|
-
...codexAccountReadyMutationResultShape
|
|
15121
|
-
}).strict();
|
|
15122
|
-
var codexAccountDeleteParamsSchema = external_exports.object({ accountId: accountIdSchema }).strict();
|
|
15123
|
-
var codexAccountDeleteResultSchema = external_exports.object({ deletedAccountId: accountIdSchema }).strict();
|
|
15124
|
-
var codexAccountLoginStartParamsSchema = external_exports.object({ accountId: accountIdSchema.optional() }).strict();
|
|
15125
|
-
var codexAccountLoginStartResultSchema = external_exports.object({
|
|
15126
|
-
accountId: accountIdSchema,
|
|
15127
|
-
loginId: nonBlankTextSchema3.max(1024),
|
|
15128
|
-
verificationUrl: external_exports.string().url().max(16384),
|
|
15129
|
-
userCode: nonBlankTextSchema3.max(1024)
|
|
15130
|
-
}).strict();
|
|
15131
|
-
var codexAccountLoginCancelParamsSchema = external_exports.object({ loginId: nonBlankTextSchema3.max(1024) }).strict();
|
|
15132
|
-
var codexAccountLoginCancelResultSchema = external_exports.object({ cancelled: external_exports.boolean() }).strict();
|
|
15133
|
-
var codexAccountLoginCompletedSchema = external_exports.object({
|
|
15134
|
-
accountId: accountIdSchema,
|
|
15135
|
-
loginId: nonBlankTextSchema3.max(1024),
|
|
15136
|
-
success: external_exports.boolean(),
|
|
15137
|
-
error: external_exports.string().max(4096).nullable(),
|
|
15138
|
-
saved: external_exports.boolean().optional(),
|
|
15139
|
-
cleanupRequired: external_exports.boolean().optional()
|
|
15140
|
-
}).strict();
|
|
15141
|
-
var codexAccountLogoutParamsSchema = external_exports.object({}).strict();
|
|
15142
|
-
var codexAccountLogoutResultSchema = external_exports.object({
|
|
15143
|
-
currentAccountId: external_exports.null(),
|
|
15144
|
-
...codexAccountReadyMutationResultShape
|
|
15145
|
-
}).strict();
|
|
15146
|
-
var codexAccountRecoverParamsSchema = external_exports.object({}).strict();
|
|
15147
15139
|
var codexAccountUsageParamsSchema = external_exports.object({ accountId: accountIdSchema, refresh: external_exports.boolean().optional() }).strict();
|
|
15148
15140
|
var codexAccountUsageResultSchema = external_exports.object({
|
|
15149
15141
|
accountId: accountIdSchema,
|
|
@@ -15152,21 +15144,6 @@ var codexAccountUsageResultSchema = external_exports.object({
|
|
|
15152
15144
|
freshness: external_exports.enum(["live", "cached"]),
|
|
15153
15145
|
observedAt: external_exports.string().datetime().nullable()
|
|
15154
15146
|
}).strict();
|
|
15155
|
-
var codexAccountResetCreditConsumeParamsSchema = external_exports.object({
|
|
15156
|
-
accountId: accountIdSchema,
|
|
15157
|
-
idempotencyKey: external_exports.string().uuid().optional()
|
|
15158
|
-
}).strict();
|
|
15159
|
-
var codexAccountResetCreditConsumeOutcomeSchema = external_exports.enum([
|
|
15160
|
-
"reset",
|
|
15161
|
-
"nothingToReset",
|
|
15162
|
-
"noCredit",
|
|
15163
|
-
"alreadyRedeemed"
|
|
15164
|
-
]);
|
|
15165
|
-
var codexAccountResetCreditConsumeResultSchema = external_exports.object({
|
|
15166
|
-
accountId: accountIdSchema,
|
|
15167
|
-
outcome: codexAccountResetCreditConsumeOutcomeSchema,
|
|
15168
|
-
accountCredits: accountCreditsSnapshotSchema.optional()
|
|
15169
|
-
}).strict();
|
|
15170
15147
|
|
|
15171
15148
|
// ../../shared-contracts/dist/harness-session-import.js
|
|
15172
15149
|
var HARNESS_SESSION_IMPORT_ID_MAX_LENGTH = 1024;
|
|
@@ -15352,6 +15329,8 @@ var nativeCheckpointRefV1RuntimeSchema = external_exports.strictObject({
|
|
|
15352
15329
|
locator: jsonValueSchema.optional(),
|
|
15353
15330
|
formatVersion: external_exports.literal(1)
|
|
15354
15331
|
}).superRefine(rejectExplicitUndefined(["locator"]));
|
|
15332
|
+
var nativeCheckpointRefV1Schema = nativeCheckpointRefV1RuntimeSchema;
|
|
15333
|
+
var nativeCheckpointRefSchema = nativeCheckpointRefV1Schema;
|
|
15355
15334
|
|
|
15356
15335
|
// ../../shared-contracts/dist/updates.js
|
|
15357
15336
|
var UPDATE_ERROR_MAX_LENGTH = 500;
|
|
@@ -15407,6 +15386,62 @@ var updateStatusResultSchema = external_exports.strictObject({
|
|
|
15407
15386
|
// ../../shared-contracts/dist/index.js
|
|
15408
15387
|
var workspaceContractVersionSchema = external_exports.literal(WORKSPACE_CONTRACT_VERSION);
|
|
15409
15388
|
|
|
15389
|
+
// dist/hermes-commands.js
|
|
15390
|
+
var SUPPORTED_COMMANDS = /* @__PURE__ */ new Set(["help", "tools", "context", "compress", "version"]);
|
|
15391
|
+
function hermesCommandCatalog(commands) {
|
|
15392
|
+
return harnessCommandCatalogSchema.parse({
|
|
15393
|
+
commands: commands.filter((command) => SUPPORTED_COMMANDS.has(command.name)).map((command) => ({
|
|
15394
|
+
id: `hermes.${command.name}`,
|
|
15395
|
+
invocation: `/${command.name}`,
|
|
15396
|
+
label: `/${command.name}`,
|
|
15397
|
+
description: command.description.slice(0, 512) || `Hermes /${command.name}`,
|
|
15398
|
+
argumentMode: command.input ? "text" : "none"
|
|
15399
|
+
}))
|
|
15400
|
+
});
|
|
15401
|
+
}
|
|
15402
|
+
var HERMES_GATEWAY_COMMANDS = [
|
|
15403
|
+
...["help", "tools", "context", "version"].map((name) => ({
|
|
15404
|
+
name,
|
|
15405
|
+
description: `Hermes /${name}`
|
|
15406
|
+
})),
|
|
15407
|
+
{
|
|
15408
|
+
name: "compress",
|
|
15409
|
+
description: "Compress conversation context",
|
|
15410
|
+
input: { hint: "Optional compression focus" }
|
|
15411
|
+
}
|
|
15412
|
+
];
|
|
15413
|
+
var HERMES_COMMAND_CATALOG = hermesCommandCatalog(HERMES_GATEWAY_COMMANDS);
|
|
15414
|
+
function hermesCommandText(command, catalog) {
|
|
15415
|
+
const descriptor = catalog.commands.find((entry) => entry.id === command.commandId);
|
|
15416
|
+
if (!descriptor)
|
|
15417
|
+
return {
|
|
15418
|
+
ok: false,
|
|
15419
|
+
error: {
|
|
15420
|
+
code: "unsupported",
|
|
15421
|
+
message: "Hermes did not advertise this command",
|
|
15422
|
+
retryable: false
|
|
15423
|
+
}
|
|
15424
|
+
};
|
|
15425
|
+
const args = command.arguments ?? {};
|
|
15426
|
+
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()) {
|
|
15427
|
+
return {
|
|
15428
|
+
ok: false,
|
|
15429
|
+
error: {
|
|
15430
|
+
code: "invalidRequest",
|
|
15431
|
+
message: "Invalid Hermes command arguments",
|
|
15432
|
+
retryable: false
|
|
15433
|
+
}
|
|
15434
|
+
};
|
|
15435
|
+
}
|
|
15436
|
+
return {
|
|
15437
|
+
ok: true,
|
|
15438
|
+
value: `${descriptor.invocation}${typeof args.text === "string" && args.text.trim() ? ` ${args.text.trim()}` : ""}`
|
|
15439
|
+
};
|
|
15440
|
+
}
|
|
15441
|
+
|
|
15442
|
+
// dist/hermes-adapter.js
|
|
15443
|
+
import { createHash } from "node:crypto";
|
|
15444
|
+
|
|
15410
15445
|
// dist/acp-transport.js
|
|
15411
15446
|
import { spawn, spawnSync } from "node:child_process";
|
|
15412
15447
|
import { Readable, Writable } from "node:stream";
|
|
@@ -15419,6 +15454,42 @@ function validateHostApprovalResponse(interaction, response) {
|
|
|
15419
15454
|
return interaction.actions.some(({ id }) => id === response.actionId) ? null : invalidRequest("Approval Response contains an undeclared action ID");
|
|
15420
15455
|
}
|
|
15421
15456
|
|
|
15457
|
+
// ../../harness-adapter/dist/question.js
|
|
15458
|
+
function invalidRequest2(message) {
|
|
15459
|
+
return { code: "invalidRequest", message, retryable: false };
|
|
15460
|
+
}
|
|
15461
|
+
function validateHostQuestionResponse(interaction, response) {
|
|
15462
|
+
const questionIds = new Set(interaction.questions.map(({ id }) => id));
|
|
15463
|
+
if (response.cancelled) {
|
|
15464
|
+
return Object.keys(response.answers).length === 0 ? null : invalidRequest2("Cancelled Question Response must not contain answers");
|
|
15465
|
+
}
|
|
15466
|
+
for (const answerId of Object.keys(response.answers)) {
|
|
15467
|
+
if (!questionIds.has(answerId)) {
|
|
15468
|
+
return invalidRequest2("Question Response contains an unknown Question ID");
|
|
15469
|
+
}
|
|
15470
|
+
}
|
|
15471
|
+
for (const question2 of interaction.questions) {
|
|
15472
|
+
const answers = response.answers[question2.id] ?? [];
|
|
15473
|
+
if (!question2.optional && answers.length === 0) {
|
|
15474
|
+
return invalidRequest2("Question Response omits a required answer");
|
|
15475
|
+
}
|
|
15476
|
+
if (question2.type === "text") {
|
|
15477
|
+
if (answers.length > 1) {
|
|
15478
|
+
return invalidRequest2("Text Question accepts at most one answer");
|
|
15479
|
+
}
|
|
15480
|
+
continue;
|
|
15481
|
+
}
|
|
15482
|
+
if (!question2.multiple && answers.length > 1) {
|
|
15483
|
+
return invalidRequest2("Single-choice Question accepts at most one answer");
|
|
15484
|
+
}
|
|
15485
|
+
const declared = new Set(question2.options.map(({ value }) => value));
|
|
15486
|
+
if (!question2.allowOther && answers.some((answer) => !declared.has(answer))) {
|
|
15487
|
+
return invalidRequest2("Question Response contains an undeclared choice");
|
|
15488
|
+
}
|
|
15489
|
+
}
|
|
15490
|
+
return null;
|
|
15491
|
+
}
|
|
15492
|
+
|
|
15422
15493
|
// ../../harness-adapter/dist/output-channel.js
|
|
15423
15494
|
var HarnessOutputChannel = class {
|
|
15424
15495
|
outputs;
|
|
@@ -19995,8 +20066,8 @@ function classifyStartupError(error51) {
|
|
|
19995
20066
|
return new HermesTransportError("notInstalled", error51.message, { cause: error51 });
|
|
19996
20067
|
}
|
|
19997
20068
|
const detail = errorDetails2(error51);
|
|
19998
|
-
const
|
|
19999
|
-
if (
|
|
20069
|
+
const text2 = detail.toLowerCase();
|
|
20070
|
+
if (text2.includes("auth_required") || text2.includes("authentication") || text2.includes("not configured") || text2.includes("no provider") || text2.includes("no llm provider")) {
|
|
20000
20071
|
return new HermesTransportError("authenticationRequired", detail, {
|
|
20001
20072
|
cause: error51,
|
|
20002
20073
|
diagnostic: detail
|
|
@@ -20076,6 +20147,10 @@ var HermesAcpTransport = class {
|
|
|
20076
20147
|
#replay = null;
|
|
20077
20148
|
#sessionId = null;
|
|
20078
20149
|
#stderrTail = "";
|
|
20150
|
+
#availableCommands = [];
|
|
20151
|
+
get availableCommands() {
|
|
20152
|
+
return this.#availableCommands;
|
|
20153
|
+
}
|
|
20079
20154
|
/** Late binding: the Session registers its fault consumer on construction. */
|
|
20080
20155
|
onFault = () => void 0;
|
|
20081
20156
|
constructor(options) {
|
|
@@ -20214,7 +20289,7 @@ var HermesAcpTransport = class {
|
|
|
20214
20289
|
throw classifyStartupError(error51);
|
|
20215
20290
|
}
|
|
20216
20291
|
}
|
|
20217
|
-
async runTurn(
|
|
20292
|
+
async runTurn(text2, onEvent, onPermission) {
|
|
20218
20293
|
const connection = this.#connection;
|
|
20219
20294
|
if (!connection || !this.#sessionId || this.#closed || this.#closing) {
|
|
20220
20295
|
throw new HermesTransportError("unavailable", "Hermes ACP Session is unavailable");
|
|
@@ -20226,7 +20301,7 @@ var HermesAcpTransport = class {
|
|
|
20226
20301
|
try {
|
|
20227
20302
|
return await connection.prompt({
|
|
20228
20303
|
sessionId: this.#sessionId,
|
|
20229
|
-
prompt: [{ type: "text", text }]
|
|
20304
|
+
prompt: [{ type: "text", text: text2 }]
|
|
20230
20305
|
});
|
|
20231
20306
|
} finally {
|
|
20232
20307
|
if (this.#activePrompt === active)
|
|
@@ -20265,6 +20340,10 @@ var HermesAcpTransport = class {
|
|
|
20265
20340
|
#handleUpdate(notification) {
|
|
20266
20341
|
if (this.#sessionId && notification.sessionId !== this.#sessionId)
|
|
20267
20342
|
return;
|
|
20343
|
+
if (notification.update.sessionUpdate === "available_commands_update") {
|
|
20344
|
+
this.#availableCommands = notification.update.availableCommands;
|
|
20345
|
+
return;
|
|
20346
|
+
}
|
|
20268
20347
|
const event = transportEvent(notification.update);
|
|
20269
20348
|
if (!event)
|
|
20270
20349
|
return;
|
|
@@ -20327,9 +20406,6 @@ var HermesAcpTransport = class {
|
|
|
20327
20406
|
clientCapabilities: {},
|
|
20328
20407
|
clientInfo: { name: "codexhost", version: "0.1.0" }
|
|
20329
20408
|
}), this.#options.commandTimeoutMs, "Hermes ACP initialize");
|
|
20330
|
-
if (initialize.protocolVersion !== PROTOCOL_VERSION) {
|
|
20331
|
-
throw new HermesTransportError("protocolError", `Hermes ACP negotiated unsupported protocol version ${initialize.protocolVersion}`);
|
|
20332
|
-
}
|
|
20333
20409
|
this.#initialize = initialize;
|
|
20334
20410
|
return initialize;
|
|
20335
20411
|
}
|
|
@@ -20468,8 +20544,28 @@ for row in payload.get("providers") or []:
|
|
|
20468
20544
|
)
|
|
20469
20545
|
if slug and model_id:
|
|
20470
20546
|
available = slug.lower() != "moa" or bool(moa_availability.get(model_id, False))
|
|
20547
|
+
aliases = [
|
|
20548
|
+
alias.strip()
|
|
20549
|
+
for alias in row.get("aliases") or []
|
|
20550
|
+
if isinstance(alias, str) and alias.strip()
|
|
20551
|
+
]
|
|
20552
|
+
# Hermes names configured custom endpoints as custom:<key>.
|
|
20553
|
+
# The inventory row slug is the bare config key (pi-openai),
|
|
20554
|
+
# which is useful for display but is not a valid native model route.
|
|
20555
|
+
# Prefer the native custom identity for the actual model ref and
|
|
20556
|
+
# retain the bare slug as an alias for matching older snapshots.
|
|
20557
|
+
native_slug = next(
|
|
20558
|
+
(alias for alias in aliases if alias.lower().startswith("custom:")),
|
|
20559
|
+
slug,
|
|
20560
|
+
)
|
|
20561
|
+
native_model_id = native_slug + ":" + model_id
|
|
20471
20562
|
rows.append({
|
|
20472
|
-
"modelId":
|
|
20563
|
+
"modelId": native_model_id,
|
|
20564
|
+
"modelIdAliases": [
|
|
20565
|
+
alias + ":" + model_id
|
|
20566
|
+
for alias in aliases
|
|
20567
|
+
if alias + ":" + model_id != native_model_id
|
|
20568
|
+
],
|
|
20473
20569
|
"label": model_id,
|
|
20474
20570
|
"provider": provider,
|
|
20475
20571
|
"available": available,
|
|
@@ -20481,6 +20577,11 @@ print(json.dumps({"models": rows, "currentModelId": current_model_id}))
|
|
|
20481
20577
|
`;
|
|
20482
20578
|
var HermesInventoryError = class extends Error {
|
|
20483
20579
|
};
|
|
20580
|
+
var HermesInventoryTimeoutError = class extends HermesInventoryError {
|
|
20581
|
+
constructor() {
|
|
20582
|
+
super("Hermes model inventory probe timed out");
|
|
20583
|
+
}
|
|
20584
|
+
};
|
|
20484
20585
|
async function venvPythonFromShim(hermesExecutable, platform = process.platform) {
|
|
20485
20586
|
try {
|
|
20486
20587
|
const shim = await readFile(hermesExecutable, "utf8");
|
|
@@ -20519,7 +20620,7 @@ function runProbe(pythonExecutable, timeoutMs, environment) {
|
|
|
20519
20620
|
let stderr = "";
|
|
20520
20621
|
const timer = setTimeout(() => {
|
|
20521
20622
|
child.kill("SIGKILL");
|
|
20522
|
-
reject(new
|
|
20623
|
+
reject(new HermesInventoryTimeoutError());
|
|
20523
20624
|
}, timeoutMs);
|
|
20524
20625
|
child.stdout.on("data", (chunk) => {
|
|
20525
20626
|
stdout += chunk.toString("utf8");
|
|
@@ -20538,11 +20639,11 @@ function runProbe(pythonExecutable, timeoutMs, environment) {
|
|
|
20538
20639
|
return;
|
|
20539
20640
|
}
|
|
20540
20641
|
try {
|
|
20541
|
-
const
|
|
20542
|
-
const models = (
|
|
20642
|
+
const parsed2 = JSON.parse(stdout.trim());
|
|
20643
|
+
const models = (parsed2.models ?? []).filter((model) => typeof model?.modelId === "string" && model.modelId.length > 0);
|
|
20543
20644
|
resolve({
|
|
20544
20645
|
models,
|
|
20545
|
-
currentModelId: typeof
|
|
20646
|
+
currentModelId: typeof parsed2.currentModelId === "string" && parsed2.currentModelId.length > 0 ? parsed2.currentModelId : null
|
|
20546
20647
|
});
|
|
20547
20648
|
} catch {
|
|
20548
20649
|
reject(new HermesInventoryError("Hermes inventory probe returned malformed output"));
|
|
@@ -20576,10 +20677,11 @@ function catalogModelsFromInventory(inventory) {
|
|
|
20576
20677
|
for (const model of inventory.models) {
|
|
20577
20678
|
if (model.available === false)
|
|
20578
20679
|
continue;
|
|
20579
|
-
const
|
|
20680
|
+
const nativeModelId = model.modelIdAliases?.find((alias) => alias.toLowerCase().startsWith("custom:")) ?? model.modelId;
|
|
20681
|
+
const ref = encodeHermesModelRef(nativeModelId);
|
|
20580
20682
|
if (!ref)
|
|
20581
20683
|
continue;
|
|
20582
|
-
if (inventory.currentModelId && model.modelId === inventory.currentModelId) {
|
|
20684
|
+
if (inventory.currentModelId && (model.modelId === inventory.currentModelId || nativeModelId === inventory.currentModelId || model.modelIdAliases?.includes(inventory.currentModelId))) {
|
|
20583
20685
|
defaultModel = ref;
|
|
20584
20686
|
}
|
|
20585
20687
|
models.push({
|
|
@@ -20639,7 +20741,558 @@ async function resolveHermesSessionCandidate(input) {
|
|
|
20639
20741
|
}
|
|
20640
20742
|
|
|
20641
20743
|
// dist/hermes-session.js
|
|
20744
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
20745
|
+
|
|
20746
|
+
// dist/hermes-questions.js
|
|
20642
20747
|
import { randomUUID } from "node:crypto";
|
|
20748
|
+
var HermesQuestions = class {
|
|
20749
|
+
emit;
|
|
20750
|
+
#waiters = /* @__PURE__ */ new Map();
|
|
20751
|
+
constructor(emit) {
|
|
20752
|
+
this.emit = emit;
|
|
20753
|
+
}
|
|
20754
|
+
open(turnId, request) {
|
|
20755
|
+
const interactionId = hostInteractionIdSchema.parse(randomUUID());
|
|
20756
|
+
const interaction = {
|
|
20757
|
+
type: "question",
|
|
20758
|
+
interactionId,
|
|
20759
|
+
turnId,
|
|
20760
|
+
questions: request.questions,
|
|
20761
|
+
...request.title ? { title: request.title } : {}
|
|
20762
|
+
};
|
|
20763
|
+
const pending = new Promise((resolve) => this.#waiters.set(interactionId, { interaction, resolve }));
|
|
20764
|
+
const abort = () => this.#cancel(interactionId, request.signal?.reason === "expired" ? "expired" : "cancelled");
|
|
20765
|
+
request.signal?.addEventListener("abort", abort, { once: true });
|
|
20766
|
+
this.emit({ kind: "interaction", interaction });
|
|
20767
|
+
if (request.signal?.aborted)
|
|
20768
|
+
abort();
|
|
20769
|
+
return pending.finally(() => request.signal?.removeEventListener("abort", abort));
|
|
20770
|
+
}
|
|
20771
|
+
respond(command) {
|
|
20772
|
+
const waiter = this.#waiters.get(command.interactionId);
|
|
20773
|
+
if (!waiter || command.response.type !== "question")
|
|
20774
|
+
return {
|
|
20775
|
+
ok: false,
|
|
20776
|
+
error: { code: "invalidRequest", message: "No pending Hermes question", retryable: false }
|
|
20777
|
+
};
|
|
20778
|
+
const error51 = validateHostQuestionResponse(waiter.interaction, command.response);
|
|
20779
|
+
if (error51)
|
|
20780
|
+
return { ok: false, error: { ...error51, retryable: false } };
|
|
20781
|
+
this.#waiters.delete(command.interactionId);
|
|
20782
|
+
waiter.resolve(command.response);
|
|
20783
|
+
this.emit({
|
|
20784
|
+
kind: "event",
|
|
20785
|
+
event: {
|
|
20786
|
+
type: "interaction.closed",
|
|
20787
|
+
interactionId: command.interactionId,
|
|
20788
|
+
turnId: waiter.interaction.turnId,
|
|
20789
|
+
reason: command.response.cancelled ? "cancelled" : "responded"
|
|
20790
|
+
}
|
|
20791
|
+
});
|
|
20792
|
+
return { ok: true, value: { accepted: true } };
|
|
20793
|
+
}
|
|
20794
|
+
cancel(turnId) {
|
|
20795
|
+
for (const [id, waiter] of this.#waiters)
|
|
20796
|
+
if (!turnId || waiter.interaction.turnId === turnId)
|
|
20797
|
+
this.#cancel(id, "cancelled");
|
|
20798
|
+
}
|
|
20799
|
+
#cancel(id, reason) {
|
|
20800
|
+
const waiter = this.#waiters.get(id);
|
|
20801
|
+
if (!waiter)
|
|
20802
|
+
return;
|
|
20803
|
+
this.#waiters.delete(id);
|
|
20804
|
+
waiter.resolve({ type: "question", answers: {}, cancelled: true });
|
|
20805
|
+
this.emit({
|
|
20806
|
+
kind: "event",
|
|
20807
|
+
event: {
|
|
20808
|
+
type: "interaction.closed",
|
|
20809
|
+
interactionId: waiter.interaction.interactionId,
|
|
20810
|
+
turnId: waiter.interaction.turnId,
|
|
20811
|
+
reason
|
|
20812
|
+
}
|
|
20813
|
+
});
|
|
20814
|
+
}
|
|
20815
|
+
};
|
|
20816
|
+
|
|
20817
|
+
// ../../../node_modules/diff/libesm/diff/base.js
|
|
20818
|
+
var Diff = class {
|
|
20819
|
+
diff(oldStr, newStr, options = {}) {
|
|
20820
|
+
let callback;
|
|
20821
|
+
if (typeof options === "function") {
|
|
20822
|
+
callback = options;
|
|
20823
|
+
options = {};
|
|
20824
|
+
} else if ("callback" in options) {
|
|
20825
|
+
callback = options.callback;
|
|
20826
|
+
}
|
|
20827
|
+
const oldString = this.castInput(oldStr, options);
|
|
20828
|
+
const newString = this.castInput(newStr, options);
|
|
20829
|
+
const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
|
|
20830
|
+
const newTokens = this.removeEmpty(this.tokenize(newString, options));
|
|
20831
|
+
return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
|
|
20832
|
+
}
|
|
20833
|
+
diffWithOptionsObj(oldTokens, newTokens, options, callback) {
|
|
20834
|
+
var _a3;
|
|
20835
|
+
const done = (value) => {
|
|
20836
|
+
value = this.postProcess(value, options);
|
|
20837
|
+
if (callback) {
|
|
20838
|
+
setTimeout(function() {
|
|
20839
|
+
callback(value);
|
|
20840
|
+
}, 0);
|
|
20841
|
+
return void 0;
|
|
20842
|
+
} else {
|
|
20843
|
+
return value;
|
|
20844
|
+
}
|
|
20845
|
+
};
|
|
20846
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
20847
|
+
let editLength = 1;
|
|
20848
|
+
let maxEditLength = newLen + oldLen;
|
|
20849
|
+
if (options.maxEditLength != null) {
|
|
20850
|
+
maxEditLength = Math.min(maxEditLength, options.maxEditLength);
|
|
20851
|
+
}
|
|
20852
|
+
const maxExecutionTime = (_a3 = options.timeout) !== null && _a3 !== void 0 ? _a3 : Infinity;
|
|
20853
|
+
const abortAfterTimestamp = Date.now() + maxExecutionTime;
|
|
20854
|
+
const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
|
|
20855
|
+
let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
|
|
20856
|
+
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
20857
|
+
return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
|
|
20858
|
+
}
|
|
20859
|
+
let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
|
|
20860
|
+
const execEditLength = () => {
|
|
20861
|
+
for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
20862
|
+
let basePath;
|
|
20863
|
+
const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
|
|
20864
|
+
if (removePath) {
|
|
20865
|
+
bestPath[diagonalPath - 1] = void 0;
|
|
20866
|
+
}
|
|
20867
|
+
let canAdd = false;
|
|
20868
|
+
if (addPath) {
|
|
20869
|
+
const addPathNewPos = addPath.oldPos - diagonalPath;
|
|
20870
|
+
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
20871
|
+
}
|
|
20872
|
+
const canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
20873
|
+
if (!canAdd && !canRemove) {
|
|
20874
|
+
bestPath[diagonalPath] = void 0;
|
|
20875
|
+
continue;
|
|
20876
|
+
}
|
|
20877
|
+
if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
|
|
20878
|
+
basePath = this.addToPath(addPath, true, false, 0, options);
|
|
20879
|
+
} else {
|
|
20880
|
+
basePath = this.addToPath(removePath, false, true, 1, options);
|
|
20881
|
+
}
|
|
20882
|
+
newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
|
|
20883
|
+
if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
20884
|
+
return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
|
|
20885
|
+
} else {
|
|
20886
|
+
bestPath[diagonalPath] = basePath;
|
|
20887
|
+
if (basePath.oldPos + 1 >= oldLen) {
|
|
20888
|
+
maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
|
|
20889
|
+
}
|
|
20890
|
+
if (newPos + 1 >= newLen) {
|
|
20891
|
+
minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
|
|
20892
|
+
}
|
|
20893
|
+
}
|
|
20894
|
+
}
|
|
20895
|
+
editLength++;
|
|
20896
|
+
};
|
|
20897
|
+
if (callback) {
|
|
20898
|
+
(function exec() {
|
|
20899
|
+
setTimeout(function() {
|
|
20900
|
+
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
|
|
20901
|
+
return callback(void 0);
|
|
20902
|
+
}
|
|
20903
|
+
if (!execEditLength()) {
|
|
20904
|
+
exec();
|
|
20905
|
+
}
|
|
20906
|
+
}, 0);
|
|
20907
|
+
})();
|
|
20908
|
+
} else {
|
|
20909
|
+
while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
20910
|
+
const ret = execEditLength();
|
|
20911
|
+
if (ret) {
|
|
20912
|
+
return ret;
|
|
20913
|
+
}
|
|
20914
|
+
}
|
|
20915
|
+
}
|
|
20916
|
+
}
|
|
20917
|
+
addToPath(path7, added, removed, oldPosInc, options) {
|
|
20918
|
+
const last = path7.lastComponent;
|
|
20919
|
+
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
20920
|
+
return {
|
|
20921
|
+
oldPos: path7.oldPos + oldPosInc,
|
|
20922
|
+
lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
|
|
20923
|
+
};
|
|
20924
|
+
} else {
|
|
20925
|
+
return {
|
|
20926
|
+
oldPos: path7.oldPos + oldPosInc,
|
|
20927
|
+
lastComponent: { count: 1, added, removed, previousComponent: last }
|
|
20928
|
+
};
|
|
20929
|
+
}
|
|
20930
|
+
}
|
|
20931
|
+
extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
|
|
20932
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
20933
|
+
let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
20934
|
+
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
|
|
20935
|
+
newPos++;
|
|
20936
|
+
oldPos++;
|
|
20937
|
+
commonCount++;
|
|
20938
|
+
if (options.oneChangePerToken) {
|
|
20939
|
+
basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
20940
|
+
}
|
|
20941
|
+
}
|
|
20942
|
+
if (commonCount && !options.oneChangePerToken) {
|
|
20943
|
+
basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
20944
|
+
}
|
|
20945
|
+
basePath.oldPos = oldPos;
|
|
20946
|
+
return newPos;
|
|
20947
|
+
}
|
|
20948
|
+
equals(left, right, options) {
|
|
20949
|
+
if (options.comparator) {
|
|
20950
|
+
return options.comparator(left, right);
|
|
20951
|
+
} else {
|
|
20952
|
+
return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
20953
|
+
}
|
|
20954
|
+
}
|
|
20955
|
+
removeEmpty(array2) {
|
|
20956
|
+
const ret = [];
|
|
20957
|
+
for (let i = 0; i < array2.length; i++) {
|
|
20958
|
+
if (array2[i]) {
|
|
20959
|
+
ret.push(array2[i]);
|
|
20960
|
+
}
|
|
20961
|
+
}
|
|
20962
|
+
return ret;
|
|
20963
|
+
}
|
|
20964
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
20965
|
+
castInput(value, options) {
|
|
20966
|
+
return value;
|
|
20967
|
+
}
|
|
20968
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
20969
|
+
tokenize(value, options) {
|
|
20970
|
+
return Array.from(value);
|
|
20971
|
+
}
|
|
20972
|
+
join(chars) {
|
|
20973
|
+
return chars.join("");
|
|
20974
|
+
}
|
|
20975
|
+
postProcess(changeObjects, options) {
|
|
20976
|
+
return changeObjects;
|
|
20977
|
+
}
|
|
20978
|
+
get useLongestToken() {
|
|
20979
|
+
return false;
|
|
20980
|
+
}
|
|
20981
|
+
buildValues(lastComponent, newTokens, oldTokens) {
|
|
20982
|
+
const components = [];
|
|
20983
|
+
let nextComponent;
|
|
20984
|
+
while (lastComponent) {
|
|
20985
|
+
components.push(lastComponent);
|
|
20986
|
+
nextComponent = lastComponent.previousComponent;
|
|
20987
|
+
delete lastComponent.previousComponent;
|
|
20988
|
+
lastComponent = nextComponent;
|
|
20989
|
+
}
|
|
20990
|
+
components.reverse();
|
|
20991
|
+
const componentLen = components.length;
|
|
20992
|
+
let componentPos = 0, newPos = 0, oldPos = 0;
|
|
20993
|
+
for (; componentPos < componentLen; componentPos++) {
|
|
20994
|
+
const component = components[componentPos];
|
|
20995
|
+
if (!component.removed) {
|
|
20996
|
+
if (!component.added && this.useLongestToken) {
|
|
20997
|
+
let value = newTokens.slice(newPos, newPos + component.count);
|
|
20998
|
+
value = value.map(function(value2, i) {
|
|
20999
|
+
const oldValue = oldTokens[oldPos + i];
|
|
21000
|
+
return oldValue.length > value2.length ? oldValue : value2;
|
|
21001
|
+
});
|
|
21002
|
+
component.value = this.join(value);
|
|
21003
|
+
} else {
|
|
21004
|
+
component.value = this.join(newTokens.slice(newPos, newPos + component.count));
|
|
21005
|
+
}
|
|
21006
|
+
newPos += component.count;
|
|
21007
|
+
if (!component.added) {
|
|
21008
|
+
oldPos += component.count;
|
|
21009
|
+
}
|
|
21010
|
+
} else {
|
|
21011
|
+
component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
|
|
21012
|
+
oldPos += component.count;
|
|
21013
|
+
}
|
|
21014
|
+
}
|
|
21015
|
+
return components;
|
|
21016
|
+
}
|
|
21017
|
+
};
|
|
21018
|
+
|
|
21019
|
+
// ../../../node_modules/diff/libesm/diff/line.js
|
|
21020
|
+
var LineDiff = class extends Diff {
|
|
21021
|
+
constructor() {
|
|
21022
|
+
super(...arguments);
|
|
21023
|
+
this.tokenize = tokenize;
|
|
21024
|
+
}
|
|
21025
|
+
equals(left, right, options) {
|
|
21026
|
+
if (options.ignoreWhitespace) {
|
|
21027
|
+
if (!options.newlineIsToken || !left.includes("\n")) {
|
|
21028
|
+
left = left.trim();
|
|
21029
|
+
}
|
|
21030
|
+
if (!options.newlineIsToken || !right.includes("\n")) {
|
|
21031
|
+
right = right.trim();
|
|
21032
|
+
}
|
|
21033
|
+
} else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
|
|
21034
|
+
if (left.endsWith("\n")) {
|
|
21035
|
+
left = left.slice(0, -1);
|
|
21036
|
+
}
|
|
21037
|
+
if (right.endsWith("\n")) {
|
|
21038
|
+
right = right.slice(0, -1);
|
|
21039
|
+
}
|
|
21040
|
+
}
|
|
21041
|
+
return super.equals(left, right, options);
|
|
21042
|
+
}
|
|
21043
|
+
};
|
|
21044
|
+
var lineDiff = new LineDiff();
|
|
21045
|
+
function diffLines(oldStr, newStr, options) {
|
|
21046
|
+
return lineDiff.diff(oldStr, newStr, options);
|
|
21047
|
+
}
|
|
21048
|
+
function tokenize(value, options) {
|
|
21049
|
+
if (options.stripTrailingCr) {
|
|
21050
|
+
value = value.replace(/\r\n/g, "\n");
|
|
21051
|
+
}
|
|
21052
|
+
const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
|
|
21053
|
+
if (!linesAndNewlines[linesAndNewlines.length - 1]) {
|
|
21054
|
+
linesAndNewlines.pop();
|
|
21055
|
+
}
|
|
21056
|
+
for (let i = 0; i < linesAndNewlines.length; i++) {
|
|
21057
|
+
const line = linesAndNewlines[i];
|
|
21058
|
+
if (i % 2 && !options.newlineIsToken) {
|
|
21059
|
+
retLines[retLines.length - 1] += line;
|
|
21060
|
+
} else {
|
|
21061
|
+
retLines.push(line);
|
|
21062
|
+
}
|
|
21063
|
+
}
|
|
21064
|
+
return retLines;
|
|
21065
|
+
}
|
|
21066
|
+
|
|
21067
|
+
// ../../../node_modules/diff/libesm/patch/create.js
|
|
21068
|
+
function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
21069
|
+
let optionsObj;
|
|
21070
|
+
if (!options) {
|
|
21071
|
+
optionsObj = {};
|
|
21072
|
+
} else if (typeof options === "function") {
|
|
21073
|
+
optionsObj = { callback: options };
|
|
21074
|
+
} else {
|
|
21075
|
+
optionsObj = options;
|
|
21076
|
+
}
|
|
21077
|
+
if (typeof optionsObj.context === "undefined") {
|
|
21078
|
+
optionsObj.context = 4;
|
|
21079
|
+
}
|
|
21080
|
+
const context = optionsObj.context;
|
|
21081
|
+
if (optionsObj.newlineIsToken) {
|
|
21082
|
+
throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
|
|
21083
|
+
}
|
|
21084
|
+
if (!optionsObj.callback) {
|
|
21085
|
+
return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
|
|
21086
|
+
} else {
|
|
21087
|
+
const { callback } = optionsObj;
|
|
21088
|
+
diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
|
|
21089
|
+
const patch = diffLinesResultToPatch(diff);
|
|
21090
|
+
callback(patch);
|
|
21091
|
+
} }));
|
|
21092
|
+
}
|
|
21093
|
+
function diffLinesResultToPatch(diff) {
|
|
21094
|
+
if (!diff) {
|
|
21095
|
+
return;
|
|
21096
|
+
}
|
|
21097
|
+
diff.push({ value: "", lines: [] });
|
|
21098
|
+
function contextLines(lines) {
|
|
21099
|
+
return lines.map(function(entry) {
|
|
21100
|
+
return " " + entry;
|
|
21101
|
+
});
|
|
21102
|
+
}
|
|
21103
|
+
const hunks = [];
|
|
21104
|
+
let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
|
|
21105
|
+
for (let i = 0; i < diff.length; i++) {
|
|
21106
|
+
const current = diff[i], lines = current.lines || splitLines(current.value);
|
|
21107
|
+
current.lines = lines;
|
|
21108
|
+
if (current.added || current.removed) {
|
|
21109
|
+
if (!oldRangeStart) {
|
|
21110
|
+
const prev = diff[i - 1];
|
|
21111
|
+
oldRangeStart = oldLine;
|
|
21112
|
+
newRangeStart = newLine;
|
|
21113
|
+
if (prev) {
|
|
21114
|
+
curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
|
|
21115
|
+
oldRangeStart -= curRange.length;
|
|
21116
|
+
newRangeStart -= curRange.length;
|
|
21117
|
+
}
|
|
21118
|
+
}
|
|
21119
|
+
for (const line of lines) {
|
|
21120
|
+
curRange.push((current.added ? "+" : "-") + line);
|
|
21121
|
+
}
|
|
21122
|
+
if (current.added) {
|
|
21123
|
+
newLine += lines.length;
|
|
21124
|
+
} else {
|
|
21125
|
+
oldLine += lines.length;
|
|
21126
|
+
}
|
|
21127
|
+
} else {
|
|
21128
|
+
if (oldRangeStart) {
|
|
21129
|
+
if (lines.length <= context * 2 && i < diff.length - 2) {
|
|
21130
|
+
for (const line of contextLines(lines)) {
|
|
21131
|
+
curRange.push(line);
|
|
21132
|
+
}
|
|
21133
|
+
} else {
|
|
21134
|
+
const contextSize = Math.min(lines.length, context);
|
|
21135
|
+
for (const line of contextLines(lines.slice(0, contextSize))) {
|
|
21136
|
+
curRange.push(line);
|
|
21137
|
+
}
|
|
21138
|
+
const hunk = {
|
|
21139
|
+
oldStart: oldRangeStart,
|
|
21140
|
+
oldLines: oldLine - oldRangeStart + contextSize,
|
|
21141
|
+
newStart: newRangeStart,
|
|
21142
|
+
newLines: newLine - newRangeStart + contextSize,
|
|
21143
|
+
lines: curRange
|
|
21144
|
+
};
|
|
21145
|
+
hunks.push(hunk);
|
|
21146
|
+
oldRangeStart = 0;
|
|
21147
|
+
newRangeStart = 0;
|
|
21148
|
+
curRange = [];
|
|
21149
|
+
}
|
|
21150
|
+
}
|
|
21151
|
+
oldLine += lines.length;
|
|
21152
|
+
newLine += lines.length;
|
|
21153
|
+
}
|
|
21154
|
+
}
|
|
21155
|
+
for (const hunk of hunks) {
|
|
21156
|
+
for (let i = 0; i < hunk.lines.length; i++) {
|
|
21157
|
+
if (hunk.lines[i].endsWith("\n")) {
|
|
21158
|
+
hunk.lines[i] = hunk.lines[i].slice(0, -1);
|
|
21159
|
+
} else {
|
|
21160
|
+
hunk.lines.splice(i + 1, 0, "\");
|
|
21161
|
+
i++;
|
|
21162
|
+
}
|
|
21163
|
+
}
|
|
21164
|
+
}
|
|
21165
|
+
return {
|
|
21166
|
+
oldFileName,
|
|
21167
|
+
newFileName,
|
|
21168
|
+
oldHeader,
|
|
21169
|
+
newHeader,
|
|
21170
|
+
hunks
|
|
21171
|
+
};
|
|
21172
|
+
}
|
|
21173
|
+
}
|
|
21174
|
+
function formatPatch(patch) {
|
|
21175
|
+
if (Array.isArray(patch)) {
|
|
21176
|
+
return patch.map(formatPatch).join("\n");
|
|
21177
|
+
}
|
|
21178
|
+
const ret = [];
|
|
21179
|
+
if (patch.oldFileName == patch.newFileName) {
|
|
21180
|
+
ret.push("Index: " + patch.oldFileName);
|
|
21181
|
+
}
|
|
21182
|
+
ret.push("===================================================================");
|
|
21183
|
+
ret.push("--- " + patch.oldFileName + (typeof patch.oldHeader === "undefined" ? "" : " " + patch.oldHeader));
|
|
21184
|
+
ret.push("+++ " + patch.newFileName + (typeof patch.newHeader === "undefined" ? "" : " " + patch.newHeader));
|
|
21185
|
+
for (let i = 0; i < patch.hunks.length; i++) {
|
|
21186
|
+
const hunk = patch.hunks[i];
|
|
21187
|
+
if (hunk.oldLines === 0) {
|
|
21188
|
+
hunk.oldStart -= 1;
|
|
21189
|
+
}
|
|
21190
|
+
if (hunk.newLines === 0) {
|
|
21191
|
+
hunk.newStart -= 1;
|
|
21192
|
+
}
|
|
21193
|
+
ret.push("@@ -" + hunk.oldStart + "," + hunk.oldLines + " +" + hunk.newStart + "," + hunk.newLines + " @@");
|
|
21194
|
+
for (const line of hunk.lines) {
|
|
21195
|
+
ret.push(line);
|
|
21196
|
+
}
|
|
21197
|
+
}
|
|
21198
|
+
return ret.join("\n") + "\n";
|
|
21199
|
+
}
|
|
21200
|
+
function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
21201
|
+
if (typeof options === "function") {
|
|
21202
|
+
options = { callback: options };
|
|
21203
|
+
}
|
|
21204
|
+
if (!(options === null || options === void 0 ? void 0 : options.callback)) {
|
|
21205
|
+
const patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
|
|
21206
|
+
if (!patchObj) {
|
|
21207
|
+
return;
|
|
21208
|
+
}
|
|
21209
|
+
return formatPatch(patchObj);
|
|
21210
|
+
} else {
|
|
21211
|
+
const { callback } = options;
|
|
21212
|
+
structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: (patchObj) => {
|
|
21213
|
+
if (!patchObj) {
|
|
21214
|
+
callback(void 0);
|
|
21215
|
+
} else {
|
|
21216
|
+
callback(formatPatch(patchObj));
|
|
21217
|
+
}
|
|
21218
|
+
} }));
|
|
21219
|
+
}
|
|
21220
|
+
}
|
|
21221
|
+
function splitLines(text2) {
|
|
21222
|
+
const hasTrailingNl = text2.endsWith("\n");
|
|
21223
|
+
const result = text2.split("\n").map((line) => line + "\n");
|
|
21224
|
+
if (hasTrailingNl) {
|
|
21225
|
+
result.pop();
|
|
21226
|
+
} else {
|
|
21227
|
+
result.push(result.pop().slice(0, -1));
|
|
21228
|
+
}
|
|
21229
|
+
return result;
|
|
21230
|
+
}
|
|
21231
|
+
|
|
21232
|
+
// dist/hermes-file-changes.js
|
|
21233
|
+
var MAX_DIFF_BYTES = 1024 * 1024;
|
|
21234
|
+
var MAX_DIFFS = 32;
|
|
21235
|
+
function hermesFileChanges(update) {
|
|
21236
|
+
const changes = [];
|
|
21237
|
+
let bytes = 0;
|
|
21238
|
+
for (const block of update.content ?? []) {
|
|
21239
|
+
if (block.type !== "diff")
|
|
21240
|
+
continue;
|
|
21241
|
+
const { path: path7, oldText, newText } = block;
|
|
21242
|
+
if (!path7.trim() || /[\0\r\n]/u.test(path7) || typeof newText !== "string")
|
|
21243
|
+
continue;
|
|
21244
|
+
if (oldText === newText)
|
|
21245
|
+
continue;
|
|
21246
|
+
bytes += Buffer.byteLength(oldText ?? "") + Buffer.byteLength(newText);
|
|
21247
|
+
if (bytes > MAX_DIFF_BYTES || changes.length === MAX_DIFFS)
|
|
21248
|
+
return [];
|
|
21249
|
+
changes.push({
|
|
21250
|
+
path: path7,
|
|
21251
|
+
kind: "update",
|
|
21252
|
+
diffScope: "fragment",
|
|
21253
|
+
unifiedDiff: createTwoFilesPatch(path7, path7, oldText ?? "", newText, "", "", {
|
|
21254
|
+
context: 3
|
|
21255
|
+
})
|
|
21256
|
+
});
|
|
21257
|
+
}
|
|
21258
|
+
return changes;
|
|
21259
|
+
}
|
|
21260
|
+
function hermesToolOutput(update) {
|
|
21261
|
+
const content = [];
|
|
21262
|
+
for (const block of update.content ?? []) {
|
|
21263
|
+
if (block.type !== "content")
|
|
21264
|
+
continue;
|
|
21265
|
+
if (block.content.type === "text" && block.content.text) {
|
|
21266
|
+
content.push({ type: "text", text: block.content.text });
|
|
21267
|
+
} else if (block.content.type === "image") {
|
|
21268
|
+
content.push({
|
|
21269
|
+
type: "image",
|
|
21270
|
+
mimeType: block.content.mimeType,
|
|
21271
|
+
base64Data: block.content.data
|
|
21272
|
+
});
|
|
21273
|
+
}
|
|
21274
|
+
}
|
|
21275
|
+
if (!content.length && typeof update.rawOutput === "string" && update.rawOutput) {
|
|
21276
|
+
content.push({ type: "text", text: update.rawOutput });
|
|
21277
|
+
}
|
|
21278
|
+
return content.length ? { content } : null;
|
|
21279
|
+
}
|
|
21280
|
+
|
|
21281
|
+
// dist/hermes-compaction.js
|
|
21282
|
+
function hermesCompactionOutcome(outcome, nativeText) {
|
|
21283
|
+
if (outcome.status !== "succeeded")
|
|
21284
|
+
return outcome;
|
|
21285
|
+
const text2 = nativeText.trim();
|
|
21286
|
+
if (/^Context compressed: \d+ -> \d+ messages\n~[\d,]+ -> ~[\d,]+ tokens$/u.test(text2)) {
|
|
21287
|
+
return { status: "succeeded" };
|
|
21288
|
+
}
|
|
21289
|
+
if (/^(?:Compression failed:|Error executing \/compress:|Context compression not available)/u.test(text2)) {
|
|
21290
|
+
return { status: "failed", error: { code: "nativeFailure", message: text2, retryable: false } };
|
|
21291
|
+
}
|
|
21292
|
+
return { status: "cancelled", reason: text2 || "Hermes did not confirm context compression" };
|
|
21293
|
+
}
|
|
21294
|
+
|
|
21295
|
+
// dist/hermes-session.js
|
|
20643
21296
|
var HOST_ERROR_CODES = {
|
|
20644
21297
|
notInstalled: "notInstalled",
|
|
20645
21298
|
authenticationRequired: "authenticationRequired",
|
|
@@ -20682,7 +21335,7 @@ function usageFromContext(used, size) {
|
|
|
20682
21335
|
...used !== void 0 ? { contextUsedTokens: used } : {}
|
|
20683
21336
|
};
|
|
20684
21337
|
}
|
|
20685
|
-
function projectPermissionOptions(options) {
|
|
21338
|
+
function projectPermissionOptions(options, effects) {
|
|
20686
21339
|
const actionEffects = {
|
|
20687
21340
|
allow_once: "allowOnce",
|
|
20688
21341
|
allow_always: "allowAlways",
|
|
@@ -20698,7 +21351,7 @@ function projectPermissionOptions(options) {
|
|
|
20698
21351
|
const actions = [];
|
|
20699
21352
|
const optionIdByAction = /* @__PURE__ */ new Map();
|
|
20700
21353
|
for (const option of options) {
|
|
20701
|
-
const effect = actionEffects[option.kind];
|
|
21354
|
+
const effect = effects?.[option.optionId] ?? actionEffects[option.kind];
|
|
20702
21355
|
if (!effect)
|
|
20703
21356
|
continue;
|
|
20704
21357
|
actions.push({
|
|
@@ -20714,18 +21367,47 @@ var ActiveTurn = class {
|
|
|
20714
21367
|
turnId;
|
|
20715
21368
|
turnKey;
|
|
20716
21369
|
input;
|
|
21370
|
+
persistsHistory;
|
|
21371
|
+
compactionItem;
|
|
21372
|
+
compactionText = "";
|
|
21373
|
+
nativeCompactionOutcome;
|
|
21374
|
+
nativeTurnSnapshot;
|
|
20717
21375
|
#currentText = null;
|
|
20718
21376
|
#toolItems = /* @__PURE__ */ new Map();
|
|
20719
21377
|
#finishedItems = [];
|
|
21378
|
+
#toolChanges = /* @__PURE__ */ new Map();
|
|
20720
21379
|
#emittedTerminalItemIds = /* @__PURE__ */ new Set();
|
|
20721
|
-
|
|
21380
|
+
rememberFileChanges(toolCallId, update) {
|
|
21381
|
+
const changes = hermesFileChanges(update);
|
|
21382
|
+
if (update.content?.some((block) => block.type === "diff"))
|
|
21383
|
+
this.#toolChanges.set(toolCallId, changes);
|
|
21384
|
+
}
|
|
21385
|
+
completeFileChanges(toolCallId, sourceItemId) {
|
|
21386
|
+
const changes = this.#toolChanges.get(toolCallId);
|
|
21387
|
+
this.#toolChanges.delete(toolCallId);
|
|
21388
|
+
if (!changes?.length)
|
|
21389
|
+
return null;
|
|
21390
|
+
const item = {
|
|
21391
|
+
type: "fileChange",
|
|
21392
|
+
itemId: hostItemIdSchema.parse(randomUUID2()),
|
|
21393
|
+
changes,
|
|
21394
|
+
sourceItemIds: [sourceItemId]
|
|
21395
|
+
};
|
|
21396
|
+
const snapshot = { item, outcome: { status: "succeeded" } };
|
|
21397
|
+
this.#finishedItems.push(snapshot);
|
|
21398
|
+
this.#emittedTerminalItemIds.add(item.itemId);
|
|
21399
|
+
return snapshot;
|
|
21400
|
+
}
|
|
21401
|
+
constructor(turnId, turnKey, input, persistsHistory, compaction) {
|
|
20722
21402
|
this.turnId = hostTurnIdSchema.parse(turnId);
|
|
20723
21403
|
this.turnKey = turnKey;
|
|
20724
21404
|
this.input = input;
|
|
21405
|
+
this.persistsHistory = persistsHistory;
|
|
21406
|
+
this.compactionItem = compaction ? { type: "contextCompaction", itemId: hostItemIdSchema.parse(randomUUID2()) } : null;
|
|
20725
21407
|
}
|
|
20726
|
-
appendText(kind,
|
|
21408
|
+
appendText(kind, text2) {
|
|
20727
21409
|
if (this.#currentText && this.#currentText.kind === kind) {
|
|
20728
|
-
const item2 = { ...this.#currentText.item, text: this.#currentText.item.text +
|
|
21410
|
+
const item2 = { ...this.#currentText.item, text: this.#currentText.item.text + text2 };
|
|
20729
21411
|
this.#currentText = { kind, item: item2 };
|
|
20730
21412
|
return { startedItem: null, item: item2 };
|
|
20731
21413
|
}
|
|
@@ -20735,8 +21417,8 @@ var ActiveTurn = class {
|
|
|
20735
21417
|
outcome: { status: "succeeded" }
|
|
20736
21418
|
});
|
|
20737
21419
|
}
|
|
20738
|
-
const item = kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(
|
|
20739
|
-
const appended = { ...item, text };
|
|
21420
|
+
const item = kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(randomUUID2()), text: "" } : { type: "agentMessage", itemId: hostItemIdSchema.parse(randomUUID2()), text: "" };
|
|
21421
|
+
const appended = { ...item, text: text2 };
|
|
20740
21422
|
this.#currentText = { kind, item: appended };
|
|
20741
21423
|
return { startedItem: item, item: appended };
|
|
20742
21424
|
}
|
|
@@ -20794,6 +21476,13 @@ var ActiveTurn = class {
|
|
|
20794
21476
|
}
|
|
20795
21477
|
this.#toolItems.clear();
|
|
20796
21478
|
}
|
|
21479
|
+
finishCompaction(outcome) {
|
|
21480
|
+
if (!this.compactionItem)
|
|
21481
|
+
return null;
|
|
21482
|
+
const itemOutcome = outcome.status === "succeeded" && this.nativeCompactionOutcome ? this.nativeCompactionOutcome : hermesCompactionOutcome(outcome, this.compactionText);
|
|
21483
|
+
this.#finishedItems.push({ item: this.compactionItem, outcome: itemOutcome });
|
|
21484
|
+
return itemOutcome;
|
|
21485
|
+
}
|
|
20797
21486
|
drainPendingItems() {
|
|
20798
21487
|
const pending = this.#finishedItems.filter((snapshot) => !this.#emittedTerminalItemIds.has(snapshot.item.itemId));
|
|
20799
21488
|
this.#finishedItems = [];
|
|
@@ -20812,25 +21501,30 @@ var ActiveTurn = class {
|
|
|
20812
21501
|
function isPlainObjectOrArray(value) {
|
|
20813
21502
|
return typeof value === "object" && value !== null;
|
|
20814
21503
|
}
|
|
20815
|
-
function toolOutputFromUpdate(update) {
|
|
20816
|
-
const content = [];
|
|
20817
|
-
if (Array.isArray(update.content)) {
|
|
20818
|
-
for (const block of update.content) {
|
|
20819
|
-
const candidate = block;
|
|
20820
|
-
if (candidate?.type === "text" && typeof candidate.text === "string" && candidate.text.length > 0) {
|
|
20821
|
-
content.push({ type: "text", text: candidate.text });
|
|
20822
|
-
}
|
|
20823
|
-
}
|
|
20824
|
-
}
|
|
20825
|
-
if (content.length === 0 && typeof update.rawOutput === "string" && update.rawOutput.length > 0) {
|
|
20826
|
-
content.push({ type: "text", text: update.rawOutput });
|
|
20827
|
-
}
|
|
20828
|
-
return content.length > 0 ? { content } : null;
|
|
20829
|
-
}
|
|
20830
21504
|
function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
20831
21505
|
const turns = [];
|
|
20832
21506
|
let current = null;
|
|
20833
21507
|
let toolItemIndexes = /* @__PURE__ */ new Map();
|
|
21508
|
+
const toolChanges = /* @__PURE__ */ new Map();
|
|
21509
|
+
const appendFileChanges = (toolCallId, update, sourceItemId) => {
|
|
21510
|
+
const changes = hermesFileChanges(update);
|
|
21511
|
+
if (update.content?.some((block) => block.type === "diff"))
|
|
21512
|
+
toolChanges.set(toolCallId, changes);
|
|
21513
|
+
const confirmedChanges = toolChanges.get(toolCallId);
|
|
21514
|
+
if (current && update.status === "completed" && confirmedChanges?.length) {
|
|
21515
|
+
current.items.push({
|
|
21516
|
+
item: {
|
|
21517
|
+
type: "fileChange",
|
|
21518
|
+
itemId: hostItemIdSchema.parse(randomUUID2()),
|
|
21519
|
+
changes: confirmedChanges,
|
|
21520
|
+
sourceItemIds: [sourceItemId]
|
|
21521
|
+
},
|
|
21522
|
+
outcome: { status: "succeeded" }
|
|
21523
|
+
});
|
|
21524
|
+
}
|
|
21525
|
+
if (update.status === "completed" || update.status === "failed")
|
|
21526
|
+
toolChanges.delete(toolCallId);
|
|
21527
|
+
};
|
|
20834
21528
|
const closeTurn = () => {
|
|
20835
21529
|
if (!current || current.items.length === 0) {
|
|
20836
21530
|
current = null;
|
|
@@ -20854,21 +21548,22 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
|
20854
21548
|
});
|
|
20855
21549
|
current = null;
|
|
20856
21550
|
toolItemIndexes = /* @__PURE__ */ new Map();
|
|
21551
|
+
toolChanges.clear();
|
|
20857
21552
|
};
|
|
20858
|
-
const pushText = (kind,
|
|
21553
|
+
const pushText = (kind, text2) => {
|
|
20859
21554
|
if (!current)
|
|
20860
21555
|
current = { inputText: "(resumed)", items: [], turnKey: `history-${turns.length + 1}` };
|
|
20861
21556
|
const last = current.items.at(-1);
|
|
20862
21557
|
if (last && last.item.type === kind) {
|
|
20863
21558
|
if (kind === "reasoning") {
|
|
20864
|
-
last.item.text +=
|
|
21559
|
+
last.item.text += text2;
|
|
20865
21560
|
} else {
|
|
20866
|
-
last.item.text +=
|
|
21561
|
+
last.item.text += text2;
|
|
20867
21562
|
}
|
|
20868
21563
|
return;
|
|
20869
21564
|
}
|
|
20870
21565
|
current.items.push({
|
|
20871
|
-
item: kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(
|
|
21566
|
+
item: kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(randomUUID2()), text: text2 } : { type: "agentMessage", itemId: hostItemIdSchema.parse(randomUUID2()), text: text2 },
|
|
20872
21567
|
outcome: { status: "succeeded" }
|
|
20873
21568
|
});
|
|
20874
21569
|
};
|
|
@@ -20893,12 +21588,12 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
|
20893
21588
|
if (!current)
|
|
20894
21589
|
current = { inputText: "(resumed)", items: [], turnKey: `history-${turns.length + 1}` };
|
|
20895
21590
|
const update = event.update;
|
|
20896
|
-
const output =
|
|
21591
|
+
const output = hermesToolOutput(update);
|
|
20897
21592
|
const toolName = typeof update.title === "string" && update.title.trim() || typeof update.name === "string" && update.name.trim() || event.toolCallId;
|
|
20898
21593
|
current.items.push({
|
|
20899
21594
|
item: {
|
|
20900
21595
|
type: "toolExecution",
|
|
20901
|
-
itemId: hostItemIdSchema.parse(
|
|
21596
|
+
itemId: hostItemIdSchema.parse(randomUUID2()),
|
|
20902
21597
|
toolName,
|
|
20903
21598
|
arguments: isPlainObjectOrArray(update.rawInput) ? update.rawInput : null,
|
|
20904
21599
|
...output ? { output } : {}
|
|
@@ -20909,6 +21604,9 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
|
20909
21604
|
} : { status: "cancelled", reason: "Turn ended before terminal tool update" }
|
|
20910
21605
|
});
|
|
20911
21606
|
toolItemIndexes.set(event.toolCallId, current.items.length - 1);
|
|
21607
|
+
const sourceItemId = current.items.at(-1)?.item.itemId;
|
|
21608
|
+
if (sourceItemId)
|
|
21609
|
+
appendFileChanges(event.toolCallId, update, sourceItemId);
|
|
20912
21610
|
break;
|
|
20913
21611
|
}
|
|
20914
21612
|
case "tool.update": {
|
|
@@ -20921,7 +21619,7 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
|
20921
21619
|
if (!prior || prior.item.type !== "toolExecution")
|
|
20922
21620
|
break;
|
|
20923
21621
|
const update = event.update;
|
|
20924
|
-
const output =
|
|
21622
|
+
const output = hermesToolOutput(update);
|
|
20925
21623
|
current.items[itemIndex] = {
|
|
20926
21624
|
item: { ...prior.item, ...output ? { output } : {} },
|
|
20927
21625
|
outcome: update.status === "completed" ? { status: "succeeded" } : update.status === "failed" ? {
|
|
@@ -20929,6 +21627,7 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
|
|
|
20929
21627
|
error: harnessError("nativeFailure", "Tool execution failed")
|
|
20930
21628
|
} : prior.outcome
|
|
20931
21629
|
};
|
|
21630
|
+
appendFileChanges(event.toolCallId, update, prior.item.itemId);
|
|
20932
21631
|
break;
|
|
20933
21632
|
}
|
|
20934
21633
|
default:
|
|
@@ -20967,6 +21666,7 @@ var HermesSession = class {
|
|
|
20967
21666
|
initialState;
|
|
20968
21667
|
initialUsage;
|
|
20969
21668
|
outputs;
|
|
21669
|
+
commands;
|
|
20970
21670
|
#channel = new HarnessOutputChannel();
|
|
20971
21671
|
#transport;
|
|
20972
21672
|
#nativeRef;
|
|
@@ -20980,16 +21680,37 @@ var HermesSession = class {
|
|
|
20980
21680
|
#completedTurns = [];
|
|
20981
21681
|
#historyTurns;
|
|
20982
21682
|
#latestUsage;
|
|
21683
|
+
#questions = new HermesQuestions((output) => this.#channel.emit(output));
|
|
20983
21684
|
#approvalWaiters = /* @__PURE__ */ new Map();
|
|
20984
21685
|
constructor(options) {
|
|
20985
21686
|
this.#transport = options.transport;
|
|
21687
|
+
this.capabilities.history.fork = options.supportsDerivation === true;
|
|
21688
|
+
this.capabilities.history.rollbackLastTurn = options.supportsDerivation === true;
|
|
21689
|
+
this.commands = {
|
|
21690
|
+
list: async () => this.#closed || this.#faulted ? err("invalidState", "Hermes Session is unavailable") : ok(hermesCommandCatalog(this.#transport.availableCommands ?? [])),
|
|
21691
|
+
execute: async (command) => {
|
|
21692
|
+
const text2 = hermesCommandText(command, hermesCommandCatalog(this.#transport.availableCommands ?? []));
|
|
21693
|
+
if (!text2.ok)
|
|
21694
|
+
return text2;
|
|
21695
|
+
return this.execute({
|
|
21696
|
+
type: "turn.start",
|
|
21697
|
+
turnId: command.turnId,
|
|
21698
|
+
input: [{ type: "text", text: text2.value }]
|
|
21699
|
+
});
|
|
21700
|
+
}
|
|
21701
|
+
};
|
|
20986
21702
|
this.#nativeRef = options.nativeRef;
|
|
20987
21703
|
this.#onSettle = options.onSettle;
|
|
20988
21704
|
const projected = projectHermesModelState(options.open.session.models);
|
|
20989
21705
|
const modes = options.open.session.modes;
|
|
20990
21706
|
this.#availableModels = options.open.session.models?.availableModels ?? [];
|
|
21707
|
+
this.capabilities.configuration.selectThinkingOption = !!this.#transport.setThinking;
|
|
20991
21708
|
this.#state = {
|
|
20992
21709
|
nativeRef: this.#nativeRef,
|
|
21710
|
+
...options.open.session.thinkingOptions ? { availableThinkingOptions: options.open.session.thinkingOptions } : {},
|
|
21711
|
+
...options.open.session.currentThinkingOptionId ? {
|
|
21712
|
+
effectiveThinkingOptionId: harnessThinkingOptionIdSchema.parse(options.open.session.currentThinkingOptionId)
|
|
21713
|
+
} : {},
|
|
20993
21714
|
...projected.effectiveModel ? { effectiveModel: projected.effectiveModel } : {},
|
|
20994
21715
|
...projected.resolvedModelLabel ? { resolvedModelLabel: projected.resolvedModelLabel } : {},
|
|
20995
21716
|
...modes ? { effectivePermissionModeId: harnessPermissionModeIdSchema.parse(modes.currentModeId) } : {}
|
|
@@ -21001,9 +21722,19 @@ var HermesSession = class {
|
|
|
21001
21722
|
this.outputs = this.#channel.outputs;
|
|
21002
21723
|
this.#transport.onFault = (error51) => this.#fault(transportErrorToHarness(error51));
|
|
21003
21724
|
}
|
|
21725
|
+
get busy() {
|
|
21726
|
+
return this.#activeTurn !== null;
|
|
21727
|
+
}
|
|
21004
21728
|
async readSnapshot() {
|
|
21005
21729
|
if (this.#closed)
|
|
21006
21730
|
return err("invalidState", "Hermes Session is closed");
|
|
21731
|
+
if (this.#transport.readNativeSnapshot) {
|
|
21732
|
+
try {
|
|
21733
|
+
return ok({ ...await this.#transport.readNativeSnapshot(), state: { ...this.#state } });
|
|
21734
|
+
} catch (error51) {
|
|
21735
|
+
return err("nativeFailure", error51 instanceof Error ? error51.message : String(error51));
|
|
21736
|
+
}
|
|
21737
|
+
}
|
|
21007
21738
|
return ok({
|
|
21008
21739
|
turns: [...this.#historyTurns, ...this.#completedTurns],
|
|
21009
21740
|
state: { ...this.#state }
|
|
@@ -21019,11 +21750,11 @@ var HermesSession = class {
|
|
|
21019
21750
|
case "turn.cancel":
|
|
21020
21751
|
return this.#cancelTurn(command);
|
|
21021
21752
|
case "interaction.respond":
|
|
21022
|
-
return Promise.resolve(this.#respondToApproval(command));
|
|
21753
|
+
return Promise.resolve(command.response.type === "question" ? this.#questions.respond(command) : this.#respondToApproval(command));
|
|
21023
21754
|
case "model.select":
|
|
21024
21755
|
return this.#selectModel(command);
|
|
21025
21756
|
case "thinking.select":
|
|
21026
|
-
return
|
|
21757
|
+
return this.#selectThinking(command);
|
|
21027
21758
|
case "permissionMode.select":
|
|
21028
21759
|
return this.#selectPermissionMode(command);
|
|
21029
21760
|
default: {
|
|
@@ -21037,13 +21768,14 @@ var HermesSession = class {
|
|
|
21037
21768
|
return;
|
|
21038
21769
|
this.#closed = true;
|
|
21039
21770
|
this.#cancelApprovalWaiters();
|
|
21040
|
-
this.#activeTurn
|
|
21041
|
-
|
|
21771
|
+
if (this.#activeTurn)
|
|
21772
|
+
this.#completeActiveTurn(this.#activeTurn, { status: "cancelled", reason: "Session closed" });
|
|
21042
21773
|
await this.#transport.close().catch(() => void 0);
|
|
21043
21774
|
this.#channel.end();
|
|
21044
21775
|
this.#onSettle(this);
|
|
21045
21776
|
}
|
|
21046
21777
|
#cancelApprovalWaiters() {
|
|
21778
|
+
this.#questions.cancel();
|
|
21047
21779
|
for (const [interactionId, waiter] of this.#approvalWaiters) {
|
|
21048
21780
|
waiter.resolve({ outcome: { outcome: "cancelled" } });
|
|
21049
21781
|
this.#emit({
|
|
@@ -21077,23 +21809,31 @@ var HermesSession = class {
|
|
|
21077
21809
|
if (this.#activeTurn) {
|
|
21078
21810
|
return err("sessionBusy", "Hermes Session already has an active Turn", true);
|
|
21079
21811
|
}
|
|
21080
|
-
const
|
|
21081
|
-
if (
|
|
21812
|
+
const text2 = command.input.map((chunk) => chunk.text).join("\n");
|
|
21813
|
+
if (text2.trim().length === 0) {
|
|
21082
21814
|
return err("invalidRequest", "turn.start requires non-empty text input");
|
|
21083
21815
|
}
|
|
21084
|
-
const turnKey =
|
|
21085
|
-
const
|
|
21816
|
+
const turnKey = randomUUID2();
|
|
21817
|
+
const firstToken = text2.trim().split(/\s/u)[0]?.replace(/^\/+/u, "").toLowerCase();
|
|
21818
|
+
const nativeCommand = this.#transport.nativeCommandName ? this.#transport.nativeCommandName(text2) : text2.trimStart().startsWith("/") && (this.#transport.availableCommands ?? []).some((entry) => entry.name === firstToken) ? firstToken : null;
|
|
21819
|
+
const isNativeCommand = nativeCommand != null;
|
|
21820
|
+
const active = new ActiveTurn(command.turnId, turnKey, command.input, !isNativeCommand, isNativeCommand && nativeCommand === "compress");
|
|
21086
21821
|
this.#activeTurn = active;
|
|
21087
21822
|
this.#activeTurnId = active.turnId;
|
|
21088
|
-
void this.#runTurn(active,
|
|
21823
|
+
void this.#runTurn(active, text2);
|
|
21089
21824
|
return ok({ turnId: active.turnId });
|
|
21090
21825
|
}
|
|
21091
|
-
async #runTurn(active,
|
|
21826
|
+
async #runTurn(active, text2) {
|
|
21092
21827
|
this.#emit({ type: "turn.started", turnId: active.turnId });
|
|
21828
|
+
if (active.compactionItem)
|
|
21829
|
+
this.#emit({ type: "item.started", turnId: active.turnId, item: active.compactionItem });
|
|
21093
21830
|
let promptResponse = null;
|
|
21094
21831
|
let failure2 = null;
|
|
21832
|
+
let previousNativeTurnKey;
|
|
21095
21833
|
try {
|
|
21096
|
-
|
|
21834
|
+
if (active.persistsHistory && this.#transport.readNativeSnapshot)
|
|
21835
|
+
previousNativeTurnKey = (await this.#transport.readNativeSnapshot()).turns.at(-1)?.nativeTurnRef.nativeTurnKey;
|
|
21836
|
+
promptResponse = await this.#transport.runTurn(text2, (event) => this.#handleTransportEvent(active, event), (request) => this.#handlePermissionRequest(active, request), (request) => this.#handleQuestionRequest(active, request));
|
|
21097
21837
|
} catch (error51) {
|
|
21098
21838
|
failure2 = error51 instanceof HermesTransportError ? transportErrorToHarness(error51) : harnessError("nativeFailure", error51 instanceof Error ? error51.message : String(error51));
|
|
21099
21839
|
}
|
|
@@ -21113,6 +21853,19 @@ var HermesSession = class {
|
|
|
21113
21853
|
} else {
|
|
21114
21854
|
outcome = { status: "succeeded" };
|
|
21115
21855
|
}
|
|
21856
|
+
active.nativeCompactionOutcome = promptResponse?.compactionOutcome;
|
|
21857
|
+
if (active.persistsHistory && this.#transport.readNativeSnapshot) {
|
|
21858
|
+
try {
|
|
21859
|
+
const latest = (await this.#transport.readNativeSnapshot()).turns.at(-1);
|
|
21860
|
+
if (latest?.nativeTurnRef.nativeTurnKey !== previousNativeTurnKey)
|
|
21861
|
+
active.nativeTurnSnapshot = latest;
|
|
21862
|
+
} catch (error51) {
|
|
21863
|
+
outcome = {
|
|
21864
|
+
status: "failed",
|
|
21865
|
+
error: harnessError("nativeFailure", error51 instanceof Error ? error51.message : String(error51))
|
|
21866
|
+
};
|
|
21867
|
+
}
|
|
21868
|
+
}
|
|
21116
21869
|
const terminalUsage = promptResponse ? usageFromPromptResponse(promptResponse.usage) : null;
|
|
21117
21870
|
const usage = terminalUsage ? this.#mergeUsage(terminalUsage) : null;
|
|
21118
21871
|
this.#completeActiveTurn(active, outcome, usage);
|
|
@@ -21120,10 +21873,17 @@ var HermesSession = class {
|
|
|
21120
21873
|
#completeActiveTurn(active, outcome, usage = null) {
|
|
21121
21874
|
if (this.#activeTurn !== active)
|
|
21122
21875
|
return;
|
|
21876
|
+
this.#cancelApprovalWaiters();
|
|
21123
21877
|
this.#activeTurn = null;
|
|
21124
21878
|
this.#activeTurnId = null;
|
|
21125
21879
|
active.finish();
|
|
21126
|
-
const
|
|
21880
|
+
const compactionOutcome = active.finishCompaction(outcome);
|
|
21881
|
+
if (outcome.status === "succeeded" && compactionOutcome?.status === "failed") {
|
|
21882
|
+
outcome = { status: "failed", error: compactionOutcome.error };
|
|
21883
|
+
}
|
|
21884
|
+
if (active.nativeTurnSnapshot?.checkpoint)
|
|
21885
|
+
outcome = { ...outcome, checkpoint: active.nativeTurnSnapshot.checkpoint };
|
|
21886
|
+
const nativeTurnRef = active.nativeTurnSnapshot?.nativeTurnRef ?? nativeTurnRefSchema.parse({
|
|
21127
21887
|
harnessId: this.#nativeRef.harnessId,
|
|
21128
21888
|
nativeSessionId: this.#nativeRef.nativeSessionId,
|
|
21129
21889
|
nativeTurnKey: active.turnKey,
|
|
@@ -21136,10 +21896,18 @@ var HermesSession = class {
|
|
|
21136
21896
|
if (usage) {
|
|
21137
21897
|
this.#emit({ type: "session.usage.changed", usage, observedForTurnId: active.turnId });
|
|
21138
21898
|
}
|
|
21139
|
-
this.#emit({
|
|
21140
|
-
|
|
21899
|
+
this.#emit({
|
|
21900
|
+
type: "turn.completed",
|
|
21901
|
+
turnId: active.turnId,
|
|
21902
|
+
...active.persistsHistory && (!this.#transport.readNativeSnapshot || active.nativeTurnSnapshot) ? { nativeTurnRef } : {},
|
|
21903
|
+
outcome
|
|
21904
|
+
});
|
|
21905
|
+
if (active.persistsHistory)
|
|
21906
|
+
this.#completedTurns.push(turnSnapshot);
|
|
21141
21907
|
}
|
|
21142
21908
|
#handleTransportEvent(active, event) {
|
|
21909
|
+
if (this.#activeTurn !== active)
|
|
21910
|
+
return;
|
|
21143
21911
|
switch (event.type) {
|
|
21144
21912
|
case "usage": {
|
|
21145
21913
|
const usage = usageFromContext(event.used, event.size);
|
|
@@ -21157,6 +21925,8 @@ var HermesSession = class {
|
|
|
21157
21925
|
this.#appendTextItem(active, "reasoning", event.text);
|
|
21158
21926
|
return;
|
|
21159
21927
|
case "agent.text":
|
|
21928
|
+
if (active.compactionItem)
|
|
21929
|
+
active.compactionText += event.text;
|
|
21160
21930
|
this.#appendTextItem(active, "agentMessage", event.text);
|
|
21161
21931
|
return;
|
|
21162
21932
|
case "tool.call": {
|
|
@@ -21172,8 +21942,8 @@ var HermesSession = class {
|
|
|
21172
21942
|
return;
|
|
21173
21943
|
}
|
|
21174
21944
|
}
|
|
21175
|
-
#appendTextItem(active, kind,
|
|
21176
|
-
const appended = active.appendText(kind,
|
|
21945
|
+
#appendTextItem(active, kind, text2) {
|
|
21946
|
+
const appended = active.appendText(kind, text2);
|
|
21177
21947
|
if (!appended)
|
|
21178
21948
|
return;
|
|
21179
21949
|
if (appended.startedItem) {
|
|
@@ -21183,7 +21953,7 @@ var HermesSession = class {
|
|
|
21183
21953
|
type: "item.updated",
|
|
21184
21954
|
turnId: active.turnId,
|
|
21185
21955
|
itemId: appended.item.itemId,
|
|
21186
|
-
update: { type: "text.append", text }
|
|
21956
|
+
update: { type: "text.append", text: text2 }
|
|
21187
21957
|
});
|
|
21188
21958
|
}
|
|
21189
21959
|
#startToolItem(active, toolCallId, title, name, rawInput) {
|
|
@@ -21193,7 +21963,7 @@ var HermesSession = class {
|
|
|
21193
21963
|
}
|
|
21194
21964
|
const item = {
|
|
21195
21965
|
type: "toolExecution",
|
|
21196
|
-
itemId: hostItemIdSchema.parse(
|
|
21966
|
+
itemId: hostItemIdSchema.parse(randomUUID2()),
|
|
21197
21967
|
toolName: (name ?? title ?? "").trim() || toolCallId,
|
|
21198
21968
|
arguments: isPlainObjectOrArray(rawInput) ? rawInput : null
|
|
21199
21969
|
};
|
|
@@ -21209,7 +21979,8 @@ var HermesSession = class {
|
|
|
21209
21979
|
const entry = active.getToolItem(update.toolCallId);
|
|
21210
21980
|
if (!entry)
|
|
21211
21981
|
return;
|
|
21212
|
-
|
|
21982
|
+
active.rememberFileChanges(update.toolCallId, update);
|
|
21983
|
+
const output = hermesToolOutput(update);
|
|
21213
21984
|
if (output && output.content.length > 0) {
|
|
21214
21985
|
active.updateToolOutput(update.toolCallId, output);
|
|
21215
21986
|
this.#emit({
|
|
@@ -21223,32 +21994,57 @@ var HermesSession = class {
|
|
|
21223
21994
|
const completed = active.completeToolItem(update.toolCallId, output, update.status === "failed");
|
|
21224
21995
|
if (completed) {
|
|
21225
21996
|
this.#emit({ type: "item.completed", turnId: active.turnId, snapshot: completed });
|
|
21997
|
+
if (update.status === "completed") {
|
|
21998
|
+
const fileChange = active.completeFileChanges(update.toolCallId, completed.item.itemId);
|
|
21999
|
+
if (fileChange) {
|
|
22000
|
+
this.#emit({ type: "item.started", turnId: active.turnId, item: fileChange.item });
|
|
22001
|
+
this.#emit({ type: "item.completed", turnId: active.turnId, snapshot: fileChange });
|
|
22002
|
+
}
|
|
22003
|
+
}
|
|
21226
22004
|
}
|
|
21227
22005
|
}
|
|
21228
22006
|
}
|
|
21229
22007
|
#handlePermissionRequest(active, request) {
|
|
21230
|
-
const projected = projectPermissionOptions(request.options);
|
|
22008
|
+
const projected = projectPermissionOptions(request.options, request.effects);
|
|
21231
22009
|
if (projected.actions.length === 0) {
|
|
21232
22010
|
return Promise.resolve({ outcome: { outcome: "cancelled" } });
|
|
21233
22011
|
}
|
|
21234
|
-
const interactionId = hostInteractionIdSchema.parse(
|
|
22012
|
+
const interactionId = hostInteractionIdSchema.parse(randomUUID2());
|
|
21235
22013
|
const interaction = {
|
|
21236
22014
|
type: "approval",
|
|
21237
22015
|
interactionId,
|
|
21238
22016
|
turnId: active.turnId,
|
|
21239
|
-
title: request.request.
|
|
22017
|
+
title: request.request.toolCall.title?.slice(0, 200) ?? "Hermes 请求批准",
|
|
22018
|
+
...request.description ? { description: request.description } : {},
|
|
21240
22019
|
subject: { type: "nativeAction" },
|
|
21241
22020
|
actions: projected.actions
|
|
21242
22021
|
};
|
|
21243
22022
|
this.#emitInteraction(interaction);
|
|
21244
|
-
|
|
22023
|
+
let abort = () => {
|
|
22024
|
+
};
|
|
22025
|
+
const pending = new Promise((resolve) => {
|
|
21245
22026
|
this.#approvalWaiters.set(interactionId, {
|
|
21246
22027
|
interaction,
|
|
21247
22028
|
turnId: active.turnId,
|
|
21248
22029
|
optionIdByAction: projected.optionIdByAction,
|
|
21249
22030
|
resolve
|
|
21250
22031
|
});
|
|
22032
|
+
abort = () => {
|
|
22033
|
+
if (!this.#approvalWaiters.delete(interactionId))
|
|
22034
|
+
return;
|
|
22035
|
+
resolve({ outcome: { outcome: "cancelled" } });
|
|
22036
|
+
this.#emit({
|
|
22037
|
+
type: "interaction.closed",
|
|
22038
|
+
interactionId,
|
|
22039
|
+
turnId: active.turnId,
|
|
22040
|
+
reason: request.signal?.reason === "expired" ? "expired" : "cancelled"
|
|
22041
|
+
});
|
|
22042
|
+
};
|
|
22043
|
+
request.signal?.addEventListener("abort", abort, { once: true });
|
|
22044
|
+
if (request.signal?.aborted)
|
|
22045
|
+
abort();
|
|
21251
22046
|
});
|
|
22047
|
+
return pending.finally(() => request.signal?.removeEventListener("abort", abort));
|
|
21252
22048
|
}
|
|
21253
22049
|
#respondToApproval(command) {
|
|
21254
22050
|
if (this.#faulted) {
|
|
@@ -21289,6 +22085,7 @@ var HermesSession = class {
|
|
|
21289
22085
|
}
|
|
21290
22086
|
try {
|
|
21291
22087
|
await this.#transport.cancel();
|
|
22088
|
+
this.#questions.cancel(command.turnId);
|
|
21292
22089
|
for (const [interactionId, waiter] of this.#approvalWaiters) {
|
|
21293
22090
|
if (waiter.turnId !== command.turnId)
|
|
21294
22091
|
continue;
|
|
@@ -21307,10 +22104,29 @@ var HermesSession = class {
|
|
|
21307
22104
|
return { ok: false, error: failure2 };
|
|
21308
22105
|
}
|
|
21309
22106
|
}
|
|
21310
|
-
|
|
21311
|
-
if (this.#activeTurn)
|
|
21312
|
-
return
|
|
22107
|
+
#handleQuestionRequest(active, request) {
|
|
22108
|
+
if (this.#activeTurn !== active)
|
|
22109
|
+
return Promise.resolve({ type: "question", answers: {}, cancelled: true });
|
|
22110
|
+
return this.#questions.open(active.turnId, request);
|
|
22111
|
+
}
|
|
22112
|
+
async #selectThinking(command) {
|
|
22113
|
+
if (!this.#transport.setThinking)
|
|
22114
|
+
return err("unsupported", "Hermes does not expose Thinking options");
|
|
22115
|
+
if (!this.#state.availableThinkingOptions?.some(({ id }) => id === command.thinkingOptionId))
|
|
22116
|
+
return err("invalidRequest", "Unknown Hermes Thinking option");
|
|
22117
|
+
try {
|
|
22118
|
+
const selected = await this.#transport.setThinking(command.thinkingOptionId);
|
|
22119
|
+
this.#state = {
|
|
22120
|
+
...this.#state,
|
|
22121
|
+
effectiveThinkingOptionId: harnessThinkingOptionIdSchema.parse(selected)
|
|
22122
|
+
};
|
|
22123
|
+
this.#emit({ type: "session.state.changed", state: { ...this.#state } });
|
|
22124
|
+
return ok({ completed: true });
|
|
22125
|
+
} catch (error51) {
|
|
22126
|
+
return err("nativeFailure", error51 instanceof Error ? error51.message : String(error51));
|
|
21313
22127
|
}
|
|
22128
|
+
}
|
|
22129
|
+
async #selectModel(command) {
|
|
21314
22130
|
const native = decodeHermesModelRefId(command.model.id);
|
|
21315
22131
|
if (!native) {
|
|
21316
22132
|
return err("invalidRequest", "Model Ref does not belong to Hermes");
|
|
@@ -21350,8 +22166,16 @@ var HermesSession = class {
|
|
|
21350
22166
|
}
|
|
21351
22167
|
return err("nativeFailure", error51 instanceof Error ? error51.message : "Hermes rejected Permission Mode selection");
|
|
21352
22168
|
}
|
|
22169
|
+
const locator = this.#nativeRef.locator;
|
|
22170
|
+
if (locator && typeof locator === "object" && !Array.isArray(locator) && locator.transport === "gateway") {
|
|
22171
|
+
this.#nativeRef = {
|
|
22172
|
+
...this.#nativeRef,
|
|
22173
|
+
locator: { ...locator, permissionModeId: command.permissionModeId }
|
|
22174
|
+
};
|
|
22175
|
+
}
|
|
21353
22176
|
this.#state = {
|
|
21354
22177
|
...this.#state,
|
|
22178
|
+
nativeRef: this.#nativeRef,
|
|
21355
22179
|
effectivePermissionModeId: harnessPermissionModeIdSchema.parse(command.permissionModeId)
|
|
21356
22180
|
};
|
|
21357
22181
|
this.#emit({ type: "session.state.changed", state: { ...this.#state } });
|
|
@@ -21359,11 +22183,1323 @@ var HermesSession = class {
|
|
|
21359
22183
|
}
|
|
21360
22184
|
};
|
|
21361
22185
|
|
|
22186
|
+
// dist/gateway-transport.js
|
|
22187
|
+
import { spawn as spawn3, spawnSync as spawnSync2 } from "node:child_process";
|
|
22188
|
+
import { access as access2 } from "node:fs/promises";
|
|
22189
|
+
import { createInterface } from "node:readline";
|
|
22190
|
+
|
|
22191
|
+
// dist/gateway-delegation.js
|
|
22192
|
+
import { mkdtemp, writeFile, rm } from "node:fs/promises";
|
|
22193
|
+
import os2 from "node:os";
|
|
22194
|
+
import path5 from "node:path";
|
|
22195
|
+
var HERMES_DELEGATION_GUIDANCE = `This Session runs inside codexhost.
|
|
22196
|
+
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.`;
|
|
22197
|
+
var required2 = [
|
|
22198
|
+
"CODEXHOST_CLI_PATH",
|
|
22199
|
+
"CODEXHOST_RUNTIME_ENDPOINT",
|
|
22200
|
+
"CODEXHOST_RUNTIME_TOKEN",
|
|
22201
|
+
"CODEXHOST_THREAD_ID"
|
|
22202
|
+
];
|
|
22203
|
+
async function prepareGatewayDelegation(environment) {
|
|
22204
|
+
if (!required2.every((key) => environment[key]))
|
|
22205
|
+
return { environment, bootstrap: "", dispose: async () => {
|
|
22206
|
+
} };
|
|
22207
|
+
const directory = await mkdtemp(path5.join(os2.tmpdir(), "codexhost-hermes-delegation-"));
|
|
22208
|
+
const file2 = path5.join(directory, "SKILL.md");
|
|
22209
|
+
try {
|
|
22210
|
+
await writeFile(file2, `---
|
|
22211
|
+
name: delegation
|
|
22212
|
+
description: Discover authorized codexhost agent collaboration.
|
|
22213
|
+
---
|
|
22214
|
+
|
|
22215
|
+
${HERMES_DELEGATION_GUIDANCE}
|
|
22216
|
+
`, { mode: 384 });
|
|
22217
|
+
return {
|
|
22218
|
+
environment: {
|
|
22219
|
+
...environment,
|
|
22220
|
+
HERMES_TUI_SKILLS: [environment.HERMES_TUI_SKILLS, "codexhost-runtime:delegation"].filter(Boolean).join(",")
|
|
22221
|
+
},
|
|
22222
|
+
// Reserve RPC stdout before native plugin imports. The registration is
|
|
22223
|
+
// process-local; no plugin is installed and no user config is changed.
|
|
22224
|
+
bootstrap: `from tui_gateway import server
|
|
22225
|
+
from pathlib import Path
|
|
22226
|
+
from hermes_cli.plugins import PluginContext, PluginManifest, get_plugin_manager
|
|
22227
|
+
PluginContext(PluginManifest(name="codexhost-runtime"), get_plugin_manager()).register_skill(
|
|
22228
|
+
"delegation", Path(${JSON.stringify(file2)}))
|
|
22229
|
+
`,
|
|
22230
|
+
dispose: () => rm(directory, { recursive: true, force: true })
|
|
22231
|
+
};
|
|
22232
|
+
} catch (error51) {
|
|
22233
|
+
await rm(directory, { recursive: true, force: true });
|
|
22234
|
+
throw error51;
|
|
22235
|
+
}
|
|
22236
|
+
}
|
|
22237
|
+
|
|
22238
|
+
// dist/gateway-transport.js
|
|
22239
|
+
function gatewayRecord(value) {
|
|
22240
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
22241
|
+
}
|
|
22242
|
+
function gatewayString(value) {
|
|
22243
|
+
return typeof value === "string" ? value : "";
|
|
22244
|
+
}
|
|
22245
|
+
var GATEWAY_LAUNCH = `import runpy
|
|
22246
|
+
runpy.run_module('tui_gateway.entry', run_name='__main__')`;
|
|
22247
|
+
var HermesGatewayTransport = class _HermesGatewayTransport {
|
|
22248
|
+
python;
|
|
22249
|
+
cwd;
|
|
22250
|
+
environment;
|
|
22251
|
+
timeoutMs;
|
|
22252
|
+
onEvent = () => void 0;
|
|
22253
|
+
onRequest = (id) => this.rejectRequest(id);
|
|
22254
|
+
onFault = () => void 0;
|
|
22255
|
+
#child = null;
|
|
22256
|
+
#nextId = 0;
|
|
22257
|
+
#pending = /* @__PURE__ */ new Map();
|
|
22258
|
+
#closed = false;
|
|
22259
|
+
#closePromise = null;
|
|
22260
|
+
#processClosed = Promise.resolve();
|
|
22261
|
+
#sessionErrors = /* @__PURE__ */ new Map();
|
|
22262
|
+
#sessionReady = /* @__PURE__ */ new Map();
|
|
22263
|
+
#sessionWaiters = /* @__PURE__ */ new Map();
|
|
22264
|
+
#stderr = "";
|
|
22265
|
+
#delegation;
|
|
22266
|
+
constructor(python, cwd, environment, timeoutMs = 3e4) {
|
|
22267
|
+
this.python = python;
|
|
22268
|
+
this.cwd = cwd;
|
|
22269
|
+
this.environment = environment;
|
|
22270
|
+
this.timeoutMs = timeoutMs;
|
|
22271
|
+
}
|
|
22272
|
+
static async probe(executable, cwd, environment) {
|
|
22273
|
+
const shim = await venvPythonFromShim(executable);
|
|
22274
|
+
const candidates = [
|
|
22275
|
+
...new Set([
|
|
22276
|
+
environment.CODEXHOST_HERMES_GATEWAY_PYTHON,
|
|
22277
|
+
shim,
|
|
22278
|
+
...inventoryPythonCandidates(executable)
|
|
22279
|
+
].filter((candidate) => !!candidate))
|
|
22280
|
+
];
|
|
22281
|
+
for (const candidate of candidates) {
|
|
22282
|
+
try {
|
|
22283
|
+
await access2(candidate);
|
|
22284
|
+
} catch {
|
|
22285
|
+
continue;
|
|
22286
|
+
}
|
|
22287
|
+
const transport = new _HermesGatewayTransport(candidate, cwd, environment, 2e4);
|
|
22288
|
+
try {
|
|
22289
|
+
await transport.start();
|
|
22290
|
+
return candidate;
|
|
22291
|
+
} catch {
|
|
22292
|
+
} finally {
|
|
22293
|
+
await transport.close();
|
|
22294
|
+
}
|
|
22295
|
+
}
|
|
22296
|
+
return null;
|
|
22297
|
+
}
|
|
22298
|
+
async prepareSession() {
|
|
22299
|
+
if (this.#child || this.#closed)
|
|
22300
|
+
throw new Error("Hermes gateway Session cannot be prepared after start");
|
|
22301
|
+
const prepared = await prepareGatewayDelegation(this.environment);
|
|
22302
|
+
if (this.#closed) {
|
|
22303
|
+
await prepared.dispose();
|
|
22304
|
+
throw new Error("Hermes gateway closed during Session preparation");
|
|
22305
|
+
}
|
|
22306
|
+
this.#delegation = prepared;
|
|
22307
|
+
}
|
|
22308
|
+
async start() {
|
|
22309
|
+
if (this.#child || this.#closed)
|
|
22310
|
+
throw new Error("Hermes gateway cannot be started twice");
|
|
22311
|
+
const child = spawn3(this.python, ["-I", "-u", "-c", (this.#delegation?.bootstrap ?? "") + GATEWAY_LAUNCH], {
|
|
22312
|
+
cwd: this.cwd,
|
|
22313
|
+
env: {
|
|
22314
|
+
...process.env,
|
|
22315
|
+
...this.#delegation?.environment ?? this.environment,
|
|
22316
|
+
HERMES_TUI_TOOL_PROGRESS: "all"
|
|
22317
|
+
},
|
|
22318
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
22319
|
+
windowsHide: true,
|
|
22320
|
+
detached: process.platform !== "win32"
|
|
22321
|
+
});
|
|
22322
|
+
this.#child = child;
|
|
22323
|
+
this.#processClosed = new Promise((resolve) => child.once("close", () => resolve()));
|
|
22324
|
+
child.stderr.on("data", (data) => {
|
|
22325
|
+
this.#stderr = sanitizeDiagnosticTail(this.#stderr + data.toString());
|
|
22326
|
+
});
|
|
22327
|
+
child.stdin.on("error", (error51) => this.#fault(error51));
|
|
22328
|
+
child.once("error", (error51) => this.#fault(error51));
|
|
22329
|
+
child.once("exit", () => {
|
|
22330
|
+
if (!this.#closed)
|
|
22331
|
+
this.#fault(new Error(`Hermes gateway exited: ${this.#stderr}`));
|
|
22332
|
+
});
|
|
22333
|
+
createInterface({ input: child.stdout }).on("line", (line) => {
|
|
22334
|
+
try {
|
|
22335
|
+
this.#frame(gatewayRecord(JSON.parse(line)));
|
|
22336
|
+
} catch (error51) {
|
|
22337
|
+
this.#fault(error51 instanceof Error ? error51 : new Error(String(error51)));
|
|
22338
|
+
}
|
|
22339
|
+
});
|
|
22340
|
+
const capabilities = await this.request("gateway.capabilities", {});
|
|
22341
|
+
if (capabilities.per_session_exclusive_submit !== true)
|
|
22342
|
+
throw new Error("Hermes gateway does not enforce exclusive turns");
|
|
22343
|
+
}
|
|
22344
|
+
async waitForSession(sessionId) {
|
|
22345
|
+
const error51 = this.#sessionErrors.get(sessionId);
|
|
22346
|
+
if (error51)
|
|
22347
|
+
throw error51;
|
|
22348
|
+
const ready = this.#sessionReady.get(sessionId);
|
|
22349
|
+
if (ready)
|
|
22350
|
+
return ready;
|
|
22351
|
+
const result = new Promise((resolve, reject) => this.#sessionWaiters.set(sessionId, { resolve, reject }));
|
|
22352
|
+
try {
|
|
22353
|
+
return await withTimeout(result, this.timeoutMs, "Hermes gateway agent initialization");
|
|
22354
|
+
} finally {
|
|
22355
|
+
this.#sessionWaiters.delete(sessionId);
|
|
22356
|
+
}
|
|
22357
|
+
}
|
|
22358
|
+
async request(method, params, timeoutMs = this.timeoutMs) {
|
|
22359
|
+
if (this.#closed || !this.#child)
|
|
22360
|
+
throw new Error("Hermes gateway is closed");
|
|
22361
|
+
const id = `codexhost-${++this.#nextId}`;
|
|
22362
|
+
const response = new Promise((resolve, reject) => {
|
|
22363
|
+
this.#pending.set(id, { resolve, reject });
|
|
22364
|
+
this.#send({ jsonrpc: "2.0", id, method, params });
|
|
22365
|
+
});
|
|
22366
|
+
try {
|
|
22367
|
+
return await withTimeout(response, timeoutMs, `Hermes ${method}`);
|
|
22368
|
+
} catch (error51) {
|
|
22369
|
+
if (error51 instanceof HermesTransportError)
|
|
22370
|
+
this.#fault(error51);
|
|
22371
|
+
throw error51;
|
|
22372
|
+
} finally {
|
|
22373
|
+
this.#pending.delete(id);
|
|
22374
|
+
}
|
|
22375
|
+
}
|
|
22376
|
+
respond(id, result) {
|
|
22377
|
+
this.#send({ jsonrpc: "2.0", id, result });
|
|
22378
|
+
}
|
|
22379
|
+
rejectRequest(id) {
|
|
22380
|
+
this.#send({
|
|
22381
|
+
jsonrpc: "2.0",
|
|
22382
|
+
id,
|
|
22383
|
+
error: { code: -32601, message: "Unsupported Host interaction" }
|
|
22384
|
+
});
|
|
22385
|
+
}
|
|
22386
|
+
#send(frame) {
|
|
22387
|
+
if (!this.#child?.stdin.writable)
|
|
22388
|
+
throw new Error("Hermes gateway input is closed");
|
|
22389
|
+
this.#child.stdin.write(JSON.stringify(frame) + "\n");
|
|
22390
|
+
}
|
|
22391
|
+
#frame(frame) {
|
|
22392
|
+
if (frame.method === "event") {
|
|
22393
|
+
const event = gatewayRecord(frame.params);
|
|
22394
|
+
const sessionId = gatewayString(event.session_id);
|
|
22395
|
+
const payload = gatewayRecord(event.payload);
|
|
22396
|
+
if (event.type === "session.info" && payload.lazy !== true) {
|
|
22397
|
+
this.#sessionReady.set(sessionId, payload);
|
|
22398
|
+
this.#sessionWaiters.get(sessionId)?.resolve(payload);
|
|
22399
|
+
} else if (event.type === "error") {
|
|
22400
|
+
const error51 = new Error(gatewayString(payload.message));
|
|
22401
|
+
this.#sessionErrors.set(sessionId, error51);
|
|
22402
|
+
this.#sessionWaiters.get(sessionId)?.reject(error51);
|
|
22403
|
+
}
|
|
22404
|
+
this.onEvent(event);
|
|
22405
|
+
return;
|
|
22406
|
+
}
|
|
22407
|
+
if (typeof frame.method === "string" && typeof frame.id === "string") {
|
|
22408
|
+
this.onRequest(frame.id, frame.method, gatewayRecord(frame.params));
|
|
22409
|
+
return;
|
|
22410
|
+
}
|
|
22411
|
+
const id = gatewayString(frame.id);
|
|
22412
|
+
const pending = this.#pending.get(id);
|
|
22413
|
+
if (!pending)
|
|
22414
|
+
return;
|
|
22415
|
+
this.#pending.delete(id);
|
|
22416
|
+
if (frame.error)
|
|
22417
|
+
pending.reject(new Error(gatewayString(gatewayRecord(frame.error).message) || "Hermes gateway request failed"));
|
|
22418
|
+
else
|
|
22419
|
+
pending.resolve(gatewayRecord(frame.result));
|
|
22420
|
+
}
|
|
22421
|
+
#fault(error51) {
|
|
22422
|
+
for (const pending of this.#pending.values())
|
|
22423
|
+
pending.reject(error51);
|
|
22424
|
+
for (const waiter of this.#sessionWaiters.values())
|
|
22425
|
+
waiter.reject(error51);
|
|
22426
|
+
this.#sessionWaiters.clear();
|
|
22427
|
+
this.#pending.clear();
|
|
22428
|
+
if (!this.#closed) {
|
|
22429
|
+
this.onFault(error51);
|
|
22430
|
+
void this.close();
|
|
22431
|
+
}
|
|
22432
|
+
}
|
|
22433
|
+
#signal(signal) {
|
|
22434
|
+
const pid = this.#child?.pid;
|
|
22435
|
+
if (!pid)
|
|
22436
|
+
return;
|
|
22437
|
+
if (process.platform === "win32") {
|
|
22438
|
+
spawnSync2("taskkill.exe", ["/pid", String(pid), "/t", "/f"], {
|
|
22439
|
+
stdio: "ignore",
|
|
22440
|
+
windowsHide: true
|
|
22441
|
+
});
|
|
22442
|
+
return;
|
|
22443
|
+
}
|
|
22444
|
+
try {
|
|
22445
|
+
process.kill(-pid, signal);
|
|
22446
|
+
} catch (error51) {
|
|
22447
|
+
if (gatewayRecord(error51).code !== "ESRCH")
|
|
22448
|
+
throw error51;
|
|
22449
|
+
}
|
|
22450
|
+
}
|
|
22451
|
+
close() {
|
|
22452
|
+
return this.#closePromise ??= this.#close().finally(() => this.#delegation?.dispose());
|
|
22453
|
+
}
|
|
22454
|
+
async #close() {
|
|
22455
|
+
if (this.#closed)
|
|
22456
|
+
return;
|
|
22457
|
+
this.#closed = true;
|
|
22458
|
+
this.#fault(new Error("Hermes gateway closed"));
|
|
22459
|
+
const child = this.#child;
|
|
22460
|
+
if (!child)
|
|
22461
|
+
return;
|
|
22462
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
22463
|
+
this.#signal("SIGKILL");
|
|
22464
|
+
return;
|
|
22465
|
+
}
|
|
22466
|
+
const exit = this.#processClosed;
|
|
22467
|
+
child.stdin.end();
|
|
22468
|
+
try {
|
|
22469
|
+
await withTimeout(exit, 2e3, "Hermes gateway close");
|
|
22470
|
+
} catch {
|
|
22471
|
+
this.#signal("SIGTERM");
|
|
22472
|
+
try {
|
|
22473
|
+
await withTimeout(exit, 2e3, "Hermes gateway exit");
|
|
22474
|
+
} catch {
|
|
22475
|
+
this.#signal("SIGKILL");
|
|
22476
|
+
await withTimeout(exit, 2e3, "Hermes gateway killed").catch(() => void 0);
|
|
22477
|
+
}
|
|
22478
|
+
}
|
|
22479
|
+
}
|
|
22480
|
+
};
|
|
22481
|
+
|
|
22482
|
+
// dist/gateway-history.js
|
|
22483
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
22484
|
+
|
|
22485
|
+
// dist/gateway-history-script.js
|
|
22486
|
+
var GATEWAY_HISTORY_SCRIPT = String.raw`
|
|
22487
|
+
import sys
|
|
22488
|
+
output = sys.stdout
|
|
22489
|
+
sys.stdout = sys.stderr
|
|
22490
|
+
import hashlib, json, sqlite3
|
|
22491
|
+
from pathlib import Path
|
|
22492
|
+
from urllib.parse import quote
|
|
22493
|
+
from tui_gateway.server import _history_to_messages
|
|
22494
|
+
from agent.context_compressor import user_originated_turn_view
|
|
22495
|
+
from hermes_state import SessionDB
|
|
22496
|
+
from hermes_state_ids import new_session_id
|
|
22497
|
+
p = json.load(sys.stdin)
|
|
22498
|
+
sid = p['sessionId']
|
|
22499
|
+
public_sid = sid
|
|
22500
|
+
|
|
22501
|
+
class HistoryFailure(Exception):
|
|
22502
|
+
def __init__(self, code, message):
|
|
22503
|
+
super().__init__(message)
|
|
22504
|
+
self.code = code
|
|
22505
|
+
|
|
22506
|
+
def digest(rows):
|
|
22507
|
+
return hashlib.sha256(json.dumps(rows, sort_keys=True, ensure_ascii=True, separators=(',', ':')).encode()).hexdigest()
|
|
22508
|
+
|
|
22509
|
+
def config(value):
|
|
22510
|
+
return json.loads(value) if isinstance(value, str) else dict(value or {})
|
|
22511
|
+
|
|
22512
|
+
def user_text(row):
|
|
22513
|
+
view = user_originated_turn_view(row)
|
|
22514
|
+
if view is None: return None
|
|
22515
|
+
# Use the original display kind for native /steer and skill rendering; the
|
|
22516
|
+
# origin predicate above separately excludes synthetic continuation turns.
|
|
22517
|
+
projected = _history_to_messages([row])
|
|
22518
|
+
if not projected: return None
|
|
22519
|
+
return projected[0].get('text')
|
|
22520
|
+
|
|
22521
|
+
def capture(db):
|
|
22522
|
+
exported = db.export_session(sid)
|
|
22523
|
+
if exported is None:
|
|
22524
|
+
raise RuntimeError('Hermes session is not persisted')
|
|
22525
|
+
active = exported['messages']
|
|
22526
|
+
lineage = db.get_compression_lineage(sid)
|
|
22527
|
+
if not lineage: raise RuntimeError('Hermes session lineage is missing')
|
|
22528
|
+
_, display = db.get_resume_conversations(sid)
|
|
22529
|
+
# get_resume_conversations preserves tool relations but sanitizes display text.
|
|
22530
|
+
# Read the same native rows verbatim and native display identities through a
|
|
22531
|
+
# separate mode=ro connection; MIN(id) preserves identity across tail cloning.
|
|
22532
|
+
uri = 'file:' + quote(str(Path(db.db_path).resolve()), safe='/') + '?mode=ro'
|
|
22533
|
+
with sqlite3.connect(uri, uri=True) as conn:
|
|
22534
|
+
conn.row_factory = sqlite3.Row
|
|
22535
|
+
rows = []
|
|
22536
|
+
native_rows = {}
|
|
22537
|
+
for msg in display:
|
|
22538
|
+
row = conn.execute('SELECT * FROM messages WHERE id = ?', (msg['_row_id'],)).fetchone()
|
|
22539
|
+
if row is None:
|
|
22540
|
+
raise RuntimeError('Hermes history changed while reading')
|
|
22541
|
+
owner = row['session_id']
|
|
22542
|
+
if owner not in native_rows:
|
|
22543
|
+
native_rows[owner] = {r['id']: r for r in db.get_messages(owner, include_inactive=True)}
|
|
22544
|
+
raw = dict(native_rows[owner][row['id']])
|
|
22545
|
+
original = row['id']
|
|
22546
|
+
if row['display_identity'] is not None:
|
|
22547
|
+
original = conn.execute('SELECT MIN(id) FROM messages WHERE session_id IN (' + ','.join('?' for _ in lineage) + ') AND display_identity = ?',
|
|
22548
|
+
(*lineage, row['display_identity'])).fetchone()[0] or row['id']
|
|
22549
|
+
raw['original_id'] = original
|
|
22550
|
+
if raw['role'] == 'user': raw['user_text'] = user_text(raw)
|
|
22551
|
+
elif raw['role'] == 'assistant':
|
|
22552
|
+
visible = _history_to_messages([raw])
|
|
22553
|
+
raw['display_text'] = visible[0].get('text', '') if visible else ''
|
|
22554
|
+
raw['display_visible'] = bool(visible)
|
|
22555
|
+
rows.append(raw)
|
|
22556
|
+
compressed = conn.execute('SELECT 1 FROM messages WHERE session_id = ? AND (compacted = 1 OR _compressed_summary = 1) LIMIT 1', (sid,)).fetchone() is not None
|
|
22557
|
+
current = db.export_session(sid)
|
|
22558
|
+
if current is None or digest(current['messages']) != digest(active):
|
|
22559
|
+
raise RuntimeError('Hermes history changed while reading')
|
|
22560
|
+
derivable = sid == public_sid and len(lineage) == 1 and not compressed and [r['id'] for r in rows] == [r['id'] for r in active]
|
|
22561
|
+
boundaries = {}
|
|
22562
|
+
if derivable:
|
|
22563
|
+
for i, row in enumerate(active):
|
|
22564
|
+
if user_text(row) is None: continue
|
|
22565
|
+
end = next((j for j in range(i + 1, len(active)) if user_text(active[j]) is not None), len(active))
|
|
22566
|
+
boundaries[str(row['id'])] = {'count': end, 'digest': digest(active[:end])}
|
|
22567
|
+
return exported, {'rows': rows, 'derivable': derivable, 'boundaries': boundaries, 'physicalSessionId': sid}
|
|
22568
|
+
|
|
22569
|
+
try:
|
|
22570
|
+
if p['operation'] == 'ensure':
|
|
22571
|
+
db = SessionDB()
|
|
22572
|
+
try:
|
|
22573
|
+
if db.get_session(sid) is None:
|
|
22574
|
+
from hermes_constants import parse_reasoning_effort
|
|
22575
|
+
initial = {}
|
|
22576
|
+
if isinstance(p.get('provider'), str) and p['provider']: initial['provider'] = p['provider']
|
|
22577
|
+
if isinstance(p.get('model'), str) and p['model']: initial['model'] = p['model']
|
|
22578
|
+
if isinstance(p.get('reasoningEffort'), str):
|
|
22579
|
+
reasoning = parse_reasoning_effort(p['reasoningEffort'])
|
|
22580
|
+
if reasoning is not None: initial['reasoning_config'] = reasoning
|
|
22581
|
+
if isinstance(p.get('yolo'), bool): initial['yolo_mode'] = p['yolo']
|
|
22582
|
+
db.create_session(session_id=sid, source='cli', cwd=p['cwd'], model=p.get('model'), model_config=initial)
|
|
22583
|
+
result = {'created': True}
|
|
22584
|
+
finally: db.close()
|
|
22585
|
+
elif p['operation'] == 'discard':
|
|
22586
|
+
child = p['derivedSessionId']
|
|
22587
|
+
result = {'deleted': False}
|
|
22588
|
+
if child == sid: raise HistoryFailure('invalidState', 'Cannot delete the source Hermes session')
|
|
22589
|
+
db = SessionDB()
|
|
22590
|
+
try:
|
|
22591
|
+
row = db.get_session(child)
|
|
22592
|
+
if (row is not None and row.get('parent_session_id') == sid
|
|
22593
|
+
and config(row.get('model_config')).get('_branched_from') == sid
|
|
22594
|
+
and digest(db.get_messages(child)) == p['expectedDigest']):
|
|
22595
|
+
result['deleted'] = db.delete_session(child, expected_delete_ids=[child])
|
|
22596
|
+
finally: db.close()
|
|
22597
|
+
else:
|
|
22598
|
+
db = SessionDB(read_only=True)
|
|
22599
|
+
try:
|
|
22600
|
+
sid = db.resolve_resume_session_id(public_sid)
|
|
22601
|
+
exported, snapshot = capture(db)
|
|
22602
|
+
finally: db.close()
|
|
22603
|
+
if p['operation'] in ('read', 'resolve'):
|
|
22604
|
+
result = snapshot
|
|
22605
|
+
elif p['operation'] == 'derive':
|
|
22606
|
+
if not snapshot['derivable']:
|
|
22607
|
+
raise HistoryFailure('unsupported', 'Hermes cannot losslessly derive compacted or inherited history')
|
|
22608
|
+
messages = exported['messages']
|
|
22609
|
+
if p.get('rollbackLastTurn'):
|
|
22610
|
+
users = [i for i, row in enumerate(messages) if user_text(row) is not None]
|
|
22611
|
+
if not users: raise HistoryFailure('invalidState', 'Hermes session has no user turn to roll back')
|
|
22612
|
+
messages = messages[:users[-1]]
|
|
22613
|
+
elif p.get('checkpoint'):
|
|
22614
|
+
checkpoint = p['checkpoint']
|
|
22615
|
+
boundary = snapshot['boundaries'].get(checkpoint['checkpointId'])
|
|
22616
|
+
if boundary is None or boundary != checkpoint.get('locator'):
|
|
22617
|
+
raise HistoryFailure('checkpointNotFound', 'Hermes checkpoint no longer matches native history')
|
|
22618
|
+
messages = messages[:boundary['count']]
|
|
22619
|
+
child = new_session_id()
|
|
22620
|
+
model_config = config(exported.get('model_config'))
|
|
22621
|
+
model_config['_branched_from'] = sid
|
|
22622
|
+
payload = {**exported, 'id': child, 'parent_session_id': sid, 'model_config': model_config,
|
|
22623
|
+
'messages': messages, 'title': None, 'ended_at': None, 'end_reason': None, 'archived': False}
|
|
22624
|
+
db = SessionDB()
|
|
22625
|
+
imported = False
|
|
22626
|
+
try:
|
|
22627
|
+
result = db.import_sessions([payload])
|
|
22628
|
+
if result.get('imported_ids') != [child]:
|
|
22629
|
+
raise RuntimeError('Hermes native import did not create the derived session')
|
|
22630
|
+
imported = True
|
|
22631
|
+
# Native import resets ownership. Fill this NEW row only through the
|
|
22632
|
+
# native COALESCE upsert, preserving source profile and working dir.
|
|
22633
|
+
db.create_session(session_id=child, source=exported.get('source') or 'cli',
|
|
22634
|
+
cwd=exported.get('cwd'), profile_name=exported.get('profile_name'), parent_session_id=sid)
|
|
22635
|
+
tools = exported.get('tool_names')
|
|
22636
|
+
if isinstance(tools, str): tools = json.loads(tools)
|
|
22637
|
+
if tools is not None: db.update_session_tool_names(child, tools)
|
|
22638
|
+
copied = db.get_messages(child)
|
|
22639
|
+
def payload_rows(rows):
|
|
22640
|
+
ignored = {'id', 'session_id', 'active', 'compacted', '_row_id'}
|
|
22641
|
+
result = []
|
|
22642
|
+
for row in rows:
|
|
22643
|
+
row = {k: v for k, v in row.items() if k not in ignored}
|
|
22644
|
+
for key in ('reasoning_details', 'codex_reasoning_items', 'codex_message_items'):
|
|
22645
|
+
if isinstance(row.get(key), str): row[key] = json.loads(row[key])
|
|
22646
|
+
result.append(row)
|
|
22647
|
+
return result
|
|
22648
|
+
if payload_rows(copied) != payload_rows(messages):
|
|
22649
|
+
raise RuntimeError('Hermes native import did not preserve the transcript')
|
|
22650
|
+
result = {'sessionId': child, 'digest': digest(copied)}
|
|
22651
|
+
except Exception:
|
|
22652
|
+
if imported: db.delete_session(child, expected_delete_ids=[child])
|
|
22653
|
+
raise
|
|
22654
|
+
finally: db.close()
|
|
22655
|
+
else: raise RuntimeError('Unknown Hermes history operation')
|
|
22656
|
+
except HistoryFailure as exc:
|
|
22657
|
+
result = {'error': {'code': exc.code, 'message': str(exc)}}
|
|
22658
|
+
print(json.dumps(result, ensure_ascii=True), file=output)
|
|
22659
|
+
`;
|
|
22660
|
+
|
|
22661
|
+
// dist/gateway-history.js
|
|
22662
|
+
function record2(value) {
|
|
22663
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
22664
|
+
}
|
|
22665
|
+
function text(value) {
|
|
22666
|
+
if (typeof value === "string")
|
|
22667
|
+
return value;
|
|
22668
|
+
if (Array.isArray(value))
|
|
22669
|
+
return value.map((part) => text(record2(part).text)).join("\n");
|
|
22670
|
+
return "";
|
|
22671
|
+
}
|
|
22672
|
+
function parsed(value) {
|
|
22673
|
+
if (typeof value === "string") {
|
|
22674
|
+
try {
|
|
22675
|
+
return JSON.parse(value);
|
|
22676
|
+
} catch {
|
|
22677
|
+
return value;
|
|
22678
|
+
}
|
|
22679
|
+
}
|
|
22680
|
+
return value ?? null;
|
|
22681
|
+
}
|
|
22682
|
+
function projectGatewayHistory(sessionId, data) {
|
|
22683
|
+
const turns = [];
|
|
22684
|
+
let turn;
|
|
22685
|
+
const tools = /* @__PURE__ */ new Map();
|
|
22686
|
+
for (const row of data.rows) {
|
|
22687
|
+
if (!Number.isSafeInteger(row.id) || !Number.isSafeInteger(row.original_id))
|
|
22688
|
+
throw new Error("Hermes history has no durable message identity");
|
|
22689
|
+
const id = String(row.original_id);
|
|
22690
|
+
if (row.role === "user") {
|
|
22691
|
+
if (typeof row.user_text !== "string")
|
|
22692
|
+
continue;
|
|
22693
|
+
tools.clear();
|
|
22694
|
+
const boundary = data.derivable ? data.boundaries[String(row.id)] : void 0;
|
|
22695
|
+
turn = {
|
|
22696
|
+
nativeTurnRef: nativeTurnRefSchema.parse({
|
|
22697
|
+
harnessId: "hermes",
|
|
22698
|
+
nativeSessionId: sessionId,
|
|
22699
|
+
nativeTurnKey: id,
|
|
22700
|
+
formatVersion: 1
|
|
22701
|
+
}),
|
|
22702
|
+
...boundary ? {
|
|
22703
|
+
checkpoint: nativeCheckpointRefSchema.parse({
|
|
22704
|
+
harnessId: "hermes",
|
|
22705
|
+
nativeSessionId: sessionId,
|
|
22706
|
+
checkpointId: String(row.id),
|
|
22707
|
+
formatVersion: 1,
|
|
22708
|
+
locator: boundary
|
|
22709
|
+
})
|
|
22710
|
+
} : {},
|
|
22711
|
+
input: [{ type: "text", text: row.user_text }],
|
|
22712
|
+
items: [],
|
|
22713
|
+
outcome: {
|
|
22714
|
+
status: "unknown",
|
|
22715
|
+
reason: "Hermes persisted history does not record a turn outcome"
|
|
22716
|
+
},
|
|
22717
|
+
...typeof row.timestamp === "number" ? { startedAtMs: Math.trunc(row.timestamp * 1e3) } : {}
|
|
22718
|
+
};
|
|
22719
|
+
turns.push(turn);
|
|
22720
|
+
continue;
|
|
22721
|
+
}
|
|
22722
|
+
if (!turn)
|
|
22723
|
+
continue;
|
|
22724
|
+
const itemId = (suffix) => hostItemIdSchema.parse(`hermes:${id}:${suffix}`);
|
|
22725
|
+
if (row.role === "assistant") {
|
|
22726
|
+
const reasoning = row.display_visible === false ? "" : text(row.reasoning_content) || text(row.reasoning);
|
|
22727
|
+
if (reasoning)
|
|
22728
|
+
turn.items.push({
|
|
22729
|
+
item: { type: "reasoning", itemId: itemId("reasoning"), text: reasoning },
|
|
22730
|
+
outcome: { status: "succeeded" }
|
|
22731
|
+
});
|
|
22732
|
+
const content = typeof row.display_text === "string" ? row.display_text : text(row.content);
|
|
22733
|
+
if (content)
|
|
22734
|
+
turn.items.push({
|
|
22735
|
+
item: { type: "agentMessage", itemId: itemId("message"), text: content },
|
|
22736
|
+
outcome: { status: "succeeded" }
|
|
22737
|
+
});
|
|
22738
|
+
if (Array.isArray(row.tool_calls))
|
|
22739
|
+
for (const value of row.tool_calls) {
|
|
22740
|
+
const call = record2(value);
|
|
22741
|
+
const fn = record2(call.function);
|
|
22742
|
+
if (typeof call.id !== "string" || typeof fn.name !== "string")
|
|
22743
|
+
continue;
|
|
22744
|
+
tools.set(call.id, {
|
|
22745
|
+
item: {
|
|
22746
|
+
type: "toolExecution",
|
|
22747
|
+
itemId: itemId(`tool:${call.id}`),
|
|
22748
|
+
toolName: fn.name,
|
|
22749
|
+
arguments: parsed(fn.arguments)
|
|
22750
|
+
},
|
|
22751
|
+
outcome: { status: "succeeded" }
|
|
22752
|
+
});
|
|
22753
|
+
}
|
|
22754
|
+
} else if (row.role === "tool" && typeof row.tool_call_id === "string") {
|
|
22755
|
+
const tool = tools.get(row.tool_call_id);
|
|
22756
|
+
if (!tool || tool.item.type !== "toolExecution")
|
|
22757
|
+
continue;
|
|
22758
|
+
const output = typeof row.content === "string" ? row.content : JSON.stringify(row.content ?? "");
|
|
22759
|
+
tool.item.output = { content: [{ type: "text", text: output }] };
|
|
22760
|
+
const result = record2(parsed(row.content));
|
|
22761
|
+
if (result.is_error === true || result.isError === true || result.error)
|
|
22762
|
+
tool.outcome = {
|
|
22763
|
+
status: "failed",
|
|
22764
|
+
error: {
|
|
22765
|
+
code: "nativeFailure",
|
|
22766
|
+
message: typeof result.error === "string" ? result.error : "Hermes tool reported an error",
|
|
22767
|
+
retryable: false
|
|
22768
|
+
}
|
|
22769
|
+
};
|
|
22770
|
+
turn.items.push(tool);
|
|
22771
|
+
tools.delete(row.tool_call_id);
|
|
22772
|
+
}
|
|
22773
|
+
}
|
|
22774
|
+
return { turns };
|
|
22775
|
+
}
|
|
22776
|
+
var HermesGatewayHistoryError = class extends Error {
|
|
22777
|
+
code;
|
|
22778
|
+
constructor(code, message) {
|
|
22779
|
+
super(message);
|
|
22780
|
+
this.code = code;
|
|
22781
|
+
}
|
|
22782
|
+
};
|
|
22783
|
+
var HermesGatewayHistory = class {
|
|
22784
|
+
options;
|
|
22785
|
+
#derived = /* @__PURE__ */ new Map();
|
|
22786
|
+
constructor(options) {
|
|
22787
|
+
this.options = options;
|
|
22788
|
+
}
|
|
22789
|
+
async readSnapshot() {
|
|
22790
|
+
const data = await this.#run({ operation: "read" });
|
|
22791
|
+
if (!Array.isArray(data.rows) || typeof data.derivable !== "boolean" || !data.boundaries)
|
|
22792
|
+
throw new Error("Malformed Hermes native history response");
|
|
22793
|
+
return projectGatewayHistory(this.options.nativeSessionId, data);
|
|
22794
|
+
}
|
|
22795
|
+
async resolvePhysicalSessionId() {
|
|
22796
|
+
const data = await this.#run({ operation: "resolve" });
|
|
22797
|
+
if (typeof data.physicalSessionId !== "string")
|
|
22798
|
+
throw new Error("Missing Hermes storage identity");
|
|
22799
|
+
return data.physicalSessionId;
|
|
22800
|
+
}
|
|
22801
|
+
async ensureCreated(input) {
|
|
22802
|
+
await this.#run({ operation: "ensure", ...input });
|
|
22803
|
+
}
|
|
22804
|
+
async derive(input = {}) {
|
|
22805
|
+
if (input.checkpoint && input.rollbackLastTurn)
|
|
22806
|
+
throw new Error("Conflicting Hermes derivation boundaries");
|
|
22807
|
+
if (input.checkpoint && (input.checkpoint.harnessId !== "hermes" || input.checkpoint.nativeSessionId !== this.options.nativeSessionId))
|
|
22808
|
+
throw new Error("Hermes checkpoint belongs to another session");
|
|
22809
|
+
const data = await this.#run({ operation: "derive", ...input });
|
|
22810
|
+
const ref = nativeSessionRefSchema.parse({
|
|
22811
|
+
harnessId: "hermes",
|
|
22812
|
+
nativeSessionId: data.sessionId,
|
|
22813
|
+
formatVersion: 1,
|
|
22814
|
+
locator: { transport: "gateway" }
|
|
22815
|
+
});
|
|
22816
|
+
if (typeof data.digest !== "string")
|
|
22817
|
+
throw new Error("Missing Hermes derived-history proof");
|
|
22818
|
+
this.#derived.set(ref.nativeSessionId, data.digest);
|
|
22819
|
+
return ref;
|
|
22820
|
+
}
|
|
22821
|
+
async discardDerived(ref) {
|
|
22822
|
+
const expectedDigest = this.#derived.get(ref.nativeSessionId);
|
|
22823
|
+
if (ref.harnessId !== "hermes" || ref.nativeSessionId === this.options.nativeSessionId || !expectedDigest)
|
|
22824
|
+
throw new HermesGatewayHistoryError("invalidRequest", "This history reader did not create the derived Hermes session");
|
|
22825
|
+
const result = await this.#run({
|
|
22826
|
+
operation: "discard",
|
|
22827
|
+
derivedSessionId: ref.nativeSessionId,
|
|
22828
|
+
expectedDigest
|
|
22829
|
+
});
|
|
22830
|
+
if (result.deleted === true)
|
|
22831
|
+
this.#derived.delete(ref.nativeSessionId);
|
|
22832
|
+
return result.deleted === true;
|
|
22833
|
+
}
|
|
22834
|
+
#run(input) {
|
|
22835
|
+
return new Promise((resolve, reject) => {
|
|
22836
|
+
const child = spawn4(this.options.python, ["-I", "-u", "-c", GATEWAY_HISTORY_SCRIPT], {
|
|
22837
|
+
cwd: this.options.cwd,
|
|
22838
|
+
env: { ...process.env, ...this.options.environment },
|
|
22839
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
22840
|
+
windowsHide: true
|
|
22841
|
+
});
|
|
22842
|
+
let stdout = "";
|
|
22843
|
+
const timer = setTimeout(() => {
|
|
22844
|
+
child.kill();
|
|
22845
|
+
reject(new Error("Hermes history operation timed out"));
|
|
22846
|
+
}, 3e4);
|
|
22847
|
+
child.on("error", () => {
|
|
22848
|
+
clearTimeout(timer);
|
|
22849
|
+
reject(new Error("Cannot start Hermes native history reader"));
|
|
22850
|
+
});
|
|
22851
|
+
child.stdout.on("data", (chunk) => {
|
|
22852
|
+
stdout += chunk.toString();
|
|
22853
|
+
if (stdout.length > 32 * 1024 * 1024) {
|
|
22854
|
+
child.kill();
|
|
22855
|
+
reject(new Error("Hermes native history exceeds the supported size"));
|
|
22856
|
+
}
|
|
22857
|
+
});
|
|
22858
|
+
child.stderr.resume();
|
|
22859
|
+
child.stdin.on("error", () => void 0);
|
|
22860
|
+
child.on("close", (code) => {
|
|
22861
|
+
clearTimeout(timer);
|
|
22862
|
+
if (code !== 0)
|
|
22863
|
+
return reject(new Error("Hermes native history operation failed; source history was not modified"));
|
|
22864
|
+
try {
|
|
22865
|
+
const value = record2(JSON.parse(stdout.trim()));
|
|
22866
|
+
const error51 = record2(value.error);
|
|
22867
|
+
if (["unsupported", "checkpointNotFound", "invalidState"].includes(String(error51.code))) {
|
|
22868
|
+
reject(new HermesGatewayHistoryError(error51.code, String(error51.message)));
|
|
22869
|
+
} else
|
|
22870
|
+
resolve(value);
|
|
22871
|
+
} catch {
|
|
22872
|
+
reject(new Error("Malformed Hermes native history response"));
|
|
22873
|
+
}
|
|
22874
|
+
});
|
|
22875
|
+
child.stdin.end(JSON.stringify({ ...input, sessionId: this.options.nativeSessionId }));
|
|
22876
|
+
});
|
|
22877
|
+
}
|
|
22878
|
+
};
|
|
22879
|
+
|
|
22880
|
+
// dist/gateway-session-transport.js
|
|
22881
|
+
var hermesGatewayThinkingOptions = [
|
|
22882
|
+
"none",
|
|
22883
|
+
"minimal",
|
|
22884
|
+
"low",
|
|
22885
|
+
"medium",
|
|
22886
|
+
"high",
|
|
22887
|
+
"xhigh",
|
|
22888
|
+
"max",
|
|
22889
|
+
"ultra"
|
|
22890
|
+
].map((id) => ({ id: harnessThinkingOptionIdSchema.parse(id), label: id }));
|
|
22891
|
+
function question(entry, fallbackId) {
|
|
22892
|
+
const id = gatewayString(entry.qid) || fallbackId;
|
|
22893
|
+
const prompt = gatewayString(entry.question);
|
|
22894
|
+
if (!prompt)
|
|
22895
|
+
throw new Error("Hermes clarify request has no question");
|
|
22896
|
+
const choices = Array.isArray(entry.choices) ? entry.choices.filter((choice) => typeof choice === "string") : [];
|
|
22897
|
+
return choices.length ? {
|
|
22898
|
+
id,
|
|
22899
|
+
type: "choice",
|
|
22900
|
+
prompt,
|
|
22901
|
+
options: choices.map((value) => ({ value, label: value })),
|
|
22902
|
+
multiple: entry.multi_select === true,
|
|
22903
|
+
allowOther: true,
|
|
22904
|
+
optional: false
|
|
22905
|
+
} : { id, type: "text", prompt, multiline: true, secret: false, optional: false };
|
|
22906
|
+
}
|
|
22907
|
+
function gatewayDiff(text2) {
|
|
22908
|
+
if (!text2 || text2.length > 1024 * 1024)
|
|
22909
|
+
return [];
|
|
22910
|
+
const result = [];
|
|
22911
|
+
let path7 = "";
|
|
22912
|
+
let inHunk = false;
|
|
22913
|
+
let oldLines = [], newLines = [];
|
|
22914
|
+
const flush = () => {
|
|
22915
|
+
if (path7)
|
|
22916
|
+
result.push({
|
|
22917
|
+
type: "diff",
|
|
22918
|
+
path: path7,
|
|
22919
|
+
oldText: oldLines.join("\n"),
|
|
22920
|
+
newText: newLines.join("\n")
|
|
22921
|
+
});
|
|
22922
|
+
oldLines = [];
|
|
22923
|
+
newLines = [];
|
|
22924
|
+
};
|
|
22925
|
+
for (const raw of text2.split("\n")) {
|
|
22926
|
+
const line = raw.replace(/\u001b\[[0-9;]*m/gu, "");
|
|
22927
|
+
const header = /^(a\/.+|\/dev\/null) → (b\/.+|\/dev\/null)$/u.exec(line);
|
|
22928
|
+
if (header) {
|
|
22929
|
+
flush();
|
|
22930
|
+
inHunk = false;
|
|
22931
|
+
path7 = (header[2] === "/dev/null" ? header[1] ?? "" : header[2] ?? "").replace(/^[ab]\//u, "");
|
|
22932
|
+
continue;
|
|
22933
|
+
}
|
|
22934
|
+
if (line.startsWith("--- ") && !inHunk) {
|
|
22935
|
+
flush();
|
|
22936
|
+
path7 = line.slice(4).replace(/^a\//u, "");
|
|
22937
|
+
} else if (line.startsWith("+++ ") && !inHunk) {
|
|
22938
|
+
const next = line.slice(4);
|
|
22939
|
+
if (next !== "/dev/null")
|
|
22940
|
+
path7 = next.replace(/^b\//u, "");
|
|
22941
|
+
} else if (line.startsWith("@@")) {
|
|
22942
|
+
inHunk = true;
|
|
22943
|
+
continue;
|
|
22944
|
+
} else if (path7 && line.startsWith("-"))
|
|
22945
|
+
oldLines.push(line.slice(1));
|
|
22946
|
+
else if (path7 && line.startsWith("+"))
|
|
22947
|
+
newLines.push(line.slice(1));
|
|
22948
|
+
else if (path7 && line.startsWith(" ")) {
|
|
22949
|
+
oldLines.push(line.slice(1));
|
|
22950
|
+
newLines.push(line.slice(1));
|
|
22951
|
+
}
|
|
22952
|
+
}
|
|
22953
|
+
flush();
|
|
22954
|
+
return result;
|
|
22955
|
+
}
|
|
22956
|
+
var HermesGatewaySessionTransport = class {
|
|
22957
|
+
transport;
|
|
22958
|
+
sessionId;
|
|
22959
|
+
nativeSessionId;
|
|
22960
|
+
onFault = () => void 0;
|
|
22961
|
+
availableCommands = HERMES_GATEWAY_COMMANDS;
|
|
22962
|
+
history;
|
|
22963
|
+
#active = null;
|
|
22964
|
+
#requests = /* @__PURE__ */ new Map();
|
|
22965
|
+
#closed = false;
|
|
22966
|
+
#info;
|
|
22967
|
+
constructor(transport, sessionId, nativeSessionId, info) {
|
|
22968
|
+
this.transport = transport;
|
|
22969
|
+
this.sessionId = sessionId;
|
|
22970
|
+
this.nativeSessionId = nativeSessionId;
|
|
22971
|
+
this.#info = info;
|
|
22972
|
+
this.history = new HermesGatewayHistory({
|
|
22973
|
+
python: transport.python,
|
|
22974
|
+
cwd: transport.cwd,
|
|
22975
|
+
environment: transport.environment,
|
|
22976
|
+
nativeSessionId
|
|
22977
|
+
});
|
|
22978
|
+
transport.onEvent = (event) => this.#event(event);
|
|
22979
|
+
transport.onRequest = (id, method, params) => {
|
|
22980
|
+
void this.#request(id, method, params).catch((error51) => this.#fault(error51));
|
|
22981
|
+
};
|
|
22982
|
+
transport.onFault = (error51) => this.#fault(error51);
|
|
22983
|
+
}
|
|
22984
|
+
async openResult() {
|
|
22985
|
+
const reasoning = await this.transport.request("config.get", {
|
|
22986
|
+
key: "reasoning",
|
|
22987
|
+
session_id: this.sessionId
|
|
22988
|
+
});
|
|
22989
|
+
const currentThinkingOptionId = gatewayString(reasoning.value);
|
|
22990
|
+
const model = gatewayString(this.#info.model);
|
|
22991
|
+
const provider = gatewayString(this.#info.provider);
|
|
22992
|
+
const currentModelId = provider ? `${provider}:${model}` : model;
|
|
22993
|
+
return {
|
|
22994
|
+
initialize: { protocolVersion: 1 },
|
|
22995
|
+
sessionId: this.nativeSessionId,
|
|
22996
|
+
replay: [],
|
|
22997
|
+
session: {
|
|
22998
|
+
sessionId: this.nativeSessionId,
|
|
22999
|
+
models: currentModelId ? {
|
|
23000
|
+
currentModelId,
|
|
23001
|
+
availableModels: [
|
|
23002
|
+
{ modelId: currentModelId, name: provider ? `${provider} / ${model}` : model }
|
|
23003
|
+
]
|
|
23004
|
+
} : null,
|
|
23005
|
+
modes: {
|
|
23006
|
+
currentModeId: this.#info.yolo === true ? "dont_ask" : "default",
|
|
23007
|
+
availableModes: [
|
|
23008
|
+
{ id: "default", name: "Native approval policy" },
|
|
23009
|
+
{ id: "dont_ask", name: "Session YOLO" }
|
|
23010
|
+
]
|
|
23011
|
+
},
|
|
23012
|
+
thinkingOptions: hermesGatewayThinkingOptions,
|
|
23013
|
+
...hermesGatewayThinkingOptions.some(({ id }) => id === currentThinkingOptionId) ? { currentThinkingOptionId } : {}
|
|
23014
|
+
}
|
|
23015
|
+
};
|
|
23016
|
+
}
|
|
23017
|
+
async readNativeSnapshot() {
|
|
23018
|
+
return this.history.readSnapshot();
|
|
23019
|
+
}
|
|
23020
|
+
async setThinking(optionId) {
|
|
23021
|
+
if (!hermesGatewayThinkingOptions.some(({ id }) => id === optionId))
|
|
23022
|
+
throw new Error("Unsupported Hermes reasoning effort");
|
|
23023
|
+
const result = await this.transport.request("config.set", {
|
|
23024
|
+
key: "reasoning",
|
|
23025
|
+
value: optionId,
|
|
23026
|
+
scope: "session",
|
|
23027
|
+
session_id: this.sessionId
|
|
23028
|
+
});
|
|
23029
|
+
if (result.value !== optionId || (await this.transport.request("config.get", { key: "reasoning", session_id: this.sessionId })).value !== optionId)
|
|
23030
|
+
throw new Error("Hermes did not confirm reasoning selection");
|
|
23031
|
+
return optionId;
|
|
23032
|
+
}
|
|
23033
|
+
async setModel(modelId) {
|
|
23034
|
+
if (/\s/u.test(modelId) || modelId.startsWith("-"))
|
|
23035
|
+
throw new Error("Invalid Hermes Model identifier");
|
|
23036
|
+
const result = await this.transport.request("config.set", {
|
|
23037
|
+
key: "model",
|
|
23038
|
+
value: modelId,
|
|
23039
|
+
scope: "session",
|
|
23040
|
+
session_id: this.sessionId
|
|
23041
|
+
});
|
|
23042
|
+
if (result.confirm_required === true)
|
|
23043
|
+
throw new Error(gatewayString(result.confirm_message) || "Hermes requires confirmation for this Model");
|
|
23044
|
+
const actualModel = gatewayString(this.#info.model);
|
|
23045
|
+
const actualProvider = gatewayString(this.#info.provider);
|
|
23046
|
+
if (modelId !== actualModel && modelId !== `${actualProvider}:${actualModel}`)
|
|
23047
|
+
throw new Error("Hermes did not confirm the requested Model");
|
|
23048
|
+
}
|
|
23049
|
+
async setPermissionMode(modeId) {
|
|
23050
|
+
if (modeId !== "default" && modeId !== "dont_ask")
|
|
23051
|
+
throw new Error("Hermes gateway has no accept_edits mode");
|
|
23052
|
+
const result = await this.transport.request("config.set", {
|
|
23053
|
+
key: "yolo",
|
|
23054
|
+
value: modeId === "dont_ask" ? "on" : "off",
|
|
23055
|
+
scope: "session",
|
|
23056
|
+
session_id: this.sessionId
|
|
23057
|
+
});
|
|
23058
|
+
if (result.value !== (modeId === "dont_ask" ? "1" : "0") || this.#info.yolo !== (modeId === "dont_ask"))
|
|
23059
|
+
throw new Error("Hermes effective approval policy differs from the requested mode; check native global approvals policy");
|
|
23060
|
+
}
|
|
23061
|
+
nativeCommandName(text2) {
|
|
23062
|
+
const commandText = text2.trim();
|
|
23063
|
+
if (/^\/compress(?:\s|$)/iu.test(commandText))
|
|
23064
|
+
return "compress";
|
|
23065
|
+
return /^\/(help|tools|context|version)$/iu.exec(commandText)?.[1]?.toLowerCase() ?? null;
|
|
23066
|
+
}
|
|
23067
|
+
async runTurn(text2, emit, permission, question2) {
|
|
23068
|
+
if (this.#active || this.#closed)
|
|
23069
|
+
throw new Error("Hermes gateway Session is unavailable");
|
|
23070
|
+
let resolveResult = () => void 0;
|
|
23071
|
+
let rejectResult = () => void 0;
|
|
23072
|
+
const result = new Promise((resolve, reject) => {
|
|
23073
|
+
resolveResult = resolve;
|
|
23074
|
+
rejectResult = reject;
|
|
23075
|
+
});
|
|
23076
|
+
void result.catch(() => void 0);
|
|
23077
|
+
const active = {
|
|
23078
|
+
emit,
|
|
23079
|
+
permission,
|
|
23080
|
+
...question2 ? { question: question2 } : {},
|
|
23081
|
+
resolve: resolveResult,
|
|
23082
|
+
reject: rejectResult,
|
|
23083
|
+
streamed: "",
|
|
23084
|
+
reasoning: "",
|
|
23085
|
+
toolIds: /* @__PURE__ */ new Set()
|
|
23086
|
+
};
|
|
23087
|
+
this.#active = active;
|
|
23088
|
+
const commandText = text2.trim();
|
|
23089
|
+
const nativeCommand = this.nativeCommandName(text2);
|
|
23090
|
+
try {
|
|
23091
|
+
if (nativeCommand === "compress") {
|
|
23092
|
+
const compact = await this.transport.request("session.compress", { session_id: this.sessionId, focus_topic: commandText.slice(9).trim() }, 3e5);
|
|
23093
|
+
const summary = gatewayRecord(compact.summary);
|
|
23094
|
+
const explanation = [summary.headline, summary.token_line, summary.note, compact.message].filter((value) => typeof value === "string" && !!value).join("\n");
|
|
23095
|
+
if (explanation)
|
|
23096
|
+
emit({ type: "agent.text", text: explanation });
|
|
23097
|
+
if (compact.status === "pending") {
|
|
23098
|
+
this.#fault(new Error("Hermes compression remains pending; Session closed to prevent overlapping native work"));
|
|
23099
|
+
return await result;
|
|
23100
|
+
}
|
|
23101
|
+
if (!["compressed", "aborted"].includes(String(compact.status)) && compact.lock_held !== true)
|
|
23102
|
+
throw new Error("Hermes returned an unrecognized compression outcome");
|
|
23103
|
+
this.#usage(gatewayRecord(compact.usage));
|
|
23104
|
+
const succeeded = compact.status === "compressed" && summary.noop !== true;
|
|
23105
|
+
active.resolve({
|
|
23106
|
+
stopReason: "end_turn",
|
|
23107
|
+
compactionOutcome: compact.status === "aborted" || summary.aborted === true ? {
|
|
23108
|
+
status: "failed",
|
|
23109
|
+
error: {
|
|
23110
|
+
code: "nativeFailure",
|
|
23111
|
+
message: explanation || "Hermes summary generation failed",
|
|
23112
|
+
retryable: true
|
|
23113
|
+
}
|
|
23114
|
+
} : succeeded ? { status: "succeeded" } : { status: "cancelled", reason: explanation || "Hermes did not compact context" }
|
|
23115
|
+
});
|
|
23116
|
+
} else if (nativeCommand) {
|
|
23117
|
+
const response = await this.transport.request("slash.exec", {
|
|
23118
|
+
session_id: this.sessionId,
|
|
23119
|
+
command: `/${nativeCommand}`
|
|
23120
|
+
});
|
|
23121
|
+
emit({ type: "agent.text", text: gatewayString(response.output) });
|
|
23122
|
+
active.resolve({ stopReason: "end_turn" });
|
|
23123
|
+
} else {
|
|
23124
|
+
const submitted = await this.transport.request("prompt.submit", {
|
|
23125
|
+
session_id: this.sessionId,
|
|
23126
|
+
text: text2
|
|
23127
|
+
});
|
|
23128
|
+
if (submitted.status !== "streaming")
|
|
23129
|
+
this.#fault(new Error(`Hermes did not start an exclusive turn (${String(submitted.status)})`));
|
|
23130
|
+
}
|
|
23131
|
+
} catch (error51) {
|
|
23132
|
+
active.reject(error51 instanceof Error ? error51 : new Error(String(error51)));
|
|
23133
|
+
}
|
|
23134
|
+
try {
|
|
23135
|
+
return await result;
|
|
23136
|
+
} finally {
|
|
23137
|
+
if (this.#active === active)
|
|
23138
|
+
this.#active = null;
|
|
23139
|
+
for (const controller of this.#requests.values())
|
|
23140
|
+
controller.abort("cancelled");
|
|
23141
|
+
this.#requests.clear();
|
|
23142
|
+
}
|
|
23143
|
+
}
|
|
23144
|
+
async cancel() {
|
|
23145
|
+
await this.transport.request("session.interrupt", { session_id: this.sessionId });
|
|
23146
|
+
}
|
|
23147
|
+
async close() {
|
|
23148
|
+
if (this.#closed)
|
|
23149
|
+
return;
|
|
23150
|
+
this.#closed = true;
|
|
23151
|
+
for (const controller of this.#requests.values())
|
|
23152
|
+
controller.abort("cancelled");
|
|
23153
|
+
this.#requests.clear();
|
|
23154
|
+
this.#active?.reject(new Error("Hermes gateway Session closed"));
|
|
23155
|
+
try {
|
|
23156
|
+
await this.transport.request("session.close", { session_id: this.sessionId });
|
|
23157
|
+
} catch {
|
|
23158
|
+
}
|
|
23159
|
+
await this.transport.close();
|
|
23160
|
+
}
|
|
23161
|
+
#fault(error51) {
|
|
23162
|
+
if (this.#closed)
|
|
23163
|
+
return;
|
|
23164
|
+
this.#closed = true;
|
|
23165
|
+
for (const controller of this.#requests.values())
|
|
23166
|
+
controller.abort("cancelled");
|
|
23167
|
+
this.#requests.clear();
|
|
23168
|
+
const fault = new HermesTransportError("protocolError", error51 instanceof Error ? error51.message : String(error51));
|
|
23169
|
+
this.#active?.reject(fault);
|
|
23170
|
+
this.onFault(fault);
|
|
23171
|
+
void this.transport.close();
|
|
23172
|
+
}
|
|
23173
|
+
#event(event) {
|
|
23174
|
+
if (event.session_id !== this.sessionId || this.#closed)
|
|
23175
|
+
return;
|
|
23176
|
+
const payload = gatewayRecord(event.payload);
|
|
23177
|
+
if (event.type === "session.info") {
|
|
23178
|
+
this.#info = payload;
|
|
23179
|
+
return;
|
|
23180
|
+
}
|
|
23181
|
+
if (event.type === "request.cancel") {
|
|
23182
|
+
const controller = this.#requests.get(gatewayString(payload.id));
|
|
23183
|
+
controller?.abort(payload.reason === "timeout" ? "expired" : "cancelled");
|
|
23184
|
+
return;
|
|
23185
|
+
}
|
|
23186
|
+
const active = this.#active;
|
|
23187
|
+
if (!active) {
|
|
23188
|
+
if (event.type === "error")
|
|
23189
|
+
this.#fault(new Error(gatewayString(payload.message)));
|
|
23190
|
+
return;
|
|
23191
|
+
}
|
|
23192
|
+
const text2 = gatewayString(payload.text);
|
|
23193
|
+
if (event.type === "message.delta") {
|
|
23194
|
+
active.streamed += text2;
|
|
23195
|
+
active.emit({ type: "agent.text", text: text2 });
|
|
23196
|
+
} else if (event.type === "reasoning.delta" || event.type === "reasoning.available") {
|
|
23197
|
+
if (event.type === "reasoning.delta" || !active.reasoning) {
|
|
23198
|
+
active.reasoning += text2;
|
|
23199
|
+
active.emit({ type: "agent.thought", text: text2 });
|
|
23200
|
+
}
|
|
23201
|
+
} else if (event.type === "message.interim") {
|
|
23202
|
+
if (payload.already_streamed !== true)
|
|
23203
|
+
active.emit({ type: "agent.text", text: text2 });
|
|
23204
|
+
active.streamed = "";
|
|
23205
|
+
active.reasoning = "";
|
|
23206
|
+
} else if (event.type === "session.usage")
|
|
23207
|
+
this.#usage(gatewayRecord(payload.usage));
|
|
23208
|
+
else if (event.type === "tool.start" || event.type === "tool.complete") {
|
|
23209
|
+
const toolCallId = gatewayString(payload.tool_id);
|
|
23210
|
+
if (!toolCallId)
|
|
23211
|
+
return;
|
|
23212
|
+
const result = gatewayRecord(payload.result);
|
|
23213
|
+
const output = payload.result !== void 0 ? typeof payload.result === "string" ? payload.result : JSON.stringify(payload.result) : gatewayString(payload.result_text) || gatewayString(payload.summary);
|
|
23214
|
+
const content = [
|
|
23215
|
+
...output ? [{ type: "content", content: { type: "text", text: output } }] : [],
|
|
23216
|
+
...gatewayDiff(gatewayString(payload.inline_diff))
|
|
23217
|
+
];
|
|
23218
|
+
if (event.type === "tool.complete" && !active.toolIds.has(toolCallId))
|
|
23219
|
+
active.emit({
|
|
23220
|
+
type: "tool.call",
|
|
23221
|
+
toolCallId,
|
|
23222
|
+
update: {
|
|
23223
|
+
sessionUpdate: "tool_call",
|
|
23224
|
+
toolCallId,
|
|
23225
|
+
title: gatewayString(payload.name),
|
|
23226
|
+
rawInput: payload.args,
|
|
23227
|
+
status: "in_progress"
|
|
23228
|
+
}
|
|
23229
|
+
});
|
|
23230
|
+
active.toolIds.add(toolCallId);
|
|
23231
|
+
active.emit(event.type === "tool.start" ? {
|
|
23232
|
+
type: "tool.call",
|
|
23233
|
+
toolCallId,
|
|
23234
|
+
update: {
|
|
23235
|
+
sessionUpdate: "tool_call",
|
|
23236
|
+
toolCallId,
|
|
23237
|
+
title: gatewayString(payload.name),
|
|
23238
|
+
rawInput: payload.args,
|
|
23239
|
+
status: "in_progress"
|
|
23240
|
+
}
|
|
23241
|
+
} : {
|
|
23242
|
+
type: "tool.update",
|
|
23243
|
+
toolCallId,
|
|
23244
|
+
update: {
|
|
23245
|
+
sessionUpdate: "tool_call_update",
|
|
23246
|
+
toolCallId,
|
|
23247
|
+
status: result.error || result.is_error === true || result.success === false ? "failed" : "completed",
|
|
23248
|
+
content
|
|
23249
|
+
}
|
|
23250
|
+
});
|
|
23251
|
+
} else if (event.type === "message.complete") {
|
|
23252
|
+
if (!active.streamed)
|
|
23253
|
+
active.emit({ type: "agent.text", text: text2 });
|
|
23254
|
+
else if (text2.startsWith(active.streamed) && text2.length > active.streamed.length)
|
|
23255
|
+
active.emit({ type: "agent.text", text: text2.slice(active.streamed.length) });
|
|
23256
|
+
const reasoning = gatewayString(payload.reasoning);
|
|
23257
|
+
if (reasoning && !active.reasoning)
|
|
23258
|
+
active.emit({ type: "agent.thought", text: reasoning });
|
|
23259
|
+
else if (reasoning.startsWith(active.reasoning) && reasoning.length > active.reasoning.length)
|
|
23260
|
+
active.emit({ type: "agent.thought", text: reasoning.slice(active.reasoning.length) });
|
|
23261
|
+
const usage = gatewayRecord(payload.usage);
|
|
23262
|
+
this.#usage(usage);
|
|
23263
|
+
if (payload.status === "error")
|
|
23264
|
+
active.reject(new Error(gatewayString(payload.error) || gatewayString(payload.failure_reason) || "Hermes turn failed"));
|
|
23265
|
+
else
|
|
23266
|
+
active.resolve({
|
|
23267
|
+
stopReason: payload.status === "interrupted" ? "cancelled" : "end_turn",
|
|
23268
|
+
usage: {
|
|
23269
|
+
inputTokens: Number(usage.input) || 0,
|
|
23270
|
+
outputTokens: Number(usage.output) || 0,
|
|
23271
|
+
totalTokens: Number(usage.total) || 0,
|
|
23272
|
+
...typeof usage.reasoning === "number" ? { thoughtTokens: usage.reasoning } : {}
|
|
23273
|
+
}
|
|
23274
|
+
});
|
|
23275
|
+
} else if (event.type === "error")
|
|
23276
|
+
active.reject(new Error(gatewayString(payload.message) || "Hermes gateway failed"));
|
|
23277
|
+
}
|
|
23278
|
+
#usage(usage) {
|
|
23279
|
+
this.#active?.emit({
|
|
23280
|
+
type: "usage",
|
|
23281
|
+
...typeof usage.context_used === "number" ? { used: usage.context_used } : {},
|
|
23282
|
+
...typeof usage.context_max === "number" ? { size: usage.context_max } : {}
|
|
23283
|
+
});
|
|
23284
|
+
}
|
|
23285
|
+
async #request(id, method, params) {
|
|
23286
|
+
const active = this.#active;
|
|
23287
|
+
if (!active || params.session_id !== this.sessionId || !["clarify", "approval"].includes(method)) {
|
|
23288
|
+
this.transport.rejectRequest(id);
|
|
23289
|
+
return;
|
|
23290
|
+
}
|
|
23291
|
+
const controller = new AbortController();
|
|
23292
|
+
this.#requests.set(id, controller);
|
|
23293
|
+
try {
|
|
23294
|
+
if (method === "clarify" && active.question) {
|
|
23295
|
+
const batch = Array.isArray(params.questions);
|
|
23296
|
+
const questions = batch ? params.questions.map((value, index) => question(gatewayRecord(value), String(index))) : [question(params, "answer")];
|
|
23297
|
+
const response = await active.question({
|
|
23298
|
+
title: "Hermes",
|
|
23299
|
+
questions,
|
|
23300
|
+
signal: controller.signal
|
|
23301
|
+
});
|
|
23302
|
+
if (!controller.signal.aborted) {
|
|
23303
|
+
const answers = Object.fromEntries(Object.entries(response.answers).map(([key, values]) => {
|
|
23304
|
+
const q = questions.find((entry) => entry.id === key);
|
|
23305
|
+
return [
|
|
23306
|
+
key,
|
|
23307
|
+
q?.type === "choice" && q.multiple ? JSON.stringify(values) : values[0] ?? ""
|
|
23308
|
+
];
|
|
23309
|
+
}));
|
|
23310
|
+
this.transport.respond(id, response.cancelled ? {} : batch ? { answers } : { answer: answers.answer ?? "" });
|
|
23311
|
+
}
|
|
23312
|
+
} else if (method === "approval") {
|
|
23313
|
+
const choices = Array.isArray(params.choices) ? params.choices : ["once", "deny"];
|
|
23314
|
+
const options = choices.flatMap((choice) => choice === "once" ? [{ optionId: "once", kind: "allow_once", name: "Allow once" }] : choice === "session" || choice === "always" ? [
|
|
23315
|
+
{
|
|
23316
|
+
optionId: choice,
|
|
23317
|
+
kind: "allow_always",
|
|
23318
|
+
name: choice === "session" ? "Allow for this session" : "Always allow"
|
|
23319
|
+
}
|
|
23320
|
+
] : choice === "deny" ? [{ optionId: "deny", kind: "reject_once", name: "Deny" }] : []);
|
|
23321
|
+
const response = await active.permission({
|
|
23322
|
+
signal: controller.signal,
|
|
23323
|
+
effects: { session: "allowForSession", always: "allowAlways" },
|
|
23324
|
+
description: gatewayString(params.command) || gatewayString(params.description),
|
|
23325
|
+
request: {
|
|
23326
|
+
sessionId: this.nativeSessionId,
|
|
23327
|
+
toolCall: {
|
|
23328
|
+
toolCallId: gatewayString(params.request_id),
|
|
23329
|
+
title: gatewayString(params.description) || gatewayString(params.command)
|
|
23330
|
+
},
|
|
23331
|
+
options
|
|
23332
|
+
},
|
|
23333
|
+
options
|
|
23334
|
+
});
|
|
23335
|
+
if (!controller.signal.aborted)
|
|
23336
|
+
this.transport.respond(id, {
|
|
23337
|
+
choice: response.outcome.outcome === "selected" ? response.outcome.optionId : "deny"
|
|
23338
|
+
});
|
|
23339
|
+
} else
|
|
23340
|
+
this.transport.rejectRequest(id);
|
|
23341
|
+
} finally {
|
|
23342
|
+
this.#requests.delete(id);
|
|
23343
|
+
}
|
|
23344
|
+
}
|
|
23345
|
+
};
|
|
23346
|
+
|
|
23347
|
+
// dist/gateway-open.js
|
|
23348
|
+
import path6 from "node:path";
|
|
23349
|
+
import { realpath } from "node:fs/promises";
|
|
23350
|
+
|
|
23351
|
+
// dist/gateway-configuration.js
|
|
23352
|
+
import { execFile } from "node:child_process";
|
|
23353
|
+
import { promisify } from "node:util";
|
|
23354
|
+
async function resolveGatewayModel(transport, modelId) {
|
|
23355
|
+
if (/\s/u.test(modelId) || modelId.startsWith("-"))
|
|
23356
|
+
throw new Error("Invalid Hermes Model identifier");
|
|
23357
|
+
const result = await promisify(execFile)(transport.python, [
|
|
23358
|
+
"-I",
|
|
23359
|
+
"-c",
|
|
23360
|
+
"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}))",
|
|
23361
|
+
modelId
|
|
23362
|
+
], {
|
|
23363
|
+
cwd: transport.cwd,
|
|
23364
|
+
env: { ...process.env, ...transport.environment },
|
|
23365
|
+
timeout: 2e4,
|
|
23366
|
+
maxBuffer: 1024 * 1024
|
|
23367
|
+
});
|
|
23368
|
+
const value = JSON.parse(result.stdout.trim());
|
|
23369
|
+
if (!value || typeof value !== "object" || !("provider" in value) || !("model" in value) || typeof value.provider !== "string" || typeof value.model !== "string")
|
|
23370
|
+
throw new Error("Hermes returned an invalid Model choice");
|
|
23371
|
+
return { provider: value.provider, model: value.model };
|
|
23372
|
+
}
|
|
23373
|
+
|
|
23374
|
+
// dist/gateway-open.js
|
|
23375
|
+
async function sameDirectory(left, right) {
|
|
23376
|
+
return await realpath(left).catch(() => path6.resolve(left)) === await realpath(right).catch(() => path6.resolve(right));
|
|
23377
|
+
}
|
|
23378
|
+
var gatewayCapabilities = {
|
|
23379
|
+
configuration: {
|
|
23380
|
+
selectModel: true,
|
|
23381
|
+
selectThinkingOption: true,
|
|
23382
|
+
selectPermissionMode: true,
|
|
23383
|
+
permissionModeScope: "live"
|
|
23384
|
+
},
|
|
23385
|
+
history: { fork: true, forkAcrossCwd: false, rollbackLastTurn: true }
|
|
23386
|
+
};
|
|
23387
|
+
function gatewayPermissionModes() {
|
|
23388
|
+
return harnessPermissionModeCatalogSchema.parse({
|
|
23389
|
+
modes: [
|
|
23390
|
+
{
|
|
23391
|
+
id: "default",
|
|
23392
|
+
label: "原生审批策略",
|
|
23393
|
+
description: "遵循 Hermes 全局审批策略,关闭本会话 YOLO。"
|
|
23394
|
+
},
|
|
23395
|
+
{
|
|
23396
|
+
id: "dont_ask",
|
|
23397
|
+
label: "会话 YOLO",
|
|
23398
|
+
description: "本会话自动批准 Hermes 工具操作。",
|
|
23399
|
+
dangerous: true
|
|
23400
|
+
}
|
|
23401
|
+
],
|
|
23402
|
+
defaultModeId: "default"
|
|
23403
|
+
});
|
|
23404
|
+
}
|
|
23405
|
+
function isGatewayRef(ref) {
|
|
23406
|
+
return ref.harnessId === "hermes" && gatewayRecord(ref.locator).transport === "gateway";
|
|
23407
|
+
}
|
|
23408
|
+
async function openGatewaySession(input, transport, onSettle, inheritedPermissionMode) {
|
|
23409
|
+
const source = input.kind === "create" ? null : input.kind === "resume" ? input.nativeRef : input.sourceRef;
|
|
23410
|
+
if (source && !isGatewayRef(source))
|
|
23411
|
+
throw new HermesGatewayHistoryError("unsupported", "This Hermes native reference is not a supported gateway Session");
|
|
23412
|
+
const savedMode = source ? gatewayRecord(source.locator).permissionModeId : void 0;
|
|
23413
|
+
const mode = (input.kind === "fork" ? inheritedPermissionMode : input.permissionModeId) ?? (typeof savedMode === "string" ? savedMode : void 0);
|
|
23414
|
+
if (mode && mode !== "default" && mode !== "dont_ask")
|
|
23415
|
+
throw new HermesGatewayHistoryError("unsupported", "Hermes gateway supports native policy and session YOLO; accept_edits remains ACP-only");
|
|
23416
|
+
if (input.kind === "create" && input.executionPolicy === "unattended-full-access" && mode && mode !== "dont_ask")
|
|
23417
|
+
throw new HermesGatewayHistoryError("invalidRequest", "unattended-full-access requires dont_ask");
|
|
23418
|
+
const model = input.kind !== "fork" && input.model ? decodeHermesModelRefId(input.model.id) : null;
|
|
23419
|
+
if (input.kind !== "fork" && input.model && !model)
|
|
23420
|
+
throw new HermesGatewayHistoryError("invalidRequest", "Model Ref does not belong to Hermes");
|
|
23421
|
+
if (source && gatewayRecord(source.locator).cwd && !await sameDirectory(String(gatewayRecord(source?.locator).cwd), input.cwd))
|
|
23422
|
+
throw new HermesGatewayHistoryError("unsupported", "Hermes gateway preserves the native Session working directory");
|
|
23423
|
+
let nativeRef = source;
|
|
23424
|
+
let sourceHistory;
|
|
23425
|
+
try {
|
|
23426
|
+
await transport.prepareSession();
|
|
23427
|
+
await transport.start();
|
|
23428
|
+
if (source && (input.kind === "fork" || input.kind === "rollbackLastTurn")) {
|
|
23429
|
+
sourceHistory = new HermesGatewayHistory({
|
|
23430
|
+
python: transport.python,
|
|
23431
|
+
cwd: transport.cwd,
|
|
23432
|
+
environment: transport.environment,
|
|
23433
|
+
nativeSessionId: source.nativeSessionId
|
|
23434
|
+
});
|
|
23435
|
+
nativeRef = await sourceHistory.derive(input.kind === "fork" ? { checkpoint: input.checkpoint } : { rollbackLastTurn: true });
|
|
23436
|
+
}
|
|
23437
|
+
const choice = model ? await resolveGatewayModel(transport, model) : null;
|
|
23438
|
+
const response = await transport.request(nativeRef ? "session.resume" : "session.create", nativeRef ? { session_id: nativeRef.nativeSessionId, eager_build: true, omit_messages: true } : {
|
|
23439
|
+
cwd: input.cwd,
|
|
23440
|
+
close_on_disconnect: true,
|
|
23441
|
+
...choice ? choice : {},
|
|
23442
|
+
...input.kind === "create" && input.thinkingOptionId ? { reasoning_effort: input.thinkingOptionId } : {}
|
|
23443
|
+
});
|
|
23444
|
+
const runtimeId = gatewayString(response.session_id);
|
|
23445
|
+
const storedId = nativeRef?.nativeSessionId || gatewayString(response.stored_session_id);
|
|
23446
|
+
if (!runtimeId || !storedId)
|
|
23447
|
+
throw new Error("Hermes gateway did not return Session identity");
|
|
23448
|
+
let info = gatewayRecord(response.info);
|
|
23449
|
+
if (info.lazy === true)
|
|
23450
|
+
info = await transport.waitForSession(runtimeId);
|
|
23451
|
+
const nativeCwd = gatewayString(info.cwd);
|
|
23452
|
+
if (nativeCwd && !await sameDirectory(nativeCwd, input.cwd))
|
|
23453
|
+
throw new HermesGatewayHistoryError("unsupported", "Hermes resumed a different native working directory");
|
|
23454
|
+
const bridge = new HermesGatewaySessionTransport(transport, runtimeId, storedId, info);
|
|
23455
|
+
if (!nativeRef)
|
|
23456
|
+
await bridge.history.ensureCreated({
|
|
23457
|
+
cwd: input.cwd,
|
|
23458
|
+
model: gatewayString(info.model),
|
|
23459
|
+
provider: gatewayString(info.provider),
|
|
23460
|
+
reasoningEffort: gatewayString(info.reasoning_effort)
|
|
23461
|
+
});
|
|
23462
|
+
const physicalId = await bridge.history.resolvePhysicalSessionId();
|
|
23463
|
+
if (gatewayString(info.stored_session_id) !== physicalId)
|
|
23464
|
+
throw new Error("Hermes live Session and persisted history identities differ");
|
|
23465
|
+
if (model)
|
|
23466
|
+
await bridge.setModel(model);
|
|
23467
|
+
if (input.kind !== "fork" && input.thinkingOptionId)
|
|
23468
|
+
await bridge.setThinking(input.thinkingOptionId);
|
|
23469
|
+
const desiredMode = input.kind === "create" && input.executionPolicy === "unattended-full-access" ? "dont_ask" : mode;
|
|
23470
|
+
if (desiredMode)
|
|
23471
|
+
await bridge.setPermissionMode(desiredMode);
|
|
23472
|
+
const open = await bridge.openResult();
|
|
23473
|
+
return new HermesSession({
|
|
23474
|
+
nativeRef: nativeSessionRefSchema.parse({
|
|
23475
|
+
harnessId: "hermes",
|
|
23476
|
+
nativeSessionId: storedId,
|
|
23477
|
+
formatVersion: 1,
|
|
23478
|
+
locator: {
|
|
23479
|
+
transport: "gateway",
|
|
23480
|
+
cwd: input.cwd,
|
|
23481
|
+
permissionModeId: open.session.modes?.currentModeId
|
|
23482
|
+
}
|
|
23483
|
+
}),
|
|
23484
|
+
transport: bridge,
|
|
23485
|
+
open,
|
|
23486
|
+
supportsDerivation: true,
|
|
23487
|
+
onSettle
|
|
23488
|
+
});
|
|
23489
|
+
} catch (error51) {
|
|
23490
|
+
await transport.close().catch(() => void 0);
|
|
23491
|
+
if (sourceHistory && nativeRef)
|
|
23492
|
+
await sourceHistory.discardDerived(nativeRef).catch(() => false);
|
|
23493
|
+
throw error51;
|
|
23494
|
+
}
|
|
23495
|
+
}
|
|
23496
|
+
|
|
21362
23497
|
// dist/hermes-adapter.js
|
|
21363
23498
|
var hermesHarnessId = harnessIdSchema.parse("hermes");
|
|
21364
23499
|
var IMPORT_TIMEOUT_MS = 2e4;
|
|
21365
23500
|
var HERMES_THREAD_ID_ENV = "CODEXHOST_THREAD_ID";
|
|
21366
23501
|
var HermesAdapter = class {
|
|
23502
|
+
commandCatalog = HERMES_COMMAND_CATALOG;
|
|
21367
23503
|
harnessId = hermesHarnessId;
|
|
21368
23504
|
sessionImport = {
|
|
21369
23505
|
listCandidates: () => this.#listImportCandidates(),
|
|
@@ -21371,11 +23507,16 @@ var HermesAdapter = class {
|
|
|
21371
23507
|
};
|
|
21372
23508
|
#options;
|
|
21373
23509
|
#inspectionCache = null;
|
|
23510
|
+
#lastInventory = null;
|
|
23511
|
+
#inventoryRead = null;
|
|
21374
23512
|
#inspectionCacheScope = null;
|
|
21375
23513
|
#sessions = /* @__PURE__ */ new Set();
|
|
21376
23514
|
#warmTransports = /* @__PURE__ */ new Map();
|
|
21377
23515
|
#transports = /* @__PURE__ */ new Set();
|
|
21378
23516
|
#closed = false;
|
|
23517
|
+
#gatewayTransports = /* @__PURE__ */ new Set();
|
|
23518
|
+
#gatewayProbes = /* @__PURE__ */ new Map();
|
|
23519
|
+
#openingNativeIds = /* @__PURE__ */ new Set();
|
|
21379
23520
|
constructor(options = {}) {
|
|
21380
23521
|
this.#options = options;
|
|
21381
23522
|
}
|
|
@@ -21385,6 +23526,29 @@ var HermesAdapter = class {
|
|
|
21385
23526
|
return this.#inspectionCache;
|
|
21386
23527
|
}
|
|
21387
23528
|
const environment = this.#effectiveEnvironment();
|
|
23529
|
+
if (input.refresh)
|
|
23530
|
+
this.#gatewayProbes.clear();
|
|
23531
|
+
const gatewayPython = await this.#gatewayPython(cwd, environment);
|
|
23532
|
+
if (gatewayPython) {
|
|
23533
|
+
try {
|
|
23534
|
+
const catalog = catalogModelsFromInventory(await this.#readInventory());
|
|
23535
|
+
const inspection = {
|
|
23536
|
+
status: "ready",
|
|
23537
|
+
catalog: {
|
|
23538
|
+
models: catalog.models.map(({ ref, label }) => ({ ref, label })),
|
|
23539
|
+
thinkingOptions: hermesGatewayThinkingOptions,
|
|
23540
|
+
...catalog.defaultModel ? { defaultModel: catalog.defaultModel } : {}
|
|
23541
|
+
},
|
|
23542
|
+
permissionModes: gatewayPermissionModes(),
|
|
23543
|
+
capabilities: gatewayCapabilities
|
|
23544
|
+
};
|
|
23545
|
+
this.#inspectionCache = inspection;
|
|
23546
|
+
this.#inspectionCacheScope = cwd;
|
|
23547
|
+
return inspection;
|
|
23548
|
+
} catch (error51) {
|
|
23549
|
+
return inspectionFromTransportError(error51);
|
|
23550
|
+
}
|
|
23551
|
+
}
|
|
21388
23552
|
const transport = await this.#takeTransport(cwd, environment);
|
|
21389
23553
|
let retainedForOpen = false;
|
|
21390
23554
|
try {
|
|
@@ -21429,13 +23593,26 @@ var HermesAdapter = class {
|
|
|
21429
23593
|
}
|
|
21430
23594
|
}
|
|
21431
23595
|
async #readInventory() {
|
|
23596
|
+
if (this.#inventoryRead)
|
|
23597
|
+
return this.#inventoryRead;
|
|
21432
23598
|
const executable = resolveHermesExecutable({
|
|
21433
23599
|
...this.#options.command ? { command: this.#options.command } : {},
|
|
21434
23600
|
...this.#options.environment ? { environment: this.#options.environment } : {}
|
|
21435
23601
|
});
|
|
21436
|
-
|
|
23602
|
+
this.#inventoryRead = readHermesModelInventory(executable, 2e4, {
|
|
21437
23603
|
...this.#options.environment ? { environment: this.#options.environment } : {}
|
|
23604
|
+
}).then((inventory) => {
|
|
23605
|
+
if (!this.#closed)
|
|
23606
|
+
this.#lastInventory = inventory;
|
|
23607
|
+
return inventory;
|
|
23608
|
+
}).catch((error51) => {
|
|
23609
|
+
if (error51 instanceof HermesInventoryTimeoutError && this.#lastInventory)
|
|
23610
|
+
return this.#lastInventory;
|
|
23611
|
+
throw error51;
|
|
23612
|
+
}).finally(() => {
|
|
23613
|
+
this.#inventoryRead = null;
|
|
21438
23614
|
});
|
|
23615
|
+
return this.#inventoryRead;
|
|
21439
23616
|
}
|
|
21440
23617
|
async open(input) {
|
|
21441
23618
|
if (this.#closed) {
|
|
@@ -21445,6 +23622,23 @@ var HermesAdapter = class {
|
|
|
21445
23622
|
if (typeof cwd !== "string" || cwd.trim().length === 0) {
|
|
21446
23623
|
return failure("invalidRequest", "open requires a cwd");
|
|
21447
23624
|
}
|
|
23625
|
+
const nativeRef = input.kind === "create" ? null : input.kind === "resume" ? input.nativeRef : input.sourceRef;
|
|
23626
|
+
const gatewayEnvironment = {
|
|
23627
|
+
...this.#options.environment ?? process.env,
|
|
23628
|
+
...input.environment ?? {}
|
|
23629
|
+
};
|
|
23630
|
+
const environment = this.#effectiveEnvironment(input.environment);
|
|
23631
|
+
if (input.kind === "resume" && (this.#openingNativeIds.has(input.nativeRef.nativeSessionId) || [...this.#sessions].some((s) => s.initialState.nativeRef?.nativeSessionId === input.nativeRef.nativeSessionId)))
|
|
23632
|
+
return failure("sessionBusy", "This Hermes Session already has an owner", true);
|
|
23633
|
+
if (!nativeRef || isGatewayRef(nativeRef)) {
|
|
23634
|
+
const python = await this.#gatewayPython(cwd, gatewayEnvironment);
|
|
23635
|
+
if (this.#closed)
|
|
23636
|
+
return failure("invalidState", "Hermes Adapter is closed");
|
|
23637
|
+
if (python)
|
|
23638
|
+
return this.#openGateway(input, python, gatewayEnvironment);
|
|
23639
|
+
if (nativeRef)
|
|
23640
|
+
return failure("unavailable", "Hermes gateway Session requires an available gateway with exclusive turn support");
|
|
23641
|
+
}
|
|
21448
23642
|
let transportOpen;
|
|
21449
23643
|
let permissionModeId;
|
|
21450
23644
|
if (input.kind === "create") {
|
|
@@ -21468,7 +23662,6 @@ var HermesAdapter = class {
|
|
|
21468
23662
|
if (permissionModeId && !isHermesModeId(permissionModeId)) {
|
|
21469
23663
|
return failure("invalidRequest", "Permission Mode does not belong to Hermes");
|
|
21470
23664
|
}
|
|
21471
|
-
const environment = this.#effectiveEnvironment(input.environment);
|
|
21472
23665
|
const transport = await this.#takeTransport(cwd, environment);
|
|
21473
23666
|
if (this.#closed) {
|
|
21474
23667
|
await this.#releaseTransport(transport);
|
|
@@ -21544,16 +23737,80 @@ var HermesAdapter = class {
|
|
|
21544
23737
|
return failure("nativeFailure", error51 instanceof Error ? error51.message : "Hermes Session open failed");
|
|
21545
23738
|
}
|
|
21546
23739
|
}
|
|
23740
|
+
async #gatewayPython(cwd, environment) {
|
|
23741
|
+
const key = `${cwd}:${this.#transportScope(cwd, environment)}`;
|
|
23742
|
+
let pending = this.#gatewayProbes.get(key);
|
|
23743
|
+
if (!pending) {
|
|
23744
|
+
pending = (async () => {
|
|
23745
|
+
try {
|
|
23746
|
+
return await HermesGatewayTransport.probe(resolveHermesExecutable({
|
|
23747
|
+
...this.#options.command ? { command: this.#options.command } : {},
|
|
23748
|
+
environment
|
|
23749
|
+
}), cwd, environment);
|
|
23750
|
+
} catch {
|
|
23751
|
+
return null;
|
|
23752
|
+
}
|
|
23753
|
+
})();
|
|
23754
|
+
this.#gatewayProbes.set(key, pending);
|
|
23755
|
+
}
|
|
23756
|
+
return pending;
|
|
23757
|
+
}
|
|
23758
|
+
async #openGateway(input, python, environment) {
|
|
23759
|
+
if (this.#closed)
|
|
23760
|
+
return failure("invalidState", "Hermes Adapter is closed");
|
|
23761
|
+
const ref = input.kind === "resume" ? input.nativeRef : null;
|
|
23762
|
+
if (input.kind === "fork" || input.kind === "rollbackLastTurn") {
|
|
23763
|
+
const source = [...this.#sessions].find((s) => s.initialState.nativeRef?.nativeSessionId === input.sourceRef.nativeSessionId);
|
|
23764
|
+
if (source?.busy)
|
|
23765
|
+
return failure("sessionBusy", "Cannot derive a Hermes Session while its source Turn is active", true);
|
|
23766
|
+
}
|
|
23767
|
+
const owned = (id) => this.#openingNativeIds.has(id) || [...this.#sessions].some((session) => session.initialState.nativeRef?.nativeSessionId === id);
|
|
23768
|
+
if (ref && owned(ref.nativeSessionId))
|
|
23769
|
+
return failure("sessionBusy", "This Hermes Session already has an owner", true);
|
|
23770
|
+
if (ref)
|
|
23771
|
+
this.#openingNativeIds.add(ref.nativeSessionId);
|
|
23772
|
+
const transport = new HermesGatewayTransport(python, input.cwd, environment, this.#options.commandTimeoutMs);
|
|
23773
|
+
this.#gatewayTransports.add(transport);
|
|
23774
|
+
try {
|
|
23775
|
+
let inheritedMode;
|
|
23776
|
+
if (input.kind === "fork") {
|
|
23777
|
+
const source = [...this.#sessions].find((s) => s.initialState.nativeRef?.nativeSessionId === input.sourceRef.nativeSessionId);
|
|
23778
|
+
const snapshot = await source?.readSnapshot();
|
|
23779
|
+
if (snapshot?.ok)
|
|
23780
|
+
inheritedMode = snapshot.value.state?.effectivePermissionModeId;
|
|
23781
|
+
}
|
|
23782
|
+
const session = await openGatewaySession(input, transport, (settled) => {
|
|
23783
|
+
this.#sessions.delete(settled);
|
|
23784
|
+
this.#gatewayTransports.delete(transport);
|
|
23785
|
+
}, inheritedMode);
|
|
23786
|
+
if (this.#closed) {
|
|
23787
|
+
await session.close();
|
|
23788
|
+
return failure("invalidState", "Hermes Adapter is closed");
|
|
23789
|
+
}
|
|
23790
|
+
this.#sessions.add(session);
|
|
23791
|
+
return { ok: true, value: session };
|
|
23792
|
+
} catch (error51) {
|
|
23793
|
+
await transport.close().catch(() => void 0);
|
|
23794
|
+
this.#gatewayTransports.delete(transport);
|
|
23795
|
+
return failure(error51 instanceof HermesGatewayHistoryError ? error51.code : "nativeFailure", error51 instanceof Error ? error51.message : "Hermes gateway open failed");
|
|
23796
|
+
} finally {
|
|
23797
|
+
if (ref)
|
|
23798
|
+
this.#openingNativeIds.delete(ref.nativeSessionId);
|
|
23799
|
+
}
|
|
23800
|
+
}
|
|
21547
23801
|
async close() {
|
|
21548
23802
|
if (this.#closed)
|
|
21549
23803
|
return;
|
|
21550
23804
|
this.#closed = true;
|
|
21551
23805
|
this.#inspectionCache = null;
|
|
23806
|
+
this.#lastInventory = null;
|
|
21552
23807
|
const sessions = [...this.#sessions];
|
|
21553
23808
|
this.#sessions.clear();
|
|
21554
23809
|
this.#warmTransports.clear();
|
|
21555
23810
|
await Promise.all(sessions.map((session) => session.close().catch(() => void 0)));
|
|
21556
23811
|
await Promise.all([...this.#transports].map((transport) => this.#releaseTransport(transport)));
|
|
23812
|
+
await Promise.all([...this.#gatewayTransports].map((transport) => transport.close()));
|
|
23813
|
+
this.#gatewayTransports.clear();
|
|
21557
23814
|
}
|
|
21558
23815
|
#effectiveEnvironment(environment) {
|
|
21559
23816
|
const merged = { ...this.#options.environment ?? process.env, ...environment ?? {} };
|