@liberseek/boft-cli-win32-arm64 0.6.9 → 0.7.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.
Files changed (36) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_NOTICES.txt +12 -4
  3. package/app/codexhost-distribution.json +1 -1
  4. package/app/desktop-controller.mjs +287 -170
  5. package/app/host-runtime.mjs +2450 -1044
  6. package/app/plugins/antigravity/plugin.mjs +167 -16
  7. package/app/plugins/claude-code/plugin.mjs +311 -58
  8. package/app/plugins/codebuddy/plugin.mjs +2355 -894
  9. package/app/plugins/cursor-cli/plugin.mjs +35973 -10933
  10. package/app/plugins/deepseek-harness/plugin.mjs +125 -7
  11. package/app/plugins/enabled.json +4 -1
  12. package/app/plugins/grok/plugin.mjs +261 -97
  13. package/app/plugins/hermes/plugin.mjs +2486 -99
  14. package/app/plugins/kiro-cli/plugin.mjs +120 -3
  15. package/app/plugins/muse/plugin.mjs +110 -0
  16. package/app/plugins/omp/plugin.mjs +145 -10
  17. package/app/plugins/opencode/plugin.mjs +162 -46
  18. package/app/plugins/pi/plugin.mjs +2891 -1678
  19. package/app/plugins/qoder/assets/icon.svg +1 -0
  20. package/app/plugins/qoder/manifest.json +12 -0
  21. package/app/plugins/qoder/plugin.mjs +52352 -0
  22. package/app/plugins/qoder-cn/assets/icon.svg +1 -0
  23. package/app/plugins/qoder-cn/manifest.json +12 -0
  24. package/app/plugins/qoder-cn/plugin.mjs +52350 -0
  25. package/app/plugins/workbuddy/assets/icon.svg +29 -0
  26. package/app/plugins/workbuddy/manifest.json +14 -0
  27. package/app/plugins/workbuddy/plugin.mjs +25007 -0
  28. package/app/renderer-extension.js +6043 -1442
  29. package/bin/boft.exe +0 -0
  30. package/libexec/boft-node-repl.exe +0 -0
  31. package/libexec/boft-shim.exe +0 -0
  32. package/libexec/boft-updater.exe +0 -0
  33. package/licenses/Qoder-Agent-SDK-LICENSE.txt +7 -0
  34. package/licenses/QoderCN-Agent-SDK-LICENSE.txt +7 -0
  35. package/licenses/{opencodex-LICENSE.txt → tailwindcss-LICENSE.txt} +1 -1
  36. 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, path5) {
778
- if (!path5)
774
+ function getElementAtPath(obj, path7) {
775
+ if (!path7)
779
776
  return obj;
780
- return path5.reduce((acc, key) => acc?.[key], obj);
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(path5, issues) {
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(path5);
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, path5 = []) => {
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 }, [...path5, ...issue2.path]));
1340
+ issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
1344
1341
  } else if (issue2.code === "invalid_key") {
1345
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1342
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
1346
1343
  } else if (issue2.code === "invalid_element") {
1347
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1344
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
1348
1345
  } else {
1349
- const fullpath = [...path5, ...issue2.path];
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, path5 = []) => {
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 }, [...path5, ...issue2.path]));
1377
+ issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
1381
1378
  } else if (issue2.code === "invalid_key") {
1382
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1379
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
1383
1380
  } else if (issue2.code === "invalid_element") {
1384
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1381
+ processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
1385
1382
  } else {
1386
- const fullpath = [...path5, ...issue2.path];
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 path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1419
- for (const seg of path5) {
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 = (text) => {
7650
- return text.charAt(0).toUpperCase() + text.slice(1);
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 path5 = ref.slice(1).split("/").filter(Boolean);
14112
- if (path5.length === 0) {
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 (path5[0] === defsKey) {
14117
- const key = path5[1];
14113
+ if (path7[0] === defsKey) {
14114
+ const key = path7[1];
14118
14115
  if (!key || !ctx.defs[key]) {
14119
14116
  throw new Error(`Reference not found: ${ref}`);
14120
14117
  }
@@ -14522,9 +14519,104 @@ function date4(params) {
14522
14519
  // ../../../node_modules/zod/v4/classic/external.js
14523
14520
  config(en_default());
14524
14521
 
14522
+ // ../../shared-contracts/dist/credential-imports.js
14523
+ var credentialSourceSchema = external_exports.object({
14524
+ id: external_exports.string().min(1).max(256),
14525
+ harnessId: external_exports.string().min(1),
14526
+ label: external_exports.string().min(1).max(512),
14527
+ provider: external_exports.enum(["openai-codex", "xai"])
14528
+ }).strict();
14529
+ var credentialImportNameSchema = external_exports.string().regex(/^[a-z][a-z0-9-]{0,47}$/);
14530
+ var credentialImportRecordSchema = external_exports.object({
14531
+ name: credentialImportNameSchema,
14532
+ source: credentialSourceSchema,
14533
+ importedAt: external_exports.string()
14534
+ }).strict();
14535
+ var credentialImportsRequestSchema = external_exports.discriminatedUnion("action", [
14536
+ external_exports.object({ action: external_exports.literal("list") }).strict(),
14537
+ external_exports.object({
14538
+ action: external_exports.literal("import"),
14539
+ sourceId: external_exports.string().min(1).max(256),
14540
+ name: credentialImportNameSchema,
14541
+ confirmed: external_exports.literal(true)
14542
+ }).strict(),
14543
+ external_exports.object({
14544
+ action: external_exports.literal("reimport"),
14545
+ sourceId: external_exports.string().min(1).max(256),
14546
+ name: credentialImportNameSchema,
14547
+ confirmed: external_exports.literal(true)
14548
+ }).strict(),
14549
+ external_exports.object({
14550
+ action: external_exports.literal("remove"),
14551
+ name: credentialImportNameSchema,
14552
+ confirmed: external_exports.literal(true)
14553
+ }).strict()
14554
+ ]);
14555
+ var credentialOtherLoginSchema = external_exports.object({
14556
+ provider: external_exports.string().min(1).max(128),
14557
+ type: external_exports.enum(["oauth", "api_key", "unknown"]),
14558
+ /** Best-effort account label (e.g. the email in a Codex token), derived locally when present. */
14559
+ label: external_exports.string().min(1).max(512).optional(),
14560
+ /** Recognized credential vendor, derived from the OAuth token issuer. Absent when unknown. */
14561
+ vendor: external_exports.enum(["openai-codex", "xai"]).optional()
14562
+ }).strict();
14563
+ var credentialImportsResultSchema = external_exports.object({
14564
+ sources: external_exports.array(credentialSourceSchema),
14565
+ targets: external_exports.array(external_exports.object({
14566
+ harnessId: external_exports.string(),
14567
+ providers: external_exports.array(external_exports.enum(["openai-codex", "xai"])),
14568
+ imports: external_exports.array(credentialImportRecordSchema),
14569
+ others: external_exports.array(credentialOtherLoginSchema).default([])
14570
+ }).strict())
14571
+ }).strict();
14572
+ var credentialImportsParamsSchema = external_exports.object({
14573
+ targetHarnessId: external_exports.string().min(1).max(128).optional(),
14574
+ request: credentialImportsRequestSchema
14575
+ }).strict();
14576
+
14525
14577
  // ../../shared-contracts/dist/version.js
14526
14578
  var WORKSPACE_CONTRACT_VERSION = 1;
14527
14579
 
14580
+ // ../../shared-contracts/dist/idle-release.js
14581
+ var IDLE_RELEASE_TIMEOUT_MINUTES_MIN = 5;
14582
+ var IDLE_RELEASE_TIMEOUT_MINUTES_MAX = 1440;
14583
+ var idleReleaseSettingsSchema = external_exports.strictObject({
14584
+ enabled: external_exports.boolean(),
14585
+ timeoutMinutes: external_exports.number().int().min(IDLE_RELEASE_TIMEOUT_MINUTES_MIN).max(IDLE_RELEASE_TIMEOUT_MINUTES_MAX)
14586
+ });
14587
+ var DEFAULT_IDLE_RELEASE_SETTINGS = Object.freeze({
14588
+ enabled: false,
14589
+ timeoutMinutes: 30
14590
+ });
14591
+
14592
+ // ../../shared-contracts/dist/loaded-sessions.js
14593
+ var loadedSessionStateSchema = external_exports.enum([
14594
+ "idle",
14595
+ "running",
14596
+ "busy",
14597
+ "closing",
14598
+ "failed",
14599
+ "blocked"
14600
+ ]);
14601
+ var loadedSessionReasonSchema = external_exports.enum([
14602
+ "none",
14603
+ "disabled",
14604
+ "timeout",
14605
+ "operation",
14606
+ "background",
14607
+ "identity",
14608
+ "persistence",
14609
+ "closeFailed"
14610
+ ]);
14611
+ var loadedSessionsSchema = external_exports.array(external_exports.strictObject({
14612
+ threadId: external_exports.string(),
14613
+ title: external_exports.string(),
14614
+ harnessId: external_exports.string(),
14615
+ state: loadedSessionStateSchema,
14616
+ reason: loadedSessionReasonSchema,
14617
+ inactiveMs: external_exports.number().nonnegative()
14618
+ }));
14619
+
14528
14620
  // ../../shared-contracts/dist/ids.js
14529
14621
  var opaqueIdSchema = external_exports.string().refine((value) => value.trim().length > 0, {
14530
14622
  message: "Identifier must not be empty or whitespace"
@@ -15031,6 +15123,8 @@ var pluginPresentationShape = {
15031
15123
  id: harnessPluginIdSchema,
15032
15124
  name: external_exports.string().trim().min(1).max(128),
15033
15125
  version: external_exports.string().min(1).max(128),
15126
+ /** The factory accepts a persisted local entrypoint through its construction context. */
15127
+ launchCommand: external_exports.literal(true).optional(),
15034
15128
  links: external_exports.object({
15035
15129
  documentation: documentationUrlSchema.optional(),
15036
15130
  installation: documentationUrlSchema.optional()
@@ -15068,6 +15162,19 @@ var harnessPluginRouteSchema = external_exports.object({
15068
15162
  permissionModeId: harnessPermissionModeIdSchema.optional()
15069
15163
  }).strict();
15070
15164
 
15165
+ // ../../shared-contracts/dist/harness-launch-settings.js
15166
+ var harnessLaunchPathSchema = external_exports.string().trim().min(1).max(4096).refine((value) => !/[\u0000\r\n]/u.test(value), "Invalid entrypoint path");
15167
+ var harnessLaunchSettingsGetSchema = external_exports.object({
15168
+ harnessId: harnessPluginIdSchema
15169
+ }).strict();
15170
+ var harnessLaunchSettingsSetSchema = harnessLaunchSettingsGetSchema.extend({
15171
+ path: harnessLaunchPathSchema.nullable()
15172
+ });
15173
+ var harnessLaunchSettingsSchema = external_exports.object({
15174
+ path: harnessLaunchPathSchema.nullable(),
15175
+ restartRequired: external_exports.boolean()
15176
+ }).strict();
15177
+
15071
15178
  // ../../shared-contracts/dist/codex-accounts.js
15072
15179
  var accountIdSchema = external_exports.string().min(1).max(256).regex(/^[A-Za-z0-9._~-]+$/u);
15073
15180
  var nonBlankTextSchema3 = external_exports.string().trim().min(1);
@@ -15301,6 +15408,8 @@ var nativeCheckpointRefV1RuntimeSchema = external_exports.strictObject({
15301
15408
  locator: jsonValueSchema.optional(),
15302
15409
  formatVersion: external_exports.literal(1)
15303
15410
  }).superRefine(rejectExplicitUndefined(["locator"]));
15411
+ var nativeCheckpointRefV1Schema = nativeCheckpointRefV1RuntimeSchema;
15412
+ var nativeCheckpointRefSchema = nativeCheckpointRefV1Schema;
15304
15413
 
15305
15414
  // ../../shared-contracts/dist/updates.js
15306
15415
  var UPDATE_ERROR_MAX_LENGTH = 500;
@@ -15356,6 +15465,62 @@ var updateStatusResultSchema = external_exports.strictObject({
15356
15465
  // ../../shared-contracts/dist/index.js
15357
15466
  var workspaceContractVersionSchema = external_exports.literal(WORKSPACE_CONTRACT_VERSION);
15358
15467
 
15468
+ // dist/hermes-commands.js
15469
+ var SUPPORTED_COMMANDS = /* @__PURE__ */ new Set(["help", "tools", "context", "compress", "version"]);
15470
+ function hermesCommandCatalog(commands) {
15471
+ return harnessCommandCatalogSchema.parse({
15472
+ commands: commands.filter((command) => SUPPORTED_COMMANDS.has(command.name)).map((command) => ({
15473
+ id: `hermes.${command.name}`,
15474
+ invocation: `/${command.name}`,
15475
+ label: `/${command.name}`,
15476
+ description: command.description.slice(0, 512) || `Hermes /${command.name}`,
15477
+ argumentMode: command.input ? "text" : "none"
15478
+ }))
15479
+ });
15480
+ }
15481
+ var HERMES_GATEWAY_COMMANDS = [
15482
+ ...["help", "tools", "context", "version"].map((name) => ({
15483
+ name,
15484
+ description: `Hermes /${name}`
15485
+ })),
15486
+ {
15487
+ name: "compress",
15488
+ description: "Compress conversation context",
15489
+ input: { hint: "Optional compression focus" }
15490
+ }
15491
+ ];
15492
+ var HERMES_COMMAND_CATALOG = hermesCommandCatalog(HERMES_GATEWAY_COMMANDS);
15493
+ function hermesCommandText(command, catalog) {
15494
+ const descriptor = catalog.commands.find((entry) => entry.id === command.commandId);
15495
+ if (!descriptor)
15496
+ return {
15497
+ ok: false,
15498
+ error: {
15499
+ code: "unsupported",
15500
+ message: "Hermes did not advertise this command",
15501
+ retryable: false
15502
+ }
15503
+ };
15504
+ const args = command.arguments ?? {};
15505
+ 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()) {
15506
+ return {
15507
+ ok: false,
15508
+ error: {
15509
+ code: "invalidRequest",
15510
+ message: "Invalid Hermes command arguments",
15511
+ retryable: false
15512
+ }
15513
+ };
15514
+ }
15515
+ return {
15516
+ ok: true,
15517
+ value: `${descriptor.invocation}${typeof args.text === "string" && args.text.trim() ? ` ${args.text.trim()}` : ""}`
15518
+ };
15519
+ }
15520
+
15521
+ // dist/hermes-adapter.js
15522
+ import { createHash } from "node:crypto";
15523
+
15359
15524
  // dist/acp-transport.js
15360
15525
  import { spawn, spawnSync } from "node:child_process";
15361
15526
  import { Readable, Writable } from "node:stream";
@@ -15368,6 +15533,42 @@ function validateHostApprovalResponse(interaction, response) {
15368
15533
  return interaction.actions.some(({ id }) => id === response.actionId) ? null : invalidRequest("Approval Response contains an undeclared action ID");
15369
15534
  }
15370
15535
 
15536
+ // ../../harness-adapter/dist/question.js
15537
+ function invalidRequest2(message) {
15538
+ return { code: "invalidRequest", message, retryable: false };
15539
+ }
15540
+ function validateHostQuestionResponse(interaction, response) {
15541
+ const questionIds = new Set(interaction.questions.map(({ id }) => id));
15542
+ if (response.cancelled) {
15543
+ return Object.keys(response.answers).length === 0 ? null : invalidRequest2("Cancelled Question Response must not contain answers");
15544
+ }
15545
+ for (const answerId of Object.keys(response.answers)) {
15546
+ if (!questionIds.has(answerId)) {
15547
+ return invalidRequest2("Question Response contains an unknown Question ID");
15548
+ }
15549
+ }
15550
+ for (const question2 of interaction.questions) {
15551
+ const answers = response.answers[question2.id] ?? [];
15552
+ if (!question2.optional && answers.length === 0) {
15553
+ return invalidRequest2("Question Response omits a required answer");
15554
+ }
15555
+ if (question2.type === "text") {
15556
+ if (answers.length > 1) {
15557
+ return invalidRequest2("Text Question accepts at most one answer");
15558
+ }
15559
+ continue;
15560
+ }
15561
+ if (!question2.multiple && answers.length > 1) {
15562
+ return invalidRequest2("Single-choice Question accepts at most one answer");
15563
+ }
15564
+ const declared = new Set(question2.options.map(({ value }) => value));
15565
+ if (!question2.allowOther && answers.some((answer) => !declared.has(answer))) {
15566
+ return invalidRequest2("Question Response contains an undeclared choice");
15567
+ }
15568
+ }
15569
+ return null;
15570
+ }
15571
+
15371
15572
  // ../../harness-adapter/dist/output-channel.js
15372
15573
  var HarnessOutputChannel = class {
15373
15574
  outputs;
@@ -19944,8 +20145,8 @@ function classifyStartupError(error51) {
19944
20145
  return new HermesTransportError("notInstalled", error51.message, { cause: error51 });
19945
20146
  }
19946
20147
  const detail = errorDetails2(error51);
19947
- const text = detail.toLowerCase();
19948
- if (text.includes("auth_required") || text.includes("authentication") || text.includes("not configured") || text.includes("no provider") || text.includes("no llm provider")) {
20148
+ const text2 = detail.toLowerCase();
20149
+ if (text2.includes("auth_required") || text2.includes("authentication") || text2.includes("not configured") || text2.includes("no provider") || text2.includes("no llm provider")) {
19949
20150
  return new HermesTransportError("authenticationRequired", detail, {
19950
20151
  cause: error51,
19951
20152
  diagnostic: detail
@@ -20025,6 +20226,10 @@ var HermesAcpTransport = class {
20025
20226
  #replay = null;
20026
20227
  #sessionId = null;
20027
20228
  #stderrTail = "";
20229
+ #availableCommands = [];
20230
+ get availableCommands() {
20231
+ return this.#availableCommands;
20232
+ }
20028
20233
  /** Late binding: the Session registers its fault consumer on construction. */
20029
20234
  onFault = () => void 0;
20030
20235
  constructor(options) {
@@ -20163,7 +20368,7 @@ var HermesAcpTransport = class {
20163
20368
  throw classifyStartupError(error51);
20164
20369
  }
20165
20370
  }
20166
- async runTurn(text, onEvent, onPermission) {
20371
+ async runTurn(text2, onEvent, onPermission) {
20167
20372
  const connection = this.#connection;
20168
20373
  if (!connection || !this.#sessionId || this.#closed || this.#closing) {
20169
20374
  throw new HermesTransportError("unavailable", "Hermes ACP Session is unavailable");
@@ -20175,7 +20380,7 @@ var HermesAcpTransport = class {
20175
20380
  try {
20176
20381
  return await connection.prompt({
20177
20382
  sessionId: this.#sessionId,
20178
- prompt: [{ type: "text", text }]
20383
+ prompt: [{ type: "text", text: text2 }]
20179
20384
  });
20180
20385
  } finally {
20181
20386
  if (this.#activePrompt === active)
@@ -20214,6 +20419,10 @@ var HermesAcpTransport = class {
20214
20419
  #handleUpdate(notification) {
20215
20420
  if (this.#sessionId && notification.sessionId !== this.#sessionId)
20216
20421
  return;
20422
+ if (notification.update.sessionUpdate === "available_commands_update") {
20423
+ this.#availableCommands = notification.update.availableCommands;
20424
+ return;
20425
+ }
20217
20426
  const event = transportEvent(notification.update);
20218
20427
  if (!event)
20219
20428
  return;
@@ -20276,9 +20485,6 @@ var HermesAcpTransport = class {
20276
20485
  clientCapabilities: {},
20277
20486
  clientInfo: { name: "codexhost", version: "0.1.0" }
20278
20487
  }), this.#options.commandTimeoutMs, "Hermes ACP initialize");
20279
- if (initialize.protocolVersion !== PROTOCOL_VERSION) {
20280
- throw new HermesTransportError("protocolError", `Hermes ACP negotiated unsupported protocol version ${initialize.protocolVersion}`);
20281
- }
20282
20488
  this.#initialize = initialize;
20283
20489
  return initialize;
20284
20490
  }
@@ -20417,8 +20623,28 @@ for row in payload.get("providers") or []:
20417
20623
  )
20418
20624
  if slug and model_id:
20419
20625
  available = slug.lower() != "moa" or bool(moa_availability.get(model_id, False))
20626
+ aliases = [
20627
+ alias.strip()
20628
+ for alias in row.get("aliases") or []
20629
+ if isinstance(alias, str) and alias.strip()
20630
+ ]
20631
+ # Hermes names configured custom endpoints as custom:<key>.
20632
+ # The inventory row slug is the bare config key (pi-openai),
20633
+ # which is useful for display but is not a valid native model route.
20634
+ # Prefer the native custom identity for the actual model ref and
20635
+ # retain the bare slug as an alias for matching older snapshots.
20636
+ native_slug = next(
20637
+ (alias for alias in aliases if alias.lower().startswith("custom:")),
20638
+ slug,
20639
+ )
20640
+ native_model_id = native_slug + ":" + model_id
20420
20641
  rows.append({
20421
- "modelId": slug + ":" + model_id,
20642
+ "modelId": native_model_id,
20643
+ "modelIdAliases": [
20644
+ alias + ":" + model_id
20645
+ for alias in aliases
20646
+ if alias + ":" + model_id != native_model_id
20647
+ ],
20422
20648
  "label": model_id,
20423
20649
  "provider": provider,
20424
20650
  "available": available,
@@ -20430,6 +20656,11 @@ print(json.dumps({"models": rows, "currentModelId": current_model_id}))
20430
20656
  `;
20431
20657
  var HermesInventoryError = class extends Error {
20432
20658
  };
20659
+ var HermesInventoryTimeoutError = class extends HermesInventoryError {
20660
+ constructor() {
20661
+ super("Hermes model inventory probe timed out");
20662
+ }
20663
+ };
20433
20664
  async function venvPythonFromShim(hermesExecutable, platform = process.platform) {
20434
20665
  try {
20435
20666
  const shim = await readFile(hermesExecutable, "utf8");
@@ -20468,7 +20699,7 @@ function runProbe(pythonExecutable, timeoutMs, environment) {
20468
20699
  let stderr = "";
20469
20700
  const timer = setTimeout(() => {
20470
20701
  child.kill("SIGKILL");
20471
- reject(new HermesInventoryError("Hermes model inventory probe timed out"));
20702
+ reject(new HermesInventoryTimeoutError());
20472
20703
  }, timeoutMs);
20473
20704
  child.stdout.on("data", (chunk) => {
20474
20705
  stdout += chunk.toString("utf8");
@@ -20487,11 +20718,11 @@ function runProbe(pythonExecutable, timeoutMs, environment) {
20487
20718
  return;
20488
20719
  }
20489
20720
  try {
20490
- const parsed = JSON.parse(stdout.trim());
20491
- const models = (parsed.models ?? []).filter((model) => typeof model?.modelId === "string" && model.modelId.length > 0);
20721
+ const parsed2 = JSON.parse(stdout.trim());
20722
+ const models = (parsed2.models ?? []).filter((model) => typeof model?.modelId === "string" && model.modelId.length > 0);
20492
20723
  resolve({
20493
20724
  models,
20494
- currentModelId: typeof parsed.currentModelId === "string" && parsed.currentModelId.length > 0 ? parsed.currentModelId : null
20725
+ currentModelId: typeof parsed2.currentModelId === "string" && parsed2.currentModelId.length > 0 ? parsed2.currentModelId : null
20495
20726
  });
20496
20727
  } catch {
20497
20728
  reject(new HermesInventoryError("Hermes inventory probe returned malformed output"));
@@ -20525,10 +20756,11 @@ function catalogModelsFromInventory(inventory) {
20525
20756
  for (const model of inventory.models) {
20526
20757
  if (model.available === false)
20527
20758
  continue;
20528
- const ref = encodeHermesModelRef(model.modelId);
20759
+ const nativeModelId = model.modelIdAliases?.find((alias) => alias.toLowerCase().startsWith("custom:")) ?? model.modelId;
20760
+ const ref = encodeHermesModelRef(nativeModelId);
20529
20761
  if (!ref)
20530
20762
  continue;
20531
- if (inventory.currentModelId && model.modelId === inventory.currentModelId) {
20763
+ if (inventory.currentModelId && (model.modelId === inventory.currentModelId || nativeModelId === inventory.currentModelId || model.modelIdAliases?.includes(inventory.currentModelId))) {
20532
20764
  defaultModel = ref;
20533
20765
  }
20534
20766
  models.push({
@@ -20588,7 +20820,558 @@ async function resolveHermesSessionCandidate(input) {
20588
20820
  }
20589
20821
 
20590
20822
  // dist/hermes-session.js
20823
+ import { randomUUID as randomUUID2 } from "node:crypto";
20824
+
20825
+ // dist/hermes-questions.js
20591
20826
  import { randomUUID } from "node:crypto";
20827
+ var HermesQuestions = class {
20828
+ emit;
20829
+ #waiters = /* @__PURE__ */ new Map();
20830
+ constructor(emit) {
20831
+ this.emit = emit;
20832
+ }
20833
+ open(turnId, request) {
20834
+ const interactionId = hostInteractionIdSchema.parse(randomUUID());
20835
+ const interaction = {
20836
+ type: "question",
20837
+ interactionId,
20838
+ turnId,
20839
+ questions: request.questions,
20840
+ ...request.title ? { title: request.title } : {}
20841
+ };
20842
+ const pending = new Promise((resolve) => this.#waiters.set(interactionId, { interaction, resolve }));
20843
+ const abort = () => this.#cancel(interactionId, request.signal?.reason === "expired" ? "expired" : "cancelled");
20844
+ request.signal?.addEventListener("abort", abort, { once: true });
20845
+ this.emit({ kind: "interaction", interaction });
20846
+ if (request.signal?.aborted)
20847
+ abort();
20848
+ return pending.finally(() => request.signal?.removeEventListener("abort", abort));
20849
+ }
20850
+ respond(command) {
20851
+ const waiter = this.#waiters.get(command.interactionId);
20852
+ if (!waiter || command.response.type !== "question")
20853
+ return {
20854
+ ok: false,
20855
+ error: { code: "invalidRequest", message: "No pending Hermes question", retryable: false }
20856
+ };
20857
+ const error51 = validateHostQuestionResponse(waiter.interaction, command.response);
20858
+ if (error51)
20859
+ return { ok: false, error: { ...error51, retryable: false } };
20860
+ this.#waiters.delete(command.interactionId);
20861
+ waiter.resolve(command.response);
20862
+ this.emit({
20863
+ kind: "event",
20864
+ event: {
20865
+ type: "interaction.closed",
20866
+ interactionId: command.interactionId,
20867
+ turnId: waiter.interaction.turnId,
20868
+ reason: command.response.cancelled ? "cancelled" : "responded"
20869
+ }
20870
+ });
20871
+ return { ok: true, value: { accepted: true } };
20872
+ }
20873
+ cancel(turnId) {
20874
+ for (const [id, waiter] of this.#waiters)
20875
+ if (!turnId || waiter.interaction.turnId === turnId)
20876
+ this.#cancel(id, "cancelled");
20877
+ }
20878
+ #cancel(id, reason) {
20879
+ const waiter = this.#waiters.get(id);
20880
+ if (!waiter)
20881
+ return;
20882
+ this.#waiters.delete(id);
20883
+ waiter.resolve({ type: "question", answers: {}, cancelled: true });
20884
+ this.emit({
20885
+ kind: "event",
20886
+ event: {
20887
+ type: "interaction.closed",
20888
+ interactionId: waiter.interaction.interactionId,
20889
+ turnId: waiter.interaction.turnId,
20890
+ reason
20891
+ }
20892
+ });
20893
+ }
20894
+ };
20895
+
20896
+ // ../../../node_modules/diff/libesm/diff/base.js
20897
+ var Diff = class {
20898
+ diff(oldStr, newStr, options = {}) {
20899
+ let callback;
20900
+ if (typeof options === "function") {
20901
+ callback = options;
20902
+ options = {};
20903
+ } else if ("callback" in options) {
20904
+ callback = options.callback;
20905
+ }
20906
+ const oldString = this.castInput(oldStr, options);
20907
+ const newString = this.castInput(newStr, options);
20908
+ const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
20909
+ const newTokens = this.removeEmpty(this.tokenize(newString, options));
20910
+ return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
20911
+ }
20912
+ diffWithOptionsObj(oldTokens, newTokens, options, callback) {
20913
+ var _a3;
20914
+ const done = (value) => {
20915
+ value = this.postProcess(value, options);
20916
+ if (callback) {
20917
+ setTimeout(function() {
20918
+ callback(value);
20919
+ }, 0);
20920
+ return void 0;
20921
+ } else {
20922
+ return value;
20923
+ }
20924
+ };
20925
+ const newLen = newTokens.length, oldLen = oldTokens.length;
20926
+ let editLength = 1;
20927
+ let maxEditLength = newLen + oldLen;
20928
+ if (options.maxEditLength != null) {
20929
+ maxEditLength = Math.min(maxEditLength, options.maxEditLength);
20930
+ }
20931
+ const maxExecutionTime = (_a3 = options.timeout) !== null && _a3 !== void 0 ? _a3 : Infinity;
20932
+ const abortAfterTimestamp = Date.now() + maxExecutionTime;
20933
+ const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
20934
+ let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
20935
+ if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
20936
+ return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
20937
+ }
20938
+ let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
20939
+ const execEditLength = () => {
20940
+ for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
20941
+ let basePath;
20942
+ const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
20943
+ if (removePath) {
20944
+ bestPath[diagonalPath - 1] = void 0;
20945
+ }
20946
+ let canAdd = false;
20947
+ if (addPath) {
20948
+ const addPathNewPos = addPath.oldPos - diagonalPath;
20949
+ canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
20950
+ }
20951
+ const canRemove = removePath && removePath.oldPos + 1 < oldLen;
20952
+ if (!canAdd && !canRemove) {
20953
+ bestPath[diagonalPath] = void 0;
20954
+ continue;
20955
+ }
20956
+ if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
20957
+ basePath = this.addToPath(addPath, true, false, 0, options);
20958
+ } else {
20959
+ basePath = this.addToPath(removePath, false, true, 1, options);
20960
+ }
20961
+ newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
20962
+ if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
20963
+ return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
20964
+ } else {
20965
+ bestPath[diagonalPath] = basePath;
20966
+ if (basePath.oldPos + 1 >= oldLen) {
20967
+ maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
20968
+ }
20969
+ if (newPos + 1 >= newLen) {
20970
+ minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
20971
+ }
20972
+ }
20973
+ }
20974
+ editLength++;
20975
+ };
20976
+ if (callback) {
20977
+ (function exec() {
20978
+ setTimeout(function() {
20979
+ if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
20980
+ return callback(void 0);
20981
+ }
20982
+ if (!execEditLength()) {
20983
+ exec();
20984
+ }
20985
+ }, 0);
20986
+ })();
20987
+ } else {
20988
+ while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
20989
+ const ret = execEditLength();
20990
+ if (ret) {
20991
+ return ret;
20992
+ }
20993
+ }
20994
+ }
20995
+ }
20996
+ addToPath(path7, added, removed, oldPosInc, options) {
20997
+ const last = path7.lastComponent;
20998
+ if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
20999
+ return {
21000
+ oldPos: path7.oldPos + oldPosInc,
21001
+ lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
21002
+ };
21003
+ } else {
21004
+ return {
21005
+ oldPos: path7.oldPos + oldPosInc,
21006
+ lastComponent: { count: 1, added, removed, previousComponent: last }
21007
+ };
21008
+ }
21009
+ }
21010
+ extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
21011
+ const newLen = newTokens.length, oldLen = oldTokens.length;
21012
+ let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
21013
+ while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
21014
+ newPos++;
21015
+ oldPos++;
21016
+ commonCount++;
21017
+ if (options.oneChangePerToken) {
21018
+ basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
21019
+ }
21020
+ }
21021
+ if (commonCount && !options.oneChangePerToken) {
21022
+ basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
21023
+ }
21024
+ basePath.oldPos = oldPos;
21025
+ return newPos;
21026
+ }
21027
+ equals(left, right, options) {
21028
+ if (options.comparator) {
21029
+ return options.comparator(left, right);
21030
+ } else {
21031
+ return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
21032
+ }
21033
+ }
21034
+ removeEmpty(array2) {
21035
+ const ret = [];
21036
+ for (let i = 0; i < array2.length; i++) {
21037
+ if (array2[i]) {
21038
+ ret.push(array2[i]);
21039
+ }
21040
+ }
21041
+ return ret;
21042
+ }
21043
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
21044
+ castInput(value, options) {
21045
+ return value;
21046
+ }
21047
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
21048
+ tokenize(value, options) {
21049
+ return Array.from(value);
21050
+ }
21051
+ join(chars) {
21052
+ return chars.join("");
21053
+ }
21054
+ postProcess(changeObjects, options) {
21055
+ return changeObjects;
21056
+ }
21057
+ get useLongestToken() {
21058
+ return false;
21059
+ }
21060
+ buildValues(lastComponent, newTokens, oldTokens) {
21061
+ const components = [];
21062
+ let nextComponent;
21063
+ while (lastComponent) {
21064
+ components.push(lastComponent);
21065
+ nextComponent = lastComponent.previousComponent;
21066
+ delete lastComponent.previousComponent;
21067
+ lastComponent = nextComponent;
21068
+ }
21069
+ components.reverse();
21070
+ const componentLen = components.length;
21071
+ let componentPos = 0, newPos = 0, oldPos = 0;
21072
+ for (; componentPos < componentLen; componentPos++) {
21073
+ const component = components[componentPos];
21074
+ if (!component.removed) {
21075
+ if (!component.added && this.useLongestToken) {
21076
+ let value = newTokens.slice(newPos, newPos + component.count);
21077
+ value = value.map(function(value2, i) {
21078
+ const oldValue = oldTokens[oldPos + i];
21079
+ return oldValue.length > value2.length ? oldValue : value2;
21080
+ });
21081
+ component.value = this.join(value);
21082
+ } else {
21083
+ component.value = this.join(newTokens.slice(newPos, newPos + component.count));
21084
+ }
21085
+ newPos += component.count;
21086
+ if (!component.added) {
21087
+ oldPos += component.count;
21088
+ }
21089
+ } else {
21090
+ component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
21091
+ oldPos += component.count;
21092
+ }
21093
+ }
21094
+ return components;
21095
+ }
21096
+ };
21097
+
21098
+ // ../../../node_modules/diff/libesm/diff/line.js
21099
+ var LineDiff = class extends Diff {
21100
+ constructor() {
21101
+ super(...arguments);
21102
+ this.tokenize = tokenize;
21103
+ }
21104
+ equals(left, right, options) {
21105
+ if (options.ignoreWhitespace) {
21106
+ if (!options.newlineIsToken || !left.includes("\n")) {
21107
+ left = left.trim();
21108
+ }
21109
+ if (!options.newlineIsToken || !right.includes("\n")) {
21110
+ right = right.trim();
21111
+ }
21112
+ } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
21113
+ if (left.endsWith("\n")) {
21114
+ left = left.slice(0, -1);
21115
+ }
21116
+ if (right.endsWith("\n")) {
21117
+ right = right.slice(0, -1);
21118
+ }
21119
+ }
21120
+ return super.equals(left, right, options);
21121
+ }
21122
+ };
21123
+ var lineDiff = new LineDiff();
21124
+ function diffLines(oldStr, newStr, options) {
21125
+ return lineDiff.diff(oldStr, newStr, options);
21126
+ }
21127
+ function tokenize(value, options) {
21128
+ if (options.stripTrailingCr) {
21129
+ value = value.replace(/\r\n/g, "\n");
21130
+ }
21131
+ const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
21132
+ if (!linesAndNewlines[linesAndNewlines.length - 1]) {
21133
+ linesAndNewlines.pop();
21134
+ }
21135
+ for (let i = 0; i < linesAndNewlines.length; i++) {
21136
+ const line = linesAndNewlines[i];
21137
+ if (i % 2 && !options.newlineIsToken) {
21138
+ retLines[retLines.length - 1] += line;
21139
+ } else {
21140
+ retLines.push(line);
21141
+ }
21142
+ }
21143
+ return retLines;
21144
+ }
21145
+
21146
+ // ../../../node_modules/diff/libesm/patch/create.js
21147
+ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
21148
+ let optionsObj;
21149
+ if (!options) {
21150
+ optionsObj = {};
21151
+ } else if (typeof options === "function") {
21152
+ optionsObj = { callback: options };
21153
+ } else {
21154
+ optionsObj = options;
21155
+ }
21156
+ if (typeof optionsObj.context === "undefined") {
21157
+ optionsObj.context = 4;
21158
+ }
21159
+ const context = optionsObj.context;
21160
+ if (optionsObj.newlineIsToken) {
21161
+ throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
21162
+ }
21163
+ if (!optionsObj.callback) {
21164
+ return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
21165
+ } else {
21166
+ const { callback } = optionsObj;
21167
+ diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
21168
+ const patch = diffLinesResultToPatch(diff);
21169
+ callback(patch);
21170
+ } }));
21171
+ }
21172
+ function diffLinesResultToPatch(diff) {
21173
+ if (!diff) {
21174
+ return;
21175
+ }
21176
+ diff.push({ value: "", lines: [] });
21177
+ function contextLines(lines) {
21178
+ return lines.map(function(entry) {
21179
+ return " " + entry;
21180
+ });
21181
+ }
21182
+ const hunks = [];
21183
+ let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
21184
+ for (let i = 0; i < diff.length; i++) {
21185
+ const current = diff[i], lines = current.lines || splitLines(current.value);
21186
+ current.lines = lines;
21187
+ if (current.added || current.removed) {
21188
+ if (!oldRangeStart) {
21189
+ const prev = diff[i - 1];
21190
+ oldRangeStart = oldLine;
21191
+ newRangeStart = newLine;
21192
+ if (prev) {
21193
+ curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
21194
+ oldRangeStart -= curRange.length;
21195
+ newRangeStart -= curRange.length;
21196
+ }
21197
+ }
21198
+ for (const line of lines) {
21199
+ curRange.push((current.added ? "+" : "-") + line);
21200
+ }
21201
+ if (current.added) {
21202
+ newLine += lines.length;
21203
+ } else {
21204
+ oldLine += lines.length;
21205
+ }
21206
+ } else {
21207
+ if (oldRangeStart) {
21208
+ if (lines.length <= context * 2 && i < diff.length - 2) {
21209
+ for (const line of contextLines(lines)) {
21210
+ curRange.push(line);
21211
+ }
21212
+ } else {
21213
+ const contextSize = Math.min(lines.length, context);
21214
+ for (const line of contextLines(lines.slice(0, contextSize))) {
21215
+ curRange.push(line);
21216
+ }
21217
+ const hunk = {
21218
+ oldStart: oldRangeStart,
21219
+ oldLines: oldLine - oldRangeStart + contextSize,
21220
+ newStart: newRangeStart,
21221
+ newLines: newLine - newRangeStart + contextSize,
21222
+ lines: curRange
21223
+ };
21224
+ hunks.push(hunk);
21225
+ oldRangeStart = 0;
21226
+ newRangeStart = 0;
21227
+ curRange = [];
21228
+ }
21229
+ }
21230
+ oldLine += lines.length;
21231
+ newLine += lines.length;
21232
+ }
21233
+ }
21234
+ for (const hunk of hunks) {
21235
+ for (let i = 0; i < hunk.lines.length; i++) {
21236
+ if (hunk.lines[i].endsWith("\n")) {
21237
+ hunk.lines[i] = hunk.lines[i].slice(0, -1);
21238
+ } else {
21239
+ hunk.lines.splice(i + 1, 0, "\");
21240
+ i++;
21241
+ }
21242
+ }
21243
+ }
21244
+ return {
21245
+ oldFileName,
21246
+ newFileName,
21247
+ oldHeader,
21248
+ newHeader,
21249
+ hunks
21250
+ };
21251
+ }
21252
+ }
21253
+ function formatPatch(patch) {
21254
+ if (Array.isArray(patch)) {
21255
+ return patch.map(formatPatch).join("\n");
21256
+ }
21257
+ const ret = [];
21258
+ if (patch.oldFileName == patch.newFileName) {
21259
+ ret.push("Index: " + patch.oldFileName);
21260
+ }
21261
+ ret.push("===================================================================");
21262
+ ret.push("--- " + patch.oldFileName + (typeof patch.oldHeader === "undefined" ? "" : " " + patch.oldHeader));
21263
+ ret.push("+++ " + patch.newFileName + (typeof patch.newHeader === "undefined" ? "" : " " + patch.newHeader));
21264
+ for (let i = 0; i < patch.hunks.length; i++) {
21265
+ const hunk = patch.hunks[i];
21266
+ if (hunk.oldLines === 0) {
21267
+ hunk.oldStart -= 1;
21268
+ }
21269
+ if (hunk.newLines === 0) {
21270
+ hunk.newStart -= 1;
21271
+ }
21272
+ ret.push("@@ -" + hunk.oldStart + "," + hunk.oldLines + " +" + hunk.newStart + "," + hunk.newLines + " @@");
21273
+ for (const line of hunk.lines) {
21274
+ ret.push(line);
21275
+ }
21276
+ }
21277
+ return ret.join("\n") + "\n";
21278
+ }
21279
+ function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
21280
+ if (typeof options === "function") {
21281
+ options = { callback: options };
21282
+ }
21283
+ if (!(options === null || options === void 0 ? void 0 : options.callback)) {
21284
+ const patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
21285
+ if (!patchObj) {
21286
+ return;
21287
+ }
21288
+ return formatPatch(patchObj);
21289
+ } else {
21290
+ const { callback } = options;
21291
+ structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: (patchObj) => {
21292
+ if (!patchObj) {
21293
+ callback(void 0);
21294
+ } else {
21295
+ callback(formatPatch(patchObj));
21296
+ }
21297
+ } }));
21298
+ }
21299
+ }
21300
+ function splitLines(text2) {
21301
+ const hasTrailingNl = text2.endsWith("\n");
21302
+ const result = text2.split("\n").map((line) => line + "\n");
21303
+ if (hasTrailingNl) {
21304
+ result.pop();
21305
+ } else {
21306
+ result.push(result.pop().slice(0, -1));
21307
+ }
21308
+ return result;
21309
+ }
21310
+
21311
+ // dist/hermes-file-changes.js
21312
+ var MAX_DIFF_BYTES = 1024 * 1024;
21313
+ var MAX_DIFFS = 32;
21314
+ function hermesFileChanges(update) {
21315
+ const changes = [];
21316
+ let bytes = 0;
21317
+ for (const block of update.content ?? []) {
21318
+ if (block.type !== "diff")
21319
+ continue;
21320
+ const { path: path7, oldText, newText } = block;
21321
+ if (!path7.trim() || /[\0\r\n]/u.test(path7) || typeof newText !== "string")
21322
+ continue;
21323
+ if (oldText === newText)
21324
+ continue;
21325
+ bytes += Buffer.byteLength(oldText ?? "") + Buffer.byteLength(newText);
21326
+ if (bytes > MAX_DIFF_BYTES || changes.length === MAX_DIFFS)
21327
+ return [];
21328
+ changes.push({
21329
+ path: path7,
21330
+ kind: "update",
21331
+ diffScope: "fragment",
21332
+ unifiedDiff: createTwoFilesPatch(path7, path7, oldText ?? "", newText, "", "", {
21333
+ context: 3
21334
+ })
21335
+ });
21336
+ }
21337
+ return changes;
21338
+ }
21339
+ function hermesToolOutput(update) {
21340
+ const content = [];
21341
+ for (const block of update.content ?? []) {
21342
+ if (block.type !== "content")
21343
+ continue;
21344
+ if (block.content.type === "text" && block.content.text) {
21345
+ content.push({ type: "text", text: block.content.text });
21346
+ } else if (block.content.type === "image") {
21347
+ content.push({
21348
+ type: "image",
21349
+ mimeType: block.content.mimeType,
21350
+ base64Data: block.content.data
21351
+ });
21352
+ }
21353
+ }
21354
+ if (!content.length && typeof update.rawOutput === "string" && update.rawOutput) {
21355
+ content.push({ type: "text", text: update.rawOutput });
21356
+ }
21357
+ return content.length ? { content } : null;
21358
+ }
21359
+
21360
+ // dist/hermes-compaction.js
21361
+ function hermesCompactionOutcome(outcome, nativeText) {
21362
+ if (outcome.status !== "succeeded")
21363
+ return outcome;
21364
+ const text2 = nativeText.trim();
21365
+ if (/^Context compressed: \d+ -> \d+ messages\n~[\d,]+ -> ~[\d,]+ tokens$/u.test(text2)) {
21366
+ return { status: "succeeded" };
21367
+ }
21368
+ if (/^(?:Compression failed:|Error executing \/compress:|Context compression not available)/u.test(text2)) {
21369
+ return { status: "failed", error: { code: "nativeFailure", message: text2, retryable: false } };
21370
+ }
21371
+ return { status: "cancelled", reason: text2 || "Hermes did not confirm context compression" };
21372
+ }
21373
+
21374
+ // dist/hermes-session.js
20592
21375
  var HOST_ERROR_CODES = {
20593
21376
  notInstalled: "notInstalled",
20594
21377
  authenticationRequired: "authenticationRequired",
@@ -20631,7 +21414,7 @@ function usageFromContext(used, size) {
20631
21414
  ...used !== void 0 ? { contextUsedTokens: used } : {}
20632
21415
  };
20633
21416
  }
20634
- function projectPermissionOptions(options) {
21417
+ function projectPermissionOptions(options, effects) {
20635
21418
  const actionEffects = {
20636
21419
  allow_once: "allowOnce",
20637
21420
  allow_always: "allowAlways",
@@ -20647,7 +21430,7 @@ function projectPermissionOptions(options) {
20647
21430
  const actions = [];
20648
21431
  const optionIdByAction = /* @__PURE__ */ new Map();
20649
21432
  for (const option of options) {
20650
- const effect = actionEffects[option.kind];
21433
+ const effect = effects?.[option.optionId] ?? actionEffects[option.kind];
20651
21434
  if (!effect)
20652
21435
  continue;
20653
21436
  actions.push({
@@ -20663,18 +21446,47 @@ var ActiveTurn = class {
20663
21446
  turnId;
20664
21447
  turnKey;
20665
21448
  input;
21449
+ persistsHistory;
21450
+ compactionItem;
21451
+ compactionText = "";
21452
+ nativeCompactionOutcome;
21453
+ nativeTurnSnapshot;
20666
21454
  #currentText = null;
20667
21455
  #toolItems = /* @__PURE__ */ new Map();
20668
21456
  #finishedItems = [];
21457
+ #toolChanges = /* @__PURE__ */ new Map();
20669
21458
  #emittedTerminalItemIds = /* @__PURE__ */ new Set();
20670
- constructor(turnId, turnKey, input) {
21459
+ rememberFileChanges(toolCallId, update) {
21460
+ const changes = hermesFileChanges(update);
21461
+ if (update.content?.some((block) => block.type === "diff"))
21462
+ this.#toolChanges.set(toolCallId, changes);
21463
+ }
21464
+ completeFileChanges(toolCallId, sourceItemId) {
21465
+ const changes = this.#toolChanges.get(toolCallId);
21466
+ this.#toolChanges.delete(toolCallId);
21467
+ if (!changes?.length)
21468
+ return null;
21469
+ const item = {
21470
+ type: "fileChange",
21471
+ itemId: hostItemIdSchema.parse(randomUUID2()),
21472
+ changes,
21473
+ sourceItemIds: [sourceItemId]
21474
+ };
21475
+ const snapshot = { item, outcome: { status: "succeeded" } };
21476
+ this.#finishedItems.push(snapshot);
21477
+ this.#emittedTerminalItemIds.add(item.itemId);
21478
+ return snapshot;
21479
+ }
21480
+ constructor(turnId, turnKey, input, persistsHistory, compaction) {
20671
21481
  this.turnId = hostTurnIdSchema.parse(turnId);
20672
21482
  this.turnKey = turnKey;
20673
21483
  this.input = input;
21484
+ this.persistsHistory = persistsHistory;
21485
+ this.compactionItem = compaction ? { type: "contextCompaction", itemId: hostItemIdSchema.parse(randomUUID2()) } : null;
20674
21486
  }
20675
- appendText(kind, text) {
21487
+ appendText(kind, text2) {
20676
21488
  if (this.#currentText && this.#currentText.kind === kind) {
20677
- const item2 = { ...this.#currentText.item, text: this.#currentText.item.text + text };
21489
+ const item2 = { ...this.#currentText.item, text: this.#currentText.item.text + text2 };
20678
21490
  this.#currentText = { kind, item: item2 };
20679
21491
  return { startedItem: null, item: item2 };
20680
21492
  }
@@ -20684,8 +21496,8 @@ var ActiveTurn = class {
20684
21496
  outcome: { status: "succeeded" }
20685
21497
  });
20686
21498
  }
20687
- const item = kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(randomUUID()), text: "" } : { type: "agentMessage", itemId: hostItemIdSchema.parse(randomUUID()), text: "" };
20688
- const appended = { ...item, text };
21499
+ const item = kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(randomUUID2()), text: "" } : { type: "agentMessage", itemId: hostItemIdSchema.parse(randomUUID2()), text: "" };
21500
+ const appended = { ...item, text: text2 };
20689
21501
  this.#currentText = { kind, item: appended };
20690
21502
  return { startedItem: item, item: appended };
20691
21503
  }
@@ -20743,6 +21555,13 @@ var ActiveTurn = class {
20743
21555
  }
20744
21556
  this.#toolItems.clear();
20745
21557
  }
21558
+ finishCompaction(outcome) {
21559
+ if (!this.compactionItem)
21560
+ return null;
21561
+ const itemOutcome = outcome.status === "succeeded" && this.nativeCompactionOutcome ? this.nativeCompactionOutcome : hermesCompactionOutcome(outcome, this.compactionText);
21562
+ this.#finishedItems.push({ item: this.compactionItem, outcome: itemOutcome });
21563
+ return itemOutcome;
21564
+ }
20746
21565
  drainPendingItems() {
20747
21566
  const pending = this.#finishedItems.filter((snapshot) => !this.#emittedTerminalItemIds.has(snapshot.item.itemId));
20748
21567
  this.#finishedItems = [];
@@ -20761,25 +21580,30 @@ var ActiveTurn = class {
20761
21580
  function isPlainObjectOrArray(value) {
20762
21581
  return typeof value === "object" && value !== null;
20763
21582
  }
20764
- function toolOutputFromUpdate(update) {
20765
- const content = [];
20766
- if (Array.isArray(update.content)) {
20767
- for (const block of update.content) {
20768
- const candidate = block;
20769
- if (candidate?.type === "text" && typeof candidate.text === "string" && candidate.text.length > 0) {
20770
- content.push({ type: "text", text: candidate.text });
20771
- }
20772
- }
20773
- }
20774
- if (content.length === 0 && typeof update.rawOutput === "string" && update.rawOutput.length > 0) {
20775
- content.push({ type: "text", text: update.rawOutput });
20776
- }
20777
- return content.length > 0 ? { content } : null;
20778
- }
20779
21583
  function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
20780
21584
  const turns = [];
20781
21585
  let current = null;
20782
21586
  let toolItemIndexes = /* @__PURE__ */ new Map();
21587
+ const toolChanges = /* @__PURE__ */ new Map();
21588
+ const appendFileChanges = (toolCallId, update, sourceItemId) => {
21589
+ const changes = hermesFileChanges(update);
21590
+ if (update.content?.some((block) => block.type === "diff"))
21591
+ toolChanges.set(toolCallId, changes);
21592
+ const confirmedChanges = toolChanges.get(toolCallId);
21593
+ if (current && update.status === "completed" && confirmedChanges?.length) {
21594
+ current.items.push({
21595
+ item: {
21596
+ type: "fileChange",
21597
+ itemId: hostItemIdSchema.parse(randomUUID2()),
21598
+ changes: confirmedChanges,
21599
+ sourceItemIds: [sourceItemId]
21600
+ },
21601
+ outcome: { status: "succeeded" }
21602
+ });
21603
+ }
21604
+ if (update.status === "completed" || update.status === "failed")
21605
+ toolChanges.delete(toolCallId);
21606
+ };
20783
21607
  const closeTurn = () => {
20784
21608
  if (!current || current.items.length === 0) {
20785
21609
  current = null;
@@ -20803,21 +21627,22 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
20803
21627
  });
20804
21628
  current = null;
20805
21629
  toolItemIndexes = /* @__PURE__ */ new Map();
21630
+ toolChanges.clear();
20806
21631
  };
20807
- const pushText = (kind, text) => {
21632
+ const pushText = (kind, text2) => {
20808
21633
  if (!current)
20809
21634
  current = { inputText: "(resumed)", items: [], turnKey: `history-${turns.length + 1}` };
20810
21635
  const last = current.items.at(-1);
20811
21636
  if (last && last.item.type === kind) {
20812
21637
  if (kind === "reasoning") {
20813
- last.item.text += text;
21638
+ last.item.text += text2;
20814
21639
  } else {
20815
- last.item.text += text;
21640
+ last.item.text += text2;
20816
21641
  }
20817
21642
  return;
20818
21643
  }
20819
21644
  current.items.push({
20820
- item: kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(randomUUID()), text } : { type: "agentMessage", itemId: hostItemIdSchema.parse(randomUUID()), text },
21645
+ item: kind === "reasoning" ? { type: "reasoning", itemId: hostItemIdSchema.parse(randomUUID2()), text: text2 } : { type: "agentMessage", itemId: hostItemIdSchema.parse(randomUUID2()), text: text2 },
20821
21646
  outcome: { status: "succeeded" }
20822
21647
  });
20823
21648
  };
@@ -20842,12 +21667,12 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
20842
21667
  if (!current)
20843
21668
  current = { inputText: "(resumed)", items: [], turnKey: `history-${turns.length + 1}` };
20844
21669
  const update = event.update;
20845
- const output = toolOutputFromUpdate(update);
21670
+ const output = hermesToolOutput(update);
20846
21671
  const toolName = typeof update.title === "string" && update.title.trim() || typeof update.name === "string" && update.name.trim() || event.toolCallId;
20847
21672
  current.items.push({
20848
21673
  item: {
20849
21674
  type: "toolExecution",
20850
- itemId: hostItemIdSchema.parse(randomUUID()),
21675
+ itemId: hostItemIdSchema.parse(randomUUID2()),
20851
21676
  toolName,
20852
21677
  arguments: isPlainObjectOrArray(update.rawInput) ? update.rawInput : null,
20853
21678
  ...output ? { output } : {}
@@ -20858,6 +21683,9 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
20858
21683
  } : { status: "cancelled", reason: "Turn ended before terminal tool update" }
20859
21684
  });
20860
21685
  toolItemIndexes.set(event.toolCallId, current.items.length - 1);
21686
+ const sourceItemId = current.items.at(-1)?.item.itemId;
21687
+ if (sourceItemId)
21688
+ appendFileChanges(event.toolCallId, update, sourceItemId);
20861
21689
  break;
20862
21690
  }
20863
21691
  case "tool.update": {
@@ -20870,7 +21698,7 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
20870
21698
  if (!prior || prior.item.type !== "toolExecution")
20871
21699
  break;
20872
21700
  const update = event.update;
20873
- const output = toolOutputFromUpdate(update);
21701
+ const output = hermesToolOutput(update);
20874
21702
  current.items[itemIndex] = {
20875
21703
  item: { ...prior.item, ...output ? { output } : {} },
20876
21704
  outcome: update.status === "completed" ? { status: "succeeded" } : update.status === "failed" ? {
@@ -20878,6 +21706,7 @@ function historyTurnsFromReplay(replay, nativeRef, knownTurnRefs = []) {
20878
21706
  error: harnessError("nativeFailure", "Tool execution failed")
20879
21707
  } : prior.outcome
20880
21708
  };
21709
+ appendFileChanges(event.toolCallId, update, prior.item.itemId);
20881
21710
  break;
20882
21711
  }
20883
21712
  default:
@@ -20916,6 +21745,7 @@ var HermesSession = class {
20916
21745
  initialState;
20917
21746
  initialUsage;
20918
21747
  outputs;
21748
+ commands;
20919
21749
  #channel = new HarnessOutputChannel();
20920
21750
  #transport;
20921
21751
  #nativeRef;
@@ -20929,16 +21759,37 @@ var HermesSession = class {
20929
21759
  #completedTurns = [];
20930
21760
  #historyTurns;
20931
21761
  #latestUsage;
21762
+ #questions = new HermesQuestions((output) => this.#channel.emit(output));
20932
21763
  #approvalWaiters = /* @__PURE__ */ new Map();
20933
21764
  constructor(options) {
20934
21765
  this.#transport = options.transport;
21766
+ this.capabilities.history.fork = options.supportsDerivation === true;
21767
+ this.capabilities.history.rollbackLastTurn = options.supportsDerivation === true;
21768
+ this.commands = {
21769
+ list: async () => this.#closed || this.#faulted ? err("invalidState", "Hermes Session is unavailable") : ok(hermesCommandCatalog(this.#transport.availableCommands ?? [])),
21770
+ execute: async (command) => {
21771
+ const text2 = hermesCommandText(command, hermesCommandCatalog(this.#transport.availableCommands ?? []));
21772
+ if (!text2.ok)
21773
+ return text2;
21774
+ return this.execute({
21775
+ type: "turn.start",
21776
+ turnId: command.turnId,
21777
+ input: [{ type: "text", text: text2.value }]
21778
+ });
21779
+ }
21780
+ };
20935
21781
  this.#nativeRef = options.nativeRef;
20936
21782
  this.#onSettle = options.onSettle;
20937
21783
  const projected = projectHermesModelState(options.open.session.models);
20938
21784
  const modes = options.open.session.modes;
20939
21785
  this.#availableModels = options.open.session.models?.availableModels ?? [];
21786
+ this.capabilities.configuration.selectThinkingOption = !!this.#transport.setThinking;
20940
21787
  this.#state = {
20941
21788
  nativeRef: this.#nativeRef,
21789
+ ...options.open.session.thinkingOptions ? { availableThinkingOptions: options.open.session.thinkingOptions } : {},
21790
+ ...options.open.session.currentThinkingOptionId ? {
21791
+ effectiveThinkingOptionId: harnessThinkingOptionIdSchema.parse(options.open.session.currentThinkingOptionId)
21792
+ } : {},
20942
21793
  ...projected.effectiveModel ? { effectiveModel: projected.effectiveModel } : {},
20943
21794
  ...projected.resolvedModelLabel ? { resolvedModelLabel: projected.resolvedModelLabel } : {},
20944
21795
  ...modes ? { effectivePermissionModeId: harnessPermissionModeIdSchema.parse(modes.currentModeId) } : {}
@@ -20950,11 +21801,21 @@ var HermesSession = class {
20950
21801
  this.outputs = this.#channel.outputs;
20951
21802
  this.#transport.onFault = (error51) => this.#fault(transportErrorToHarness(error51));
20952
21803
  }
21804
+ get busy() {
21805
+ return this.#activeTurn !== null;
21806
+ }
20953
21807
  async readSnapshot() {
20954
21808
  if (this.#closed)
20955
21809
  return err("invalidState", "Hermes Session is closed");
20956
- return ok({
20957
- turns: [...this.#historyTurns, ...this.#completedTurns],
21810
+ if (this.#transport.readNativeSnapshot) {
21811
+ try {
21812
+ return ok({ ...await this.#transport.readNativeSnapshot(), state: { ...this.#state } });
21813
+ } catch (error51) {
21814
+ return err("nativeFailure", error51 instanceof Error ? error51.message : String(error51));
21815
+ }
21816
+ }
21817
+ return ok({
21818
+ turns: [...this.#historyTurns, ...this.#completedTurns],
20958
21819
  state: { ...this.#state }
20959
21820
  });
20960
21821
  }
@@ -20968,11 +21829,11 @@ var HermesSession = class {
20968
21829
  case "turn.cancel":
20969
21830
  return this.#cancelTurn(command);
20970
21831
  case "interaction.respond":
20971
- return Promise.resolve(this.#respondToApproval(command));
21832
+ return Promise.resolve(command.response.type === "question" ? this.#questions.respond(command) : this.#respondToApproval(command));
20972
21833
  case "model.select":
20973
21834
  return this.#selectModel(command);
20974
21835
  case "thinking.select":
20975
- return err("unsupported", "Hermes does not expose Thinking options");
21836
+ return this.#selectThinking(command);
20976
21837
  case "permissionMode.select":
20977
21838
  return this.#selectPermissionMode(command);
20978
21839
  default: {
@@ -20986,13 +21847,14 @@ var HermesSession = class {
20986
21847
  return;
20987
21848
  this.#closed = true;
20988
21849
  this.#cancelApprovalWaiters();
20989
- this.#activeTurn = null;
20990
- this.#activeTurnId = null;
21850
+ if (this.#activeTurn)
21851
+ this.#completeActiveTurn(this.#activeTurn, { status: "cancelled", reason: "Session closed" });
20991
21852
  await this.#transport.close().catch(() => void 0);
20992
21853
  this.#channel.end();
20993
21854
  this.#onSettle(this);
20994
21855
  }
20995
21856
  #cancelApprovalWaiters() {
21857
+ this.#questions.cancel();
20996
21858
  for (const [interactionId, waiter] of this.#approvalWaiters) {
20997
21859
  waiter.resolve({ outcome: { outcome: "cancelled" } });
20998
21860
  this.#emit({
@@ -21026,23 +21888,31 @@ var HermesSession = class {
21026
21888
  if (this.#activeTurn) {
21027
21889
  return err("sessionBusy", "Hermes Session already has an active Turn", true);
21028
21890
  }
21029
- const text = command.input.flatMap((chunk) => chunk.type === "text" ? [chunk.text] : []).join("\n");
21030
- if (text.trim().length === 0) {
21891
+ const text2 = command.input.flatMap((chunk) => chunk.type === "text" ? [chunk.text] : []).join("\n");
21892
+ if (text2.trim().length === 0) {
21031
21893
  return err("invalidRequest", "turn.start requires non-empty text input");
21032
21894
  }
21033
- const turnKey = randomUUID();
21034
- const active = new ActiveTurn(command.turnId, turnKey, command.input);
21895
+ const turnKey = randomUUID2();
21896
+ const firstToken = text2.trim().split(/\s/u)[0]?.replace(/^\/+/u, "").toLowerCase();
21897
+ const nativeCommand = this.#transport.nativeCommandName ? this.#transport.nativeCommandName(text2) : text2.trimStart().startsWith("/") && (this.#transport.availableCommands ?? []).some((entry) => entry.name === firstToken) ? firstToken : null;
21898
+ const isNativeCommand = nativeCommand != null;
21899
+ const active = new ActiveTurn(command.turnId, turnKey, command.input, !isNativeCommand, isNativeCommand && nativeCommand === "compress");
21035
21900
  this.#activeTurn = active;
21036
21901
  this.#activeTurnId = active.turnId;
21037
- void this.#runTurn(active, text);
21902
+ void this.#runTurn(active, text2);
21038
21903
  return ok({ turnId: active.turnId });
21039
21904
  }
21040
- async #runTurn(active, text) {
21905
+ async #runTurn(active, text2) {
21041
21906
  this.#emit({ type: "turn.started", turnId: active.turnId });
21907
+ if (active.compactionItem)
21908
+ this.#emit({ type: "item.started", turnId: active.turnId, item: active.compactionItem });
21042
21909
  let promptResponse = null;
21043
21910
  let failure2 = null;
21911
+ let previousNativeTurnKey;
21044
21912
  try {
21045
- promptResponse = await this.#transport.runTurn(text, (event) => this.#handleTransportEvent(active, event), (request) => this.#handlePermissionRequest(active, request));
21913
+ if (active.persistsHistory && this.#transport.readNativeSnapshot)
21914
+ previousNativeTurnKey = (await this.#transport.readNativeSnapshot()).turns.at(-1)?.nativeTurnRef.nativeTurnKey;
21915
+ promptResponse = await this.#transport.runTurn(text2, (event) => this.#handleTransportEvent(active, event), (request) => this.#handlePermissionRequest(active, request), (request) => this.#handleQuestionRequest(active, request));
21046
21916
  } catch (error51) {
21047
21917
  failure2 = error51 instanceof HermesTransportError ? transportErrorToHarness(error51) : harnessError("nativeFailure", error51 instanceof Error ? error51.message : String(error51));
21048
21918
  }
@@ -21062,6 +21932,19 @@ var HermesSession = class {
21062
21932
  } else {
21063
21933
  outcome = { status: "succeeded" };
21064
21934
  }
21935
+ active.nativeCompactionOutcome = promptResponse?.compactionOutcome;
21936
+ if (active.persistsHistory && this.#transport.readNativeSnapshot) {
21937
+ try {
21938
+ const latest = (await this.#transport.readNativeSnapshot()).turns.at(-1);
21939
+ if (latest?.nativeTurnRef.nativeTurnKey !== previousNativeTurnKey)
21940
+ active.nativeTurnSnapshot = latest;
21941
+ } catch (error51) {
21942
+ outcome = {
21943
+ status: "failed",
21944
+ error: harnessError("nativeFailure", error51 instanceof Error ? error51.message : String(error51))
21945
+ };
21946
+ }
21947
+ }
21065
21948
  const terminalUsage = promptResponse ? usageFromPromptResponse(promptResponse.usage) : null;
21066
21949
  const usage = terminalUsage ? this.#mergeUsage(terminalUsage) : null;
21067
21950
  this.#completeActiveTurn(active, outcome, usage);
@@ -21069,10 +21952,17 @@ var HermesSession = class {
21069
21952
  #completeActiveTurn(active, outcome, usage = null) {
21070
21953
  if (this.#activeTurn !== active)
21071
21954
  return;
21955
+ this.#cancelApprovalWaiters();
21072
21956
  this.#activeTurn = null;
21073
21957
  this.#activeTurnId = null;
21074
21958
  active.finish();
21075
- const nativeTurnRef = nativeTurnRefSchema.parse({
21959
+ const compactionOutcome = active.finishCompaction(outcome);
21960
+ if (outcome.status === "succeeded" && compactionOutcome?.status === "failed") {
21961
+ outcome = { status: "failed", error: compactionOutcome.error };
21962
+ }
21963
+ if (active.nativeTurnSnapshot?.checkpoint)
21964
+ outcome = { ...outcome, checkpoint: active.nativeTurnSnapshot.checkpoint };
21965
+ const nativeTurnRef = active.nativeTurnSnapshot?.nativeTurnRef ?? nativeTurnRefSchema.parse({
21076
21966
  harnessId: this.#nativeRef.harnessId,
21077
21967
  nativeSessionId: this.#nativeRef.nativeSessionId,
21078
21968
  nativeTurnKey: active.turnKey,
@@ -21085,10 +21975,18 @@ var HermesSession = class {
21085
21975
  if (usage) {
21086
21976
  this.#emit({ type: "session.usage.changed", usage, observedForTurnId: active.turnId });
21087
21977
  }
21088
- this.#emit({ type: "turn.completed", turnId: active.turnId, nativeTurnRef, outcome });
21089
- this.#completedTurns.push(turnSnapshot);
21978
+ this.#emit({
21979
+ type: "turn.completed",
21980
+ turnId: active.turnId,
21981
+ ...active.persistsHistory && (!this.#transport.readNativeSnapshot || active.nativeTurnSnapshot) ? { nativeTurnRef } : {},
21982
+ outcome
21983
+ });
21984
+ if (active.persistsHistory)
21985
+ this.#completedTurns.push(turnSnapshot);
21090
21986
  }
21091
21987
  #handleTransportEvent(active, event) {
21988
+ if (this.#activeTurn !== active)
21989
+ return;
21092
21990
  switch (event.type) {
21093
21991
  case "usage": {
21094
21992
  const usage = usageFromContext(event.used, event.size);
@@ -21106,6 +22004,8 @@ var HermesSession = class {
21106
22004
  this.#appendTextItem(active, "reasoning", event.text);
21107
22005
  return;
21108
22006
  case "agent.text":
22007
+ if (active.compactionItem)
22008
+ active.compactionText += event.text;
21109
22009
  this.#appendTextItem(active, "agentMessage", event.text);
21110
22010
  return;
21111
22011
  case "tool.call": {
@@ -21121,8 +22021,8 @@ var HermesSession = class {
21121
22021
  return;
21122
22022
  }
21123
22023
  }
21124
- #appendTextItem(active, kind, text) {
21125
- const appended = active.appendText(kind, text);
22024
+ #appendTextItem(active, kind, text2) {
22025
+ const appended = active.appendText(kind, text2);
21126
22026
  if (!appended)
21127
22027
  return;
21128
22028
  if (appended.startedItem) {
@@ -21132,7 +22032,7 @@ var HermesSession = class {
21132
22032
  type: "item.updated",
21133
22033
  turnId: active.turnId,
21134
22034
  itemId: appended.item.itemId,
21135
- update: { type: "text.append", text }
22035
+ update: { type: "text.append", text: text2 }
21136
22036
  });
21137
22037
  }
21138
22038
  #startToolItem(active, toolCallId, title, name, rawInput) {
@@ -21142,7 +22042,7 @@ var HermesSession = class {
21142
22042
  }
21143
22043
  const item = {
21144
22044
  type: "toolExecution",
21145
- itemId: hostItemIdSchema.parse(randomUUID()),
22045
+ itemId: hostItemIdSchema.parse(randomUUID2()),
21146
22046
  toolName: (name ?? title ?? "").trim() || toolCallId,
21147
22047
  arguments: isPlainObjectOrArray(rawInput) ? rawInput : null
21148
22048
  };
@@ -21158,7 +22058,8 @@ var HermesSession = class {
21158
22058
  const entry = active.getToolItem(update.toolCallId);
21159
22059
  if (!entry)
21160
22060
  return;
21161
- const output = toolOutputFromUpdate(update);
22061
+ active.rememberFileChanges(update.toolCallId, update);
22062
+ const output = hermesToolOutput(update);
21162
22063
  if (output && output.content.length > 0) {
21163
22064
  active.updateToolOutput(update.toolCallId, output);
21164
22065
  this.#emit({
@@ -21172,32 +22073,57 @@ var HermesSession = class {
21172
22073
  const completed = active.completeToolItem(update.toolCallId, output, update.status === "failed");
21173
22074
  if (completed) {
21174
22075
  this.#emit({ type: "item.completed", turnId: active.turnId, snapshot: completed });
22076
+ if (update.status === "completed") {
22077
+ const fileChange = active.completeFileChanges(update.toolCallId, completed.item.itemId);
22078
+ if (fileChange) {
22079
+ this.#emit({ type: "item.started", turnId: active.turnId, item: fileChange.item });
22080
+ this.#emit({ type: "item.completed", turnId: active.turnId, snapshot: fileChange });
22081
+ }
22082
+ }
21175
22083
  }
21176
22084
  }
21177
22085
  }
21178
22086
  #handlePermissionRequest(active, request) {
21179
- const projected = projectPermissionOptions(request.options);
22087
+ const projected = projectPermissionOptions(request.options, request.effects);
21180
22088
  if (projected.actions.length === 0) {
21181
22089
  return Promise.resolve({ outcome: { outcome: "cancelled" } });
21182
22090
  }
21183
- const interactionId = hostInteractionIdSchema.parse(randomUUID());
22091
+ const interactionId = hostInteractionIdSchema.parse(randomUUID2());
21184
22092
  const interaction = {
21185
22093
  type: "approval",
21186
22094
  interactionId,
21187
22095
  turnId: active.turnId,
21188
- title: request.request.options?.[0]?.name?.slice(0, 200) ?? "Hermes 请求批准",
22096
+ title: request.request.toolCall.title?.slice(0, 200) ?? "Hermes 请求批准",
22097
+ ...request.description ? { description: request.description } : {},
21189
22098
  subject: { type: "nativeAction" },
21190
22099
  actions: projected.actions
21191
22100
  };
21192
22101
  this.#emitInteraction(interaction);
21193
- return new Promise((resolve) => {
22102
+ let abort = () => {
22103
+ };
22104
+ const pending = new Promise((resolve) => {
21194
22105
  this.#approvalWaiters.set(interactionId, {
21195
22106
  interaction,
21196
22107
  turnId: active.turnId,
21197
22108
  optionIdByAction: projected.optionIdByAction,
21198
22109
  resolve
21199
22110
  });
22111
+ abort = () => {
22112
+ if (!this.#approvalWaiters.delete(interactionId))
22113
+ return;
22114
+ resolve({ outcome: { outcome: "cancelled" } });
22115
+ this.#emit({
22116
+ type: "interaction.closed",
22117
+ interactionId,
22118
+ turnId: active.turnId,
22119
+ reason: request.signal?.reason === "expired" ? "expired" : "cancelled"
22120
+ });
22121
+ };
22122
+ request.signal?.addEventListener("abort", abort, { once: true });
22123
+ if (request.signal?.aborted)
22124
+ abort();
21200
22125
  });
22126
+ return pending.finally(() => request.signal?.removeEventListener("abort", abort));
21201
22127
  }
21202
22128
  #respondToApproval(command) {
21203
22129
  if (this.#faulted) {
@@ -21238,6 +22164,7 @@ var HermesSession = class {
21238
22164
  }
21239
22165
  try {
21240
22166
  await this.#transport.cancel();
22167
+ this.#questions.cancel(command.turnId);
21241
22168
  for (const [interactionId, waiter] of this.#approvalWaiters) {
21242
22169
  if (waiter.turnId !== command.turnId)
21243
22170
  continue;
@@ -21256,10 +22183,29 @@ var HermesSession = class {
21256
22183
  return { ok: false, error: failure2 };
21257
22184
  }
21258
22185
  }
21259
- async #selectModel(command) {
21260
- if (this.#activeTurn) {
21261
- return err("sessionBusy", "Model selection conflicts with an active Turn", true);
22186
+ #handleQuestionRequest(active, request) {
22187
+ if (this.#activeTurn !== active)
22188
+ return Promise.resolve({ type: "question", answers: {}, cancelled: true });
22189
+ return this.#questions.open(active.turnId, request);
22190
+ }
22191
+ async #selectThinking(command) {
22192
+ if (!this.#transport.setThinking)
22193
+ return err("unsupported", "Hermes does not expose Thinking options");
22194
+ if (!this.#state.availableThinkingOptions?.some(({ id }) => id === command.thinkingOptionId))
22195
+ return err("invalidRequest", "Unknown Hermes Thinking option");
22196
+ try {
22197
+ const selected = await this.#transport.setThinking(command.thinkingOptionId);
22198
+ this.#state = {
22199
+ ...this.#state,
22200
+ effectiveThinkingOptionId: harnessThinkingOptionIdSchema.parse(selected)
22201
+ };
22202
+ this.#emit({ type: "session.state.changed", state: { ...this.#state } });
22203
+ return ok({ completed: true });
22204
+ } catch (error51) {
22205
+ return err("nativeFailure", error51 instanceof Error ? error51.message : String(error51));
21262
22206
  }
22207
+ }
22208
+ async #selectModel(command) {
21263
22209
  const native = decodeHermesModelRefId(command.model.id);
21264
22210
  if (!native) {
21265
22211
  return err("invalidRequest", "Model Ref does not belong to Hermes");
@@ -21299,8 +22245,16 @@ var HermesSession = class {
21299
22245
  }
21300
22246
  return err("nativeFailure", error51 instanceof Error ? error51.message : "Hermes rejected Permission Mode selection");
21301
22247
  }
22248
+ const locator = this.#nativeRef.locator;
22249
+ if (locator && typeof locator === "object" && !Array.isArray(locator) && locator.transport === "gateway") {
22250
+ this.#nativeRef = {
22251
+ ...this.#nativeRef,
22252
+ locator: { ...locator, permissionModeId: command.permissionModeId }
22253
+ };
22254
+ }
21302
22255
  this.#state = {
21303
22256
  ...this.#state,
22257
+ nativeRef: this.#nativeRef,
21304
22258
  effectivePermissionModeId: harnessPermissionModeIdSchema.parse(command.permissionModeId)
21305
22259
  };
21306
22260
  this.#emit({ type: "session.state.changed", state: { ...this.#state } });
@@ -21308,11 +22262,1323 @@ var HermesSession = class {
21308
22262
  }
21309
22263
  };
21310
22264
 
22265
+ // dist/gateway-transport.js
22266
+ import { spawn as spawn3, spawnSync as spawnSync2 } from "node:child_process";
22267
+ import { access as access2 } from "node:fs/promises";
22268
+ import { createInterface } from "node:readline";
22269
+
22270
+ // dist/gateway-delegation.js
22271
+ import { mkdtemp, writeFile, rm } from "node:fs/promises";
22272
+ import os2 from "node:os";
22273
+ import path5 from "node:path";
22274
+ var HERMES_DELEGATION_GUIDANCE = `This Session runs inside codexhost.
22275
+ 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.`;
22276
+ var required2 = [
22277
+ "CODEXHOST_CLI_PATH",
22278
+ "CODEXHOST_RUNTIME_ENDPOINT",
22279
+ "CODEXHOST_RUNTIME_TOKEN",
22280
+ "CODEXHOST_THREAD_ID"
22281
+ ];
22282
+ async function prepareGatewayDelegation(environment) {
22283
+ if (!required2.every((key) => environment[key]))
22284
+ return { environment, bootstrap: "", dispose: async () => {
22285
+ } };
22286
+ const directory = await mkdtemp(path5.join(os2.tmpdir(), "codexhost-hermes-delegation-"));
22287
+ const file2 = path5.join(directory, "SKILL.md");
22288
+ try {
22289
+ await writeFile(file2, `---
22290
+ name: delegation
22291
+ description: Discover authorized codexhost agent collaboration.
22292
+ ---
22293
+
22294
+ ${HERMES_DELEGATION_GUIDANCE}
22295
+ `, { mode: 384 });
22296
+ return {
22297
+ environment: {
22298
+ ...environment,
22299
+ HERMES_TUI_SKILLS: [environment.HERMES_TUI_SKILLS, "codexhost-runtime:delegation"].filter(Boolean).join(",")
22300
+ },
22301
+ // Reserve RPC stdout before native plugin imports. The registration is
22302
+ // process-local; no plugin is installed and no user config is changed.
22303
+ bootstrap: `from tui_gateway import server
22304
+ from pathlib import Path
22305
+ from hermes_cli.plugins import PluginContext, PluginManifest, get_plugin_manager
22306
+ PluginContext(PluginManifest(name="codexhost-runtime"), get_plugin_manager()).register_skill(
22307
+ "delegation", Path(${JSON.stringify(file2)}))
22308
+ `,
22309
+ dispose: () => rm(directory, { recursive: true, force: true })
22310
+ };
22311
+ } catch (error51) {
22312
+ await rm(directory, { recursive: true, force: true });
22313
+ throw error51;
22314
+ }
22315
+ }
22316
+
22317
+ // dist/gateway-transport.js
22318
+ function gatewayRecord(value) {
22319
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
22320
+ }
22321
+ function gatewayString(value) {
22322
+ return typeof value === "string" ? value : "";
22323
+ }
22324
+ var GATEWAY_LAUNCH = `import runpy
22325
+ runpy.run_module('tui_gateway.entry', run_name='__main__')`;
22326
+ var HermesGatewayTransport = class _HermesGatewayTransport {
22327
+ python;
22328
+ cwd;
22329
+ environment;
22330
+ timeoutMs;
22331
+ onEvent = () => void 0;
22332
+ onRequest = (id) => this.rejectRequest(id);
22333
+ onFault = () => void 0;
22334
+ #child = null;
22335
+ #nextId = 0;
22336
+ #pending = /* @__PURE__ */ new Map();
22337
+ #closed = false;
22338
+ #closePromise = null;
22339
+ #processClosed = Promise.resolve();
22340
+ #sessionErrors = /* @__PURE__ */ new Map();
22341
+ #sessionReady = /* @__PURE__ */ new Map();
22342
+ #sessionWaiters = /* @__PURE__ */ new Map();
22343
+ #stderr = "";
22344
+ #delegation;
22345
+ constructor(python, cwd, environment, timeoutMs = 3e4) {
22346
+ this.python = python;
22347
+ this.cwd = cwd;
22348
+ this.environment = environment;
22349
+ this.timeoutMs = timeoutMs;
22350
+ }
22351
+ static async probe(executable, cwd, environment) {
22352
+ const shim = await venvPythonFromShim(executable);
22353
+ const candidates = [
22354
+ ...new Set([
22355
+ environment.CODEXHOST_HERMES_GATEWAY_PYTHON,
22356
+ shim,
22357
+ ...inventoryPythonCandidates(executable)
22358
+ ].filter((candidate) => !!candidate))
22359
+ ];
22360
+ for (const candidate of candidates) {
22361
+ try {
22362
+ await access2(candidate);
22363
+ } catch {
22364
+ continue;
22365
+ }
22366
+ const transport = new _HermesGatewayTransport(candidate, cwd, environment, 2e4);
22367
+ try {
22368
+ await transport.start();
22369
+ return candidate;
22370
+ } catch {
22371
+ } finally {
22372
+ await transport.close();
22373
+ }
22374
+ }
22375
+ return null;
22376
+ }
22377
+ async prepareSession() {
22378
+ if (this.#child || this.#closed)
22379
+ throw new Error("Hermes gateway Session cannot be prepared after start");
22380
+ const prepared = await prepareGatewayDelegation(this.environment);
22381
+ if (this.#closed) {
22382
+ await prepared.dispose();
22383
+ throw new Error("Hermes gateway closed during Session preparation");
22384
+ }
22385
+ this.#delegation = prepared;
22386
+ }
22387
+ async start() {
22388
+ if (this.#child || this.#closed)
22389
+ throw new Error("Hermes gateway cannot be started twice");
22390
+ const child = spawn3(this.python, ["-I", "-u", "-c", (this.#delegation?.bootstrap ?? "") + GATEWAY_LAUNCH], {
22391
+ cwd: this.cwd,
22392
+ env: {
22393
+ ...process.env,
22394
+ ...this.#delegation?.environment ?? this.environment,
22395
+ HERMES_TUI_TOOL_PROGRESS: "all"
22396
+ },
22397
+ stdio: ["pipe", "pipe", "pipe"],
22398
+ windowsHide: true,
22399
+ detached: process.platform !== "win32"
22400
+ });
22401
+ this.#child = child;
22402
+ this.#processClosed = new Promise((resolve) => child.once("close", () => resolve()));
22403
+ child.stderr.on("data", (data) => {
22404
+ this.#stderr = sanitizeDiagnosticTail(this.#stderr + data.toString());
22405
+ });
22406
+ child.stdin.on("error", (error51) => this.#fault(error51));
22407
+ child.once("error", (error51) => this.#fault(error51));
22408
+ child.once("exit", () => {
22409
+ if (!this.#closed)
22410
+ this.#fault(new Error(`Hermes gateway exited: ${this.#stderr}`));
22411
+ });
22412
+ createInterface({ input: child.stdout }).on("line", (line) => {
22413
+ try {
22414
+ this.#frame(gatewayRecord(JSON.parse(line)));
22415
+ } catch (error51) {
22416
+ this.#fault(error51 instanceof Error ? error51 : new Error(String(error51)));
22417
+ }
22418
+ });
22419
+ const capabilities = await this.request("gateway.capabilities", {});
22420
+ if (capabilities.per_session_exclusive_submit !== true)
22421
+ throw new Error("Hermes gateway does not enforce exclusive turns");
22422
+ }
22423
+ async waitForSession(sessionId) {
22424
+ const error51 = this.#sessionErrors.get(sessionId);
22425
+ if (error51)
22426
+ throw error51;
22427
+ const ready = this.#sessionReady.get(sessionId);
22428
+ if (ready)
22429
+ return ready;
22430
+ const result = new Promise((resolve, reject) => this.#sessionWaiters.set(sessionId, { resolve, reject }));
22431
+ try {
22432
+ return await withTimeout(result, this.timeoutMs, "Hermes gateway agent initialization");
22433
+ } finally {
22434
+ this.#sessionWaiters.delete(sessionId);
22435
+ }
22436
+ }
22437
+ async request(method, params, timeoutMs = this.timeoutMs) {
22438
+ if (this.#closed || !this.#child)
22439
+ throw new Error("Hermes gateway is closed");
22440
+ const id = `codexhost-${++this.#nextId}`;
22441
+ const response = new Promise((resolve, reject) => {
22442
+ this.#pending.set(id, { resolve, reject });
22443
+ this.#send({ jsonrpc: "2.0", id, method, params });
22444
+ });
22445
+ try {
22446
+ return await withTimeout(response, timeoutMs, `Hermes ${method}`);
22447
+ } catch (error51) {
22448
+ if (error51 instanceof HermesTransportError)
22449
+ this.#fault(error51);
22450
+ throw error51;
22451
+ } finally {
22452
+ this.#pending.delete(id);
22453
+ }
22454
+ }
22455
+ respond(id, result) {
22456
+ this.#send({ jsonrpc: "2.0", id, result });
22457
+ }
22458
+ rejectRequest(id) {
22459
+ this.#send({
22460
+ jsonrpc: "2.0",
22461
+ id,
22462
+ error: { code: -32601, message: "Unsupported Host interaction" }
22463
+ });
22464
+ }
22465
+ #send(frame) {
22466
+ if (!this.#child?.stdin.writable)
22467
+ throw new Error("Hermes gateway input is closed");
22468
+ this.#child.stdin.write(JSON.stringify(frame) + "\n");
22469
+ }
22470
+ #frame(frame) {
22471
+ if (frame.method === "event") {
22472
+ const event = gatewayRecord(frame.params);
22473
+ const sessionId = gatewayString(event.session_id);
22474
+ const payload = gatewayRecord(event.payload);
22475
+ if (event.type === "session.info" && payload.lazy !== true) {
22476
+ this.#sessionReady.set(sessionId, payload);
22477
+ this.#sessionWaiters.get(sessionId)?.resolve(payload);
22478
+ } else if (event.type === "error") {
22479
+ const error51 = new Error(gatewayString(payload.message));
22480
+ this.#sessionErrors.set(sessionId, error51);
22481
+ this.#sessionWaiters.get(sessionId)?.reject(error51);
22482
+ }
22483
+ this.onEvent(event);
22484
+ return;
22485
+ }
22486
+ if (typeof frame.method === "string" && typeof frame.id === "string") {
22487
+ this.onRequest(frame.id, frame.method, gatewayRecord(frame.params));
22488
+ return;
22489
+ }
22490
+ const id = gatewayString(frame.id);
22491
+ const pending = this.#pending.get(id);
22492
+ if (!pending)
22493
+ return;
22494
+ this.#pending.delete(id);
22495
+ if (frame.error)
22496
+ pending.reject(new Error(gatewayString(gatewayRecord(frame.error).message) || "Hermes gateway request failed"));
22497
+ else
22498
+ pending.resolve(gatewayRecord(frame.result));
22499
+ }
22500
+ #fault(error51) {
22501
+ for (const pending of this.#pending.values())
22502
+ pending.reject(error51);
22503
+ for (const waiter of this.#sessionWaiters.values())
22504
+ waiter.reject(error51);
22505
+ this.#sessionWaiters.clear();
22506
+ this.#pending.clear();
22507
+ if (!this.#closed) {
22508
+ this.onFault(error51);
22509
+ void this.close();
22510
+ }
22511
+ }
22512
+ #signal(signal) {
22513
+ const pid = this.#child?.pid;
22514
+ if (!pid)
22515
+ return;
22516
+ if (process.platform === "win32") {
22517
+ spawnSync2("taskkill.exe", ["/pid", String(pid), "/t", "/f"], {
22518
+ stdio: "ignore",
22519
+ windowsHide: true
22520
+ });
22521
+ return;
22522
+ }
22523
+ try {
22524
+ process.kill(-pid, signal);
22525
+ } catch (error51) {
22526
+ if (gatewayRecord(error51).code !== "ESRCH")
22527
+ throw error51;
22528
+ }
22529
+ }
22530
+ close() {
22531
+ return this.#closePromise ??= this.#close().finally(() => this.#delegation?.dispose());
22532
+ }
22533
+ async #close() {
22534
+ if (this.#closed)
22535
+ return;
22536
+ this.#closed = true;
22537
+ this.#fault(new Error("Hermes gateway closed"));
22538
+ const child = this.#child;
22539
+ if (!child)
22540
+ return;
22541
+ if (child.exitCode !== null || child.signalCode !== null) {
22542
+ this.#signal("SIGKILL");
22543
+ return;
22544
+ }
22545
+ const exit = this.#processClosed;
22546
+ child.stdin.end();
22547
+ try {
22548
+ await withTimeout(exit, 2e3, "Hermes gateway close");
22549
+ } catch {
22550
+ this.#signal("SIGTERM");
22551
+ try {
22552
+ await withTimeout(exit, 2e3, "Hermes gateway exit");
22553
+ } catch {
22554
+ this.#signal("SIGKILL");
22555
+ await withTimeout(exit, 2e3, "Hermes gateway killed").catch(() => void 0);
22556
+ }
22557
+ }
22558
+ }
22559
+ };
22560
+
22561
+ // dist/gateway-history.js
22562
+ import { spawn as spawn4 } from "node:child_process";
22563
+
22564
+ // dist/gateway-history-script.js
22565
+ var GATEWAY_HISTORY_SCRIPT = String.raw`
22566
+ import sys
22567
+ output = sys.stdout
22568
+ sys.stdout = sys.stderr
22569
+ import hashlib, json, sqlite3
22570
+ from pathlib import Path
22571
+ from urllib.parse import quote
22572
+ from tui_gateway.server import _history_to_messages
22573
+ from agent.context_compressor import user_originated_turn_view
22574
+ from hermes_state import SessionDB
22575
+ from hermes_state_ids import new_session_id
22576
+ p = json.load(sys.stdin)
22577
+ sid = p['sessionId']
22578
+ public_sid = sid
22579
+
22580
+ class HistoryFailure(Exception):
22581
+ def __init__(self, code, message):
22582
+ super().__init__(message)
22583
+ self.code = code
22584
+
22585
+ def digest(rows):
22586
+ return hashlib.sha256(json.dumps(rows, sort_keys=True, ensure_ascii=True, separators=(',', ':')).encode()).hexdigest()
22587
+
22588
+ def config(value):
22589
+ return json.loads(value) if isinstance(value, str) else dict(value or {})
22590
+
22591
+ def user_text(row):
22592
+ view = user_originated_turn_view(row)
22593
+ if view is None: return None
22594
+ # Use the original display kind for native /steer and skill rendering; the
22595
+ # origin predicate above separately excludes synthetic continuation turns.
22596
+ projected = _history_to_messages([row])
22597
+ if not projected: return None
22598
+ return projected[0].get('text')
22599
+
22600
+ def capture(db):
22601
+ exported = db.export_session(sid)
22602
+ if exported is None:
22603
+ raise RuntimeError('Hermes session is not persisted')
22604
+ active = exported['messages']
22605
+ lineage = db.get_compression_lineage(sid)
22606
+ if not lineage: raise RuntimeError('Hermes session lineage is missing')
22607
+ _, display = db.get_resume_conversations(sid)
22608
+ # get_resume_conversations preserves tool relations but sanitizes display text.
22609
+ # Read the same native rows verbatim and native display identities through a
22610
+ # separate mode=ro connection; MIN(id) preserves identity across tail cloning.
22611
+ uri = 'file:' + quote(str(Path(db.db_path).resolve()), safe='/') + '?mode=ro'
22612
+ with sqlite3.connect(uri, uri=True) as conn:
22613
+ conn.row_factory = sqlite3.Row
22614
+ rows = []
22615
+ native_rows = {}
22616
+ for msg in display:
22617
+ row = conn.execute('SELECT * FROM messages WHERE id = ?', (msg['_row_id'],)).fetchone()
22618
+ if row is None:
22619
+ raise RuntimeError('Hermes history changed while reading')
22620
+ owner = row['session_id']
22621
+ if owner not in native_rows:
22622
+ native_rows[owner] = {r['id']: r for r in db.get_messages(owner, include_inactive=True)}
22623
+ raw = dict(native_rows[owner][row['id']])
22624
+ original = row['id']
22625
+ if row['display_identity'] is not None:
22626
+ original = conn.execute('SELECT MIN(id) FROM messages WHERE session_id IN (' + ','.join('?' for _ in lineage) + ') AND display_identity = ?',
22627
+ (*lineage, row['display_identity'])).fetchone()[0] or row['id']
22628
+ raw['original_id'] = original
22629
+ if raw['role'] == 'user': raw['user_text'] = user_text(raw)
22630
+ elif raw['role'] == 'assistant':
22631
+ visible = _history_to_messages([raw])
22632
+ raw['display_text'] = visible[0].get('text', '') if visible else ''
22633
+ raw['display_visible'] = bool(visible)
22634
+ rows.append(raw)
22635
+ compressed = conn.execute('SELECT 1 FROM messages WHERE session_id = ? AND (compacted = 1 OR _compressed_summary = 1) LIMIT 1', (sid,)).fetchone() is not None
22636
+ current = db.export_session(sid)
22637
+ if current is None or digest(current['messages']) != digest(active):
22638
+ raise RuntimeError('Hermes history changed while reading')
22639
+ derivable = sid == public_sid and len(lineage) == 1 and not compressed and [r['id'] for r in rows] == [r['id'] for r in active]
22640
+ boundaries = {}
22641
+ if derivable:
22642
+ for i, row in enumerate(active):
22643
+ if user_text(row) is None: continue
22644
+ end = next((j for j in range(i + 1, len(active)) if user_text(active[j]) is not None), len(active))
22645
+ boundaries[str(row['id'])] = {'count': end, 'digest': digest(active[:end])}
22646
+ return exported, {'rows': rows, 'derivable': derivable, 'boundaries': boundaries, 'physicalSessionId': sid}
22647
+
22648
+ try:
22649
+ if p['operation'] == 'ensure':
22650
+ db = SessionDB()
22651
+ try:
22652
+ if db.get_session(sid) is None:
22653
+ from hermes_constants import parse_reasoning_effort
22654
+ initial = {}
22655
+ if isinstance(p.get('provider'), str) and p['provider']: initial['provider'] = p['provider']
22656
+ if isinstance(p.get('model'), str) and p['model']: initial['model'] = p['model']
22657
+ if isinstance(p.get('reasoningEffort'), str):
22658
+ reasoning = parse_reasoning_effort(p['reasoningEffort'])
22659
+ if reasoning is not None: initial['reasoning_config'] = reasoning
22660
+ if isinstance(p.get('yolo'), bool): initial['yolo_mode'] = p['yolo']
22661
+ db.create_session(session_id=sid, source='cli', cwd=p['cwd'], model=p.get('model'), model_config=initial)
22662
+ result = {'created': True}
22663
+ finally: db.close()
22664
+ elif p['operation'] == 'discard':
22665
+ child = p['derivedSessionId']
22666
+ result = {'deleted': False}
22667
+ if child == sid: raise HistoryFailure('invalidState', 'Cannot delete the source Hermes session')
22668
+ db = SessionDB()
22669
+ try:
22670
+ row = db.get_session(child)
22671
+ if (row is not None and row.get('parent_session_id') == sid
22672
+ and config(row.get('model_config')).get('_branched_from') == sid
22673
+ and digest(db.get_messages(child)) == p['expectedDigest']):
22674
+ result['deleted'] = db.delete_session(child, expected_delete_ids=[child])
22675
+ finally: db.close()
22676
+ else:
22677
+ db = SessionDB(read_only=True)
22678
+ try:
22679
+ sid = db.resolve_resume_session_id(public_sid)
22680
+ exported, snapshot = capture(db)
22681
+ finally: db.close()
22682
+ if p['operation'] in ('read', 'resolve'):
22683
+ result = snapshot
22684
+ elif p['operation'] == 'derive':
22685
+ if not snapshot['derivable']:
22686
+ raise HistoryFailure('unsupported', 'Hermes cannot losslessly derive compacted or inherited history')
22687
+ messages = exported['messages']
22688
+ if p.get('rollbackLastTurn'):
22689
+ users = [i for i, row in enumerate(messages) if user_text(row) is not None]
22690
+ if not users: raise HistoryFailure('invalidState', 'Hermes session has no user turn to roll back')
22691
+ messages = messages[:users[-1]]
22692
+ elif p.get('checkpoint'):
22693
+ checkpoint = p['checkpoint']
22694
+ boundary = snapshot['boundaries'].get(checkpoint['checkpointId'])
22695
+ if boundary is None or boundary != checkpoint.get('locator'):
22696
+ raise HistoryFailure('checkpointNotFound', 'Hermes checkpoint no longer matches native history')
22697
+ messages = messages[:boundary['count']]
22698
+ child = new_session_id()
22699
+ model_config = config(exported.get('model_config'))
22700
+ model_config['_branched_from'] = sid
22701
+ payload = {**exported, 'id': child, 'parent_session_id': sid, 'model_config': model_config,
22702
+ 'messages': messages, 'title': None, 'ended_at': None, 'end_reason': None, 'archived': False}
22703
+ db = SessionDB()
22704
+ imported = False
22705
+ try:
22706
+ result = db.import_sessions([payload])
22707
+ if result.get('imported_ids') != [child]:
22708
+ raise RuntimeError('Hermes native import did not create the derived session')
22709
+ imported = True
22710
+ # Native import resets ownership. Fill this NEW row only through the
22711
+ # native COALESCE upsert, preserving source profile and working dir.
22712
+ db.create_session(session_id=child, source=exported.get('source') or 'cli',
22713
+ cwd=exported.get('cwd'), profile_name=exported.get('profile_name'), parent_session_id=sid)
22714
+ tools = exported.get('tool_names')
22715
+ if isinstance(tools, str): tools = json.loads(tools)
22716
+ if tools is not None: db.update_session_tool_names(child, tools)
22717
+ copied = db.get_messages(child)
22718
+ def payload_rows(rows):
22719
+ ignored = {'id', 'session_id', 'active', 'compacted', '_row_id'}
22720
+ result = []
22721
+ for row in rows:
22722
+ row = {k: v for k, v in row.items() if k not in ignored}
22723
+ for key in ('reasoning_details', 'codex_reasoning_items', 'codex_message_items'):
22724
+ if isinstance(row.get(key), str): row[key] = json.loads(row[key])
22725
+ result.append(row)
22726
+ return result
22727
+ if payload_rows(copied) != payload_rows(messages):
22728
+ raise RuntimeError('Hermes native import did not preserve the transcript')
22729
+ result = {'sessionId': child, 'digest': digest(copied)}
22730
+ except Exception:
22731
+ if imported: db.delete_session(child, expected_delete_ids=[child])
22732
+ raise
22733
+ finally: db.close()
22734
+ else: raise RuntimeError('Unknown Hermes history operation')
22735
+ except HistoryFailure as exc:
22736
+ result = {'error': {'code': exc.code, 'message': str(exc)}}
22737
+ print(json.dumps(result, ensure_ascii=True), file=output)
22738
+ `;
22739
+
22740
+ // dist/gateway-history.js
22741
+ function record2(value) {
22742
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
22743
+ }
22744
+ function text(value) {
22745
+ if (typeof value === "string")
22746
+ return value;
22747
+ if (Array.isArray(value))
22748
+ return value.map((part) => text(record2(part).text)).join("\n");
22749
+ return "";
22750
+ }
22751
+ function parsed(value) {
22752
+ if (typeof value === "string") {
22753
+ try {
22754
+ return JSON.parse(value);
22755
+ } catch {
22756
+ return value;
22757
+ }
22758
+ }
22759
+ return value ?? null;
22760
+ }
22761
+ function projectGatewayHistory(sessionId, data) {
22762
+ const turns = [];
22763
+ let turn;
22764
+ const tools = /* @__PURE__ */ new Map();
22765
+ for (const row of data.rows) {
22766
+ if (!Number.isSafeInteger(row.id) || !Number.isSafeInteger(row.original_id))
22767
+ throw new Error("Hermes history has no durable message identity");
22768
+ const id = String(row.original_id);
22769
+ if (row.role === "user") {
22770
+ if (typeof row.user_text !== "string")
22771
+ continue;
22772
+ tools.clear();
22773
+ const boundary = data.derivable ? data.boundaries[String(row.id)] : void 0;
22774
+ turn = {
22775
+ nativeTurnRef: nativeTurnRefSchema.parse({
22776
+ harnessId: "hermes",
22777
+ nativeSessionId: sessionId,
22778
+ nativeTurnKey: id,
22779
+ formatVersion: 1
22780
+ }),
22781
+ ...boundary ? {
22782
+ checkpoint: nativeCheckpointRefSchema.parse({
22783
+ harnessId: "hermes",
22784
+ nativeSessionId: sessionId,
22785
+ checkpointId: String(row.id),
22786
+ formatVersion: 1,
22787
+ locator: boundary
22788
+ })
22789
+ } : {},
22790
+ input: [{ type: "text", text: row.user_text }],
22791
+ items: [],
22792
+ outcome: {
22793
+ status: "unknown",
22794
+ reason: "Hermes persisted history does not record a turn outcome"
22795
+ },
22796
+ ...typeof row.timestamp === "number" ? { startedAtMs: Math.trunc(row.timestamp * 1e3) } : {}
22797
+ };
22798
+ turns.push(turn);
22799
+ continue;
22800
+ }
22801
+ if (!turn)
22802
+ continue;
22803
+ const itemId = (suffix) => hostItemIdSchema.parse(`hermes:${id}:${suffix}`);
22804
+ if (row.role === "assistant") {
22805
+ const reasoning = row.display_visible === false ? "" : text(row.reasoning_content) || text(row.reasoning);
22806
+ if (reasoning)
22807
+ turn.items.push({
22808
+ item: { type: "reasoning", itemId: itemId("reasoning"), text: reasoning },
22809
+ outcome: { status: "succeeded" }
22810
+ });
22811
+ const content = typeof row.display_text === "string" ? row.display_text : text(row.content);
22812
+ if (content)
22813
+ turn.items.push({
22814
+ item: { type: "agentMessage", itemId: itemId("message"), text: content },
22815
+ outcome: { status: "succeeded" }
22816
+ });
22817
+ if (Array.isArray(row.tool_calls))
22818
+ for (const value of row.tool_calls) {
22819
+ const call = record2(value);
22820
+ const fn = record2(call.function);
22821
+ if (typeof call.id !== "string" || typeof fn.name !== "string")
22822
+ continue;
22823
+ tools.set(call.id, {
22824
+ item: {
22825
+ type: "toolExecution",
22826
+ itemId: itemId(`tool:${call.id}`),
22827
+ toolName: fn.name,
22828
+ arguments: parsed(fn.arguments)
22829
+ },
22830
+ outcome: { status: "succeeded" }
22831
+ });
22832
+ }
22833
+ } else if (row.role === "tool" && typeof row.tool_call_id === "string") {
22834
+ const tool = tools.get(row.tool_call_id);
22835
+ if (!tool || tool.item.type !== "toolExecution")
22836
+ continue;
22837
+ const output = typeof row.content === "string" ? row.content : JSON.stringify(row.content ?? "");
22838
+ tool.item.output = { content: [{ type: "text", text: output }] };
22839
+ const result = record2(parsed(row.content));
22840
+ if (result.is_error === true || result.isError === true || result.error)
22841
+ tool.outcome = {
22842
+ status: "failed",
22843
+ error: {
22844
+ code: "nativeFailure",
22845
+ message: typeof result.error === "string" ? result.error : "Hermes tool reported an error",
22846
+ retryable: false
22847
+ }
22848
+ };
22849
+ turn.items.push(tool);
22850
+ tools.delete(row.tool_call_id);
22851
+ }
22852
+ }
22853
+ return { turns };
22854
+ }
22855
+ var HermesGatewayHistoryError = class extends Error {
22856
+ code;
22857
+ constructor(code, message) {
22858
+ super(message);
22859
+ this.code = code;
22860
+ }
22861
+ };
22862
+ var HermesGatewayHistory = class {
22863
+ options;
22864
+ #derived = /* @__PURE__ */ new Map();
22865
+ constructor(options) {
22866
+ this.options = options;
22867
+ }
22868
+ async readSnapshot() {
22869
+ const data = await this.#run({ operation: "read" });
22870
+ if (!Array.isArray(data.rows) || typeof data.derivable !== "boolean" || !data.boundaries)
22871
+ throw new Error("Malformed Hermes native history response");
22872
+ return projectGatewayHistory(this.options.nativeSessionId, data);
22873
+ }
22874
+ async resolvePhysicalSessionId() {
22875
+ const data = await this.#run({ operation: "resolve" });
22876
+ if (typeof data.physicalSessionId !== "string")
22877
+ throw new Error("Missing Hermes storage identity");
22878
+ return data.physicalSessionId;
22879
+ }
22880
+ async ensureCreated(input) {
22881
+ await this.#run({ operation: "ensure", ...input });
22882
+ }
22883
+ async derive(input = {}) {
22884
+ if (input.checkpoint && input.rollbackLastTurn)
22885
+ throw new Error("Conflicting Hermes derivation boundaries");
22886
+ if (input.checkpoint && (input.checkpoint.harnessId !== "hermes" || input.checkpoint.nativeSessionId !== this.options.nativeSessionId))
22887
+ throw new Error("Hermes checkpoint belongs to another session");
22888
+ const data = await this.#run({ operation: "derive", ...input });
22889
+ const ref = nativeSessionRefSchema.parse({
22890
+ harnessId: "hermes",
22891
+ nativeSessionId: data.sessionId,
22892
+ formatVersion: 1,
22893
+ locator: { transport: "gateway" }
22894
+ });
22895
+ if (typeof data.digest !== "string")
22896
+ throw new Error("Missing Hermes derived-history proof");
22897
+ this.#derived.set(ref.nativeSessionId, data.digest);
22898
+ return ref;
22899
+ }
22900
+ async discardDerived(ref) {
22901
+ const expectedDigest = this.#derived.get(ref.nativeSessionId);
22902
+ if (ref.harnessId !== "hermes" || ref.nativeSessionId === this.options.nativeSessionId || !expectedDigest)
22903
+ throw new HermesGatewayHistoryError("invalidRequest", "This history reader did not create the derived Hermes session");
22904
+ const result = await this.#run({
22905
+ operation: "discard",
22906
+ derivedSessionId: ref.nativeSessionId,
22907
+ expectedDigest
22908
+ });
22909
+ if (result.deleted === true)
22910
+ this.#derived.delete(ref.nativeSessionId);
22911
+ return result.deleted === true;
22912
+ }
22913
+ #run(input) {
22914
+ return new Promise((resolve, reject) => {
22915
+ const child = spawn4(this.options.python, ["-I", "-u", "-c", GATEWAY_HISTORY_SCRIPT], {
22916
+ cwd: this.options.cwd,
22917
+ env: { ...process.env, ...this.options.environment },
22918
+ stdio: ["pipe", "pipe", "pipe"],
22919
+ windowsHide: true
22920
+ });
22921
+ let stdout = "";
22922
+ const timer = setTimeout(() => {
22923
+ child.kill();
22924
+ reject(new Error("Hermes history operation timed out"));
22925
+ }, 3e4);
22926
+ child.on("error", () => {
22927
+ clearTimeout(timer);
22928
+ reject(new Error("Cannot start Hermes native history reader"));
22929
+ });
22930
+ child.stdout.on("data", (chunk) => {
22931
+ stdout += chunk.toString();
22932
+ if (stdout.length > 32 * 1024 * 1024) {
22933
+ child.kill();
22934
+ reject(new Error("Hermes native history exceeds the supported size"));
22935
+ }
22936
+ });
22937
+ child.stderr.resume();
22938
+ child.stdin.on("error", () => void 0);
22939
+ child.on("close", (code) => {
22940
+ clearTimeout(timer);
22941
+ if (code !== 0)
22942
+ return reject(new Error("Hermes native history operation failed; source history was not modified"));
22943
+ try {
22944
+ const value = record2(JSON.parse(stdout.trim()));
22945
+ const error51 = record2(value.error);
22946
+ if (["unsupported", "checkpointNotFound", "invalidState"].includes(String(error51.code))) {
22947
+ reject(new HermesGatewayHistoryError(error51.code, String(error51.message)));
22948
+ } else
22949
+ resolve(value);
22950
+ } catch {
22951
+ reject(new Error("Malformed Hermes native history response"));
22952
+ }
22953
+ });
22954
+ child.stdin.end(JSON.stringify({ ...input, sessionId: this.options.nativeSessionId }));
22955
+ });
22956
+ }
22957
+ };
22958
+
22959
+ // dist/gateway-session-transport.js
22960
+ var hermesGatewayThinkingOptions = [
22961
+ "none",
22962
+ "minimal",
22963
+ "low",
22964
+ "medium",
22965
+ "high",
22966
+ "xhigh",
22967
+ "max",
22968
+ "ultra"
22969
+ ].map((id) => ({ id: harnessThinkingOptionIdSchema.parse(id), label: id }));
22970
+ function question(entry, fallbackId) {
22971
+ const id = gatewayString(entry.qid) || fallbackId;
22972
+ const prompt = gatewayString(entry.question);
22973
+ if (!prompt)
22974
+ throw new Error("Hermes clarify request has no question");
22975
+ const choices = Array.isArray(entry.choices) ? entry.choices.filter((choice) => typeof choice === "string") : [];
22976
+ return choices.length ? {
22977
+ id,
22978
+ type: "choice",
22979
+ prompt,
22980
+ options: choices.map((value) => ({ value, label: value })),
22981
+ multiple: entry.multi_select === true,
22982
+ allowOther: true,
22983
+ optional: false
22984
+ } : { id, type: "text", prompt, multiline: true, secret: false, optional: false };
22985
+ }
22986
+ function gatewayDiff(text2) {
22987
+ if (!text2 || text2.length > 1024 * 1024)
22988
+ return [];
22989
+ const result = [];
22990
+ let path7 = "";
22991
+ let inHunk = false;
22992
+ let oldLines = [], newLines = [];
22993
+ const flush = () => {
22994
+ if (path7)
22995
+ result.push({
22996
+ type: "diff",
22997
+ path: path7,
22998
+ oldText: oldLines.join("\n"),
22999
+ newText: newLines.join("\n")
23000
+ });
23001
+ oldLines = [];
23002
+ newLines = [];
23003
+ };
23004
+ for (const raw of text2.split("\n")) {
23005
+ const line = raw.replace(/\u001b\[[0-9;]*m/gu, "");
23006
+ const header = /^(a\/.+|\/dev\/null) → (b\/.+|\/dev\/null)$/u.exec(line);
23007
+ if (header) {
23008
+ flush();
23009
+ inHunk = false;
23010
+ path7 = (header[2] === "/dev/null" ? header[1] ?? "" : header[2] ?? "").replace(/^[ab]\//u, "");
23011
+ continue;
23012
+ }
23013
+ if (line.startsWith("--- ") && !inHunk) {
23014
+ flush();
23015
+ path7 = line.slice(4).replace(/^a\//u, "");
23016
+ } else if (line.startsWith("+++ ") && !inHunk) {
23017
+ const next = line.slice(4);
23018
+ if (next !== "/dev/null")
23019
+ path7 = next.replace(/^b\//u, "");
23020
+ } else if (line.startsWith("@@")) {
23021
+ inHunk = true;
23022
+ continue;
23023
+ } else if (path7 && line.startsWith("-"))
23024
+ oldLines.push(line.slice(1));
23025
+ else if (path7 && line.startsWith("+"))
23026
+ newLines.push(line.slice(1));
23027
+ else if (path7 && line.startsWith(" ")) {
23028
+ oldLines.push(line.slice(1));
23029
+ newLines.push(line.slice(1));
23030
+ }
23031
+ }
23032
+ flush();
23033
+ return result;
23034
+ }
23035
+ var HermesGatewaySessionTransport = class {
23036
+ transport;
23037
+ sessionId;
23038
+ nativeSessionId;
23039
+ onFault = () => void 0;
23040
+ availableCommands = HERMES_GATEWAY_COMMANDS;
23041
+ history;
23042
+ #active = null;
23043
+ #requests = /* @__PURE__ */ new Map();
23044
+ #closed = false;
23045
+ #info;
23046
+ constructor(transport, sessionId, nativeSessionId, info) {
23047
+ this.transport = transport;
23048
+ this.sessionId = sessionId;
23049
+ this.nativeSessionId = nativeSessionId;
23050
+ this.#info = info;
23051
+ this.history = new HermesGatewayHistory({
23052
+ python: transport.python,
23053
+ cwd: transport.cwd,
23054
+ environment: transport.environment,
23055
+ nativeSessionId
23056
+ });
23057
+ transport.onEvent = (event) => this.#event(event);
23058
+ transport.onRequest = (id, method, params) => {
23059
+ void this.#request(id, method, params).catch((error51) => this.#fault(error51));
23060
+ };
23061
+ transport.onFault = (error51) => this.#fault(error51);
23062
+ }
23063
+ async openResult() {
23064
+ const reasoning = await this.transport.request("config.get", {
23065
+ key: "reasoning",
23066
+ session_id: this.sessionId
23067
+ });
23068
+ const currentThinkingOptionId = gatewayString(reasoning.value);
23069
+ const model = gatewayString(this.#info.model);
23070
+ const provider = gatewayString(this.#info.provider);
23071
+ const currentModelId = provider ? `${provider}:${model}` : model;
23072
+ return {
23073
+ initialize: { protocolVersion: 1 },
23074
+ sessionId: this.nativeSessionId,
23075
+ replay: [],
23076
+ session: {
23077
+ sessionId: this.nativeSessionId,
23078
+ models: currentModelId ? {
23079
+ currentModelId,
23080
+ availableModels: [
23081
+ { modelId: currentModelId, name: provider ? `${provider} / ${model}` : model }
23082
+ ]
23083
+ } : null,
23084
+ modes: {
23085
+ currentModeId: this.#info.yolo === true ? "dont_ask" : "default",
23086
+ availableModes: [
23087
+ { id: "default", name: "Native approval policy" },
23088
+ { id: "dont_ask", name: "Session YOLO" }
23089
+ ]
23090
+ },
23091
+ thinkingOptions: hermesGatewayThinkingOptions,
23092
+ ...hermesGatewayThinkingOptions.some(({ id }) => id === currentThinkingOptionId) ? { currentThinkingOptionId } : {}
23093
+ }
23094
+ };
23095
+ }
23096
+ async readNativeSnapshot() {
23097
+ return this.history.readSnapshot();
23098
+ }
23099
+ async setThinking(optionId) {
23100
+ if (!hermesGatewayThinkingOptions.some(({ id }) => id === optionId))
23101
+ throw new Error("Unsupported Hermes reasoning effort");
23102
+ const result = await this.transport.request("config.set", {
23103
+ key: "reasoning",
23104
+ value: optionId,
23105
+ scope: "session",
23106
+ session_id: this.sessionId
23107
+ });
23108
+ if (result.value !== optionId || (await this.transport.request("config.get", { key: "reasoning", session_id: this.sessionId })).value !== optionId)
23109
+ throw new Error("Hermes did not confirm reasoning selection");
23110
+ return optionId;
23111
+ }
23112
+ async setModel(modelId) {
23113
+ if (/\s/u.test(modelId) || modelId.startsWith("-"))
23114
+ throw new Error("Invalid Hermes Model identifier");
23115
+ const result = await this.transport.request("config.set", {
23116
+ key: "model",
23117
+ value: modelId,
23118
+ scope: "session",
23119
+ session_id: this.sessionId
23120
+ });
23121
+ if (result.confirm_required === true)
23122
+ throw new Error(gatewayString(result.confirm_message) || "Hermes requires confirmation for this Model");
23123
+ const actualModel = gatewayString(this.#info.model);
23124
+ const actualProvider = gatewayString(this.#info.provider);
23125
+ if (modelId !== actualModel && modelId !== `${actualProvider}:${actualModel}`)
23126
+ throw new Error("Hermes did not confirm the requested Model");
23127
+ }
23128
+ async setPermissionMode(modeId) {
23129
+ if (modeId !== "default" && modeId !== "dont_ask")
23130
+ throw new Error("Hermes gateway has no accept_edits mode");
23131
+ const result = await this.transport.request("config.set", {
23132
+ key: "yolo",
23133
+ value: modeId === "dont_ask" ? "on" : "off",
23134
+ scope: "session",
23135
+ session_id: this.sessionId
23136
+ });
23137
+ if (result.value !== (modeId === "dont_ask" ? "1" : "0") || this.#info.yolo !== (modeId === "dont_ask"))
23138
+ throw new Error("Hermes effective approval policy differs from the requested mode; check native global approvals policy");
23139
+ }
23140
+ nativeCommandName(text2) {
23141
+ const commandText = text2.trim();
23142
+ if (/^\/compress(?:\s|$)/iu.test(commandText))
23143
+ return "compress";
23144
+ return /^\/(help|tools|context|version)$/iu.exec(commandText)?.[1]?.toLowerCase() ?? null;
23145
+ }
23146
+ async runTurn(text2, emit, permission, question2) {
23147
+ if (this.#active || this.#closed)
23148
+ throw new Error("Hermes gateway Session is unavailable");
23149
+ let resolveResult = () => void 0;
23150
+ let rejectResult = () => void 0;
23151
+ const result = new Promise((resolve, reject) => {
23152
+ resolveResult = resolve;
23153
+ rejectResult = reject;
23154
+ });
23155
+ void result.catch(() => void 0);
23156
+ const active = {
23157
+ emit,
23158
+ permission,
23159
+ ...question2 ? { question: question2 } : {},
23160
+ resolve: resolveResult,
23161
+ reject: rejectResult,
23162
+ streamed: "",
23163
+ reasoning: "",
23164
+ toolIds: /* @__PURE__ */ new Set()
23165
+ };
23166
+ this.#active = active;
23167
+ const commandText = text2.trim();
23168
+ const nativeCommand = this.nativeCommandName(text2);
23169
+ try {
23170
+ if (nativeCommand === "compress") {
23171
+ const compact = await this.transport.request("session.compress", { session_id: this.sessionId, focus_topic: commandText.slice(9).trim() }, 3e5);
23172
+ const summary = gatewayRecord(compact.summary);
23173
+ const explanation = [summary.headline, summary.token_line, summary.note, compact.message].filter((value) => typeof value === "string" && !!value).join("\n");
23174
+ if (explanation)
23175
+ emit({ type: "agent.text", text: explanation });
23176
+ if (compact.status === "pending") {
23177
+ this.#fault(new Error("Hermes compression remains pending; Session closed to prevent overlapping native work"));
23178
+ return await result;
23179
+ }
23180
+ if (!["compressed", "aborted"].includes(String(compact.status)) && compact.lock_held !== true)
23181
+ throw new Error("Hermes returned an unrecognized compression outcome");
23182
+ this.#usage(gatewayRecord(compact.usage));
23183
+ const succeeded = compact.status === "compressed" && summary.noop !== true;
23184
+ active.resolve({
23185
+ stopReason: "end_turn",
23186
+ compactionOutcome: compact.status === "aborted" || summary.aborted === true ? {
23187
+ status: "failed",
23188
+ error: {
23189
+ code: "nativeFailure",
23190
+ message: explanation || "Hermes summary generation failed",
23191
+ retryable: true
23192
+ }
23193
+ } : succeeded ? { status: "succeeded" } : { status: "cancelled", reason: explanation || "Hermes did not compact context" }
23194
+ });
23195
+ } else if (nativeCommand) {
23196
+ const response = await this.transport.request("slash.exec", {
23197
+ session_id: this.sessionId,
23198
+ command: `/${nativeCommand}`
23199
+ });
23200
+ emit({ type: "agent.text", text: gatewayString(response.output) });
23201
+ active.resolve({ stopReason: "end_turn" });
23202
+ } else {
23203
+ const submitted = await this.transport.request("prompt.submit", {
23204
+ session_id: this.sessionId,
23205
+ text: text2
23206
+ });
23207
+ if (submitted.status !== "streaming")
23208
+ this.#fault(new Error(`Hermes did not start an exclusive turn (${String(submitted.status)})`));
23209
+ }
23210
+ } catch (error51) {
23211
+ active.reject(error51 instanceof Error ? error51 : new Error(String(error51)));
23212
+ }
23213
+ try {
23214
+ return await result;
23215
+ } finally {
23216
+ if (this.#active === active)
23217
+ this.#active = null;
23218
+ for (const controller of this.#requests.values())
23219
+ controller.abort("cancelled");
23220
+ this.#requests.clear();
23221
+ }
23222
+ }
23223
+ async cancel() {
23224
+ await this.transport.request("session.interrupt", { session_id: this.sessionId });
23225
+ }
23226
+ async close() {
23227
+ if (this.#closed)
23228
+ return;
23229
+ this.#closed = true;
23230
+ for (const controller of this.#requests.values())
23231
+ controller.abort("cancelled");
23232
+ this.#requests.clear();
23233
+ this.#active?.reject(new Error("Hermes gateway Session closed"));
23234
+ try {
23235
+ await this.transport.request("session.close", { session_id: this.sessionId });
23236
+ } catch {
23237
+ }
23238
+ await this.transport.close();
23239
+ }
23240
+ #fault(error51) {
23241
+ if (this.#closed)
23242
+ return;
23243
+ this.#closed = true;
23244
+ for (const controller of this.#requests.values())
23245
+ controller.abort("cancelled");
23246
+ this.#requests.clear();
23247
+ const fault = new HermesTransportError("protocolError", error51 instanceof Error ? error51.message : String(error51));
23248
+ this.#active?.reject(fault);
23249
+ this.onFault(fault);
23250
+ void this.transport.close();
23251
+ }
23252
+ #event(event) {
23253
+ if (event.session_id !== this.sessionId || this.#closed)
23254
+ return;
23255
+ const payload = gatewayRecord(event.payload);
23256
+ if (event.type === "session.info") {
23257
+ this.#info = payload;
23258
+ return;
23259
+ }
23260
+ if (event.type === "request.cancel") {
23261
+ const controller = this.#requests.get(gatewayString(payload.id));
23262
+ controller?.abort(payload.reason === "timeout" ? "expired" : "cancelled");
23263
+ return;
23264
+ }
23265
+ const active = this.#active;
23266
+ if (!active) {
23267
+ if (event.type === "error")
23268
+ this.#fault(new Error(gatewayString(payload.message)));
23269
+ return;
23270
+ }
23271
+ const text2 = gatewayString(payload.text);
23272
+ if (event.type === "message.delta") {
23273
+ active.streamed += text2;
23274
+ active.emit({ type: "agent.text", text: text2 });
23275
+ } else if (event.type === "reasoning.delta" || event.type === "reasoning.available") {
23276
+ if (event.type === "reasoning.delta" || !active.reasoning) {
23277
+ active.reasoning += text2;
23278
+ active.emit({ type: "agent.thought", text: text2 });
23279
+ }
23280
+ } else if (event.type === "message.interim") {
23281
+ if (payload.already_streamed !== true)
23282
+ active.emit({ type: "agent.text", text: text2 });
23283
+ active.streamed = "";
23284
+ active.reasoning = "";
23285
+ } else if (event.type === "session.usage")
23286
+ this.#usage(gatewayRecord(payload.usage));
23287
+ else if (event.type === "tool.start" || event.type === "tool.complete") {
23288
+ const toolCallId = gatewayString(payload.tool_id);
23289
+ if (!toolCallId)
23290
+ return;
23291
+ const result = gatewayRecord(payload.result);
23292
+ const output = payload.result !== void 0 ? typeof payload.result === "string" ? payload.result : JSON.stringify(payload.result) : gatewayString(payload.result_text) || gatewayString(payload.summary);
23293
+ const content = [
23294
+ ...output ? [{ type: "content", content: { type: "text", text: output } }] : [],
23295
+ ...gatewayDiff(gatewayString(payload.inline_diff))
23296
+ ];
23297
+ if (event.type === "tool.complete" && !active.toolIds.has(toolCallId))
23298
+ active.emit({
23299
+ type: "tool.call",
23300
+ toolCallId,
23301
+ update: {
23302
+ sessionUpdate: "tool_call",
23303
+ toolCallId,
23304
+ title: gatewayString(payload.name),
23305
+ rawInput: payload.args,
23306
+ status: "in_progress"
23307
+ }
23308
+ });
23309
+ active.toolIds.add(toolCallId);
23310
+ active.emit(event.type === "tool.start" ? {
23311
+ type: "tool.call",
23312
+ toolCallId,
23313
+ update: {
23314
+ sessionUpdate: "tool_call",
23315
+ toolCallId,
23316
+ title: gatewayString(payload.name),
23317
+ rawInput: payload.args,
23318
+ status: "in_progress"
23319
+ }
23320
+ } : {
23321
+ type: "tool.update",
23322
+ toolCallId,
23323
+ update: {
23324
+ sessionUpdate: "tool_call_update",
23325
+ toolCallId,
23326
+ status: result.error || result.is_error === true || result.success === false ? "failed" : "completed",
23327
+ content
23328
+ }
23329
+ });
23330
+ } else if (event.type === "message.complete") {
23331
+ if (!active.streamed)
23332
+ active.emit({ type: "agent.text", text: text2 });
23333
+ else if (text2.startsWith(active.streamed) && text2.length > active.streamed.length)
23334
+ active.emit({ type: "agent.text", text: text2.slice(active.streamed.length) });
23335
+ const reasoning = gatewayString(payload.reasoning);
23336
+ if (reasoning && !active.reasoning)
23337
+ active.emit({ type: "agent.thought", text: reasoning });
23338
+ else if (reasoning.startsWith(active.reasoning) && reasoning.length > active.reasoning.length)
23339
+ active.emit({ type: "agent.thought", text: reasoning.slice(active.reasoning.length) });
23340
+ const usage = gatewayRecord(payload.usage);
23341
+ this.#usage(usage);
23342
+ if (payload.status === "error")
23343
+ active.reject(new Error(gatewayString(payload.error) || gatewayString(payload.failure_reason) || "Hermes turn failed"));
23344
+ else
23345
+ active.resolve({
23346
+ stopReason: payload.status === "interrupted" ? "cancelled" : "end_turn",
23347
+ usage: {
23348
+ inputTokens: Number(usage.input) || 0,
23349
+ outputTokens: Number(usage.output) || 0,
23350
+ totalTokens: Number(usage.total) || 0,
23351
+ ...typeof usage.reasoning === "number" ? { thoughtTokens: usage.reasoning } : {}
23352
+ }
23353
+ });
23354
+ } else if (event.type === "error")
23355
+ active.reject(new Error(gatewayString(payload.message) || "Hermes gateway failed"));
23356
+ }
23357
+ #usage(usage) {
23358
+ this.#active?.emit({
23359
+ type: "usage",
23360
+ ...typeof usage.context_used === "number" ? { used: usage.context_used } : {},
23361
+ ...typeof usage.context_max === "number" ? { size: usage.context_max } : {}
23362
+ });
23363
+ }
23364
+ async #request(id, method, params) {
23365
+ const active = this.#active;
23366
+ if (!active || params.session_id !== this.sessionId || !["clarify", "approval"].includes(method)) {
23367
+ this.transport.rejectRequest(id);
23368
+ return;
23369
+ }
23370
+ const controller = new AbortController();
23371
+ this.#requests.set(id, controller);
23372
+ try {
23373
+ if (method === "clarify" && active.question) {
23374
+ const batch = Array.isArray(params.questions);
23375
+ const questions = batch ? params.questions.map((value, index) => question(gatewayRecord(value), String(index))) : [question(params, "answer")];
23376
+ const response = await active.question({
23377
+ title: "Hermes",
23378
+ questions,
23379
+ signal: controller.signal
23380
+ });
23381
+ if (!controller.signal.aborted) {
23382
+ const answers = Object.fromEntries(Object.entries(response.answers).map(([key, values]) => {
23383
+ const q = questions.find((entry) => entry.id === key);
23384
+ return [
23385
+ key,
23386
+ q?.type === "choice" && q.multiple ? JSON.stringify(values) : values[0] ?? ""
23387
+ ];
23388
+ }));
23389
+ this.transport.respond(id, response.cancelled ? {} : batch ? { answers } : { answer: answers.answer ?? "" });
23390
+ }
23391
+ } else if (method === "approval") {
23392
+ const choices = Array.isArray(params.choices) ? params.choices : ["once", "deny"];
23393
+ const options = choices.flatMap((choice) => choice === "once" ? [{ optionId: "once", kind: "allow_once", name: "Allow once" }] : choice === "session" || choice === "always" ? [
23394
+ {
23395
+ optionId: choice,
23396
+ kind: "allow_always",
23397
+ name: choice === "session" ? "Allow for this session" : "Always allow"
23398
+ }
23399
+ ] : choice === "deny" ? [{ optionId: "deny", kind: "reject_once", name: "Deny" }] : []);
23400
+ const response = await active.permission({
23401
+ signal: controller.signal,
23402
+ effects: { session: "allowForSession", always: "allowAlways" },
23403
+ description: gatewayString(params.command) || gatewayString(params.description),
23404
+ request: {
23405
+ sessionId: this.nativeSessionId,
23406
+ toolCall: {
23407
+ toolCallId: gatewayString(params.request_id),
23408
+ title: gatewayString(params.description) || gatewayString(params.command)
23409
+ },
23410
+ options
23411
+ },
23412
+ options
23413
+ });
23414
+ if (!controller.signal.aborted)
23415
+ this.transport.respond(id, {
23416
+ choice: response.outcome.outcome === "selected" ? response.outcome.optionId : "deny"
23417
+ });
23418
+ } else
23419
+ this.transport.rejectRequest(id);
23420
+ } finally {
23421
+ this.#requests.delete(id);
23422
+ }
23423
+ }
23424
+ };
23425
+
23426
+ // dist/gateway-open.js
23427
+ import path6 from "node:path";
23428
+ import { realpath } from "node:fs/promises";
23429
+
23430
+ // dist/gateway-configuration.js
23431
+ import { execFile } from "node:child_process";
23432
+ import { promisify } from "node:util";
23433
+ async function resolveGatewayModel(transport, modelId) {
23434
+ if (/\s/u.test(modelId) || modelId.startsWith("-"))
23435
+ throw new Error("Invalid Hermes Model identifier");
23436
+ const result = await promisify(execFile)(transport.python, [
23437
+ "-I",
23438
+ "-c",
23439
+ "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}))",
23440
+ modelId
23441
+ ], {
23442
+ cwd: transport.cwd,
23443
+ env: { ...process.env, ...transport.environment },
23444
+ timeout: 2e4,
23445
+ maxBuffer: 1024 * 1024
23446
+ });
23447
+ const value = JSON.parse(result.stdout.trim());
23448
+ if (!value || typeof value !== "object" || !("provider" in value) || !("model" in value) || typeof value.provider !== "string" || typeof value.model !== "string")
23449
+ throw new Error("Hermes returned an invalid Model choice");
23450
+ return { provider: value.provider, model: value.model };
23451
+ }
23452
+
23453
+ // dist/gateway-open.js
23454
+ async function sameDirectory(left, right) {
23455
+ return await realpath(left).catch(() => path6.resolve(left)) === await realpath(right).catch(() => path6.resolve(right));
23456
+ }
23457
+ var gatewayCapabilities = {
23458
+ configuration: {
23459
+ selectModel: true,
23460
+ selectThinkingOption: true,
23461
+ selectPermissionMode: true,
23462
+ permissionModeScope: "live"
23463
+ },
23464
+ history: { fork: true, forkAcrossCwd: false, rollbackLastTurn: true }
23465
+ };
23466
+ function gatewayPermissionModes() {
23467
+ return harnessPermissionModeCatalogSchema.parse({
23468
+ modes: [
23469
+ {
23470
+ id: "default",
23471
+ label: "原生审批策略",
23472
+ description: "遵循 Hermes 全局审批策略,关闭本会话 YOLO。"
23473
+ },
23474
+ {
23475
+ id: "dont_ask",
23476
+ label: "会话 YOLO",
23477
+ description: "本会话自动批准 Hermes 工具操作。",
23478
+ dangerous: true
23479
+ }
23480
+ ],
23481
+ defaultModeId: "default"
23482
+ });
23483
+ }
23484
+ function isGatewayRef(ref) {
23485
+ return ref.harnessId === "hermes" && gatewayRecord(ref.locator).transport === "gateway";
23486
+ }
23487
+ async function openGatewaySession(input, transport, onSettle, inheritedPermissionMode) {
23488
+ const source = input.kind === "create" ? null : input.kind === "resume" ? input.nativeRef : input.sourceRef;
23489
+ if (source && !isGatewayRef(source))
23490
+ throw new HermesGatewayHistoryError("unsupported", "This Hermes native reference is not a supported gateway Session");
23491
+ const savedMode = source ? gatewayRecord(source.locator).permissionModeId : void 0;
23492
+ const mode = (input.kind === "fork" ? inheritedPermissionMode : input.permissionModeId) ?? (typeof savedMode === "string" ? savedMode : void 0);
23493
+ if (mode && mode !== "default" && mode !== "dont_ask")
23494
+ throw new HermesGatewayHistoryError("unsupported", "Hermes gateway supports native policy and session YOLO; accept_edits remains ACP-only");
23495
+ if (input.kind === "create" && input.executionPolicy === "unattended-full-access" && mode && mode !== "dont_ask")
23496
+ throw new HermesGatewayHistoryError("invalidRequest", "unattended-full-access requires dont_ask");
23497
+ const model = input.kind !== "fork" && input.model ? decodeHermesModelRefId(input.model.id) : null;
23498
+ if (input.kind !== "fork" && input.model && !model)
23499
+ throw new HermesGatewayHistoryError("invalidRequest", "Model Ref does not belong to Hermes");
23500
+ if (source && gatewayRecord(source.locator).cwd && !await sameDirectory(String(gatewayRecord(source?.locator).cwd), input.cwd))
23501
+ throw new HermesGatewayHistoryError("unsupported", "Hermes gateway preserves the native Session working directory");
23502
+ let nativeRef = source;
23503
+ let sourceHistory;
23504
+ try {
23505
+ await transport.prepareSession();
23506
+ await transport.start();
23507
+ if (source && (input.kind === "fork" || input.kind === "rollbackLastTurn")) {
23508
+ sourceHistory = new HermesGatewayHistory({
23509
+ python: transport.python,
23510
+ cwd: transport.cwd,
23511
+ environment: transport.environment,
23512
+ nativeSessionId: source.nativeSessionId
23513
+ });
23514
+ nativeRef = await sourceHistory.derive(input.kind === "fork" ? { checkpoint: input.checkpoint } : { rollbackLastTurn: true });
23515
+ }
23516
+ const choice = model ? await resolveGatewayModel(transport, model) : null;
23517
+ const response = await transport.request(nativeRef ? "session.resume" : "session.create", nativeRef ? { session_id: nativeRef.nativeSessionId, eager_build: true, omit_messages: true } : {
23518
+ cwd: input.cwd,
23519
+ close_on_disconnect: true,
23520
+ ...choice ? choice : {},
23521
+ ...input.kind === "create" && input.thinkingOptionId ? { reasoning_effort: input.thinkingOptionId } : {}
23522
+ });
23523
+ const runtimeId = gatewayString(response.session_id);
23524
+ const storedId = nativeRef?.nativeSessionId || gatewayString(response.stored_session_id);
23525
+ if (!runtimeId || !storedId)
23526
+ throw new Error("Hermes gateway did not return Session identity");
23527
+ let info = gatewayRecord(response.info);
23528
+ if (info.lazy === true)
23529
+ info = await transport.waitForSession(runtimeId);
23530
+ const nativeCwd = gatewayString(info.cwd);
23531
+ if (nativeCwd && !await sameDirectory(nativeCwd, input.cwd))
23532
+ throw new HermesGatewayHistoryError("unsupported", "Hermes resumed a different native working directory");
23533
+ const bridge = new HermesGatewaySessionTransport(transport, runtimeId, storedId, info);
23534
+ if (!nativeRef)
23535
+ await bridge.history.ensureCreated({
23536
+ cwd: input.cwd,
23537
+ model: gatewayString(info.model),
23538
+ provider: gatewayString(info.provider),
23539
+ reasoningEffort: gatewayString(info.reasoning_effort)
23540
+ });
23541
+ const physicalId = await bridge.history.resolvePhysicalSessionId();
23542
+ if (gatewayString(info.stored_session_id) !== physicalId)
23543
+ throw new Error("Hermes live Session and persisted history identities differ");
23544
+ if (model)
23545
+ await bridge.setModel(model);
23546
+ if (input.kind !== "fork" && input.thinkingOptionId)
23547
+ await bridge.setThinking(input.thinkingOptionId);
23548
+ const desiredMode = input.kind === "create" && input.executionPolicy === "unattended-full-access" ? "dont_ask" : mode;
23549
+ if (desiredMode)
23550
+ await bridge.setPermissionMode(desiredMode);
23551
+ const open = await bridge.openResult();
23552
+ return new HermesSession({
23553
+ nativeRef: nativeSessionRefSchema.parse({
23554
+ harnessId: "hermes",
23555
+ nativeSessionId: storedId,
23556
+ formatVersion: 1,
23557
+ locator: {
23558
+ transport: "gateway",
23559
+ cwd: input.cwd,
23560
+ permissionModeId: open.session.modes?.currentModeId
23561
+ }
23562
+ }),
23563
+ transport: bridge,
23564
+ open,
23565
+ supportsDerivation: true,
23566
+ onSettle
23567
+ });
23568
+ } catch (error51) {
23569
+ await transport.close().catch(() => void 0);
23570
+ if (sourceHistory && nativeRef)
23571
+ await sourceHistory.discardDerived(nativeRef).catch(() => false);
23572
+ throw error51;
23573
+ }
23574
+ }
23575
+
21311
23576
  // dist/hermes-adapter.js
21312
23577
  var hermesHarnessId = harnessIdSchema.parse("hermes");
21313
23578
  var IMPORT_TIMEOUT_MS = 2e4;
21314
23579
  var HERMES_THREAD_ID_ENV = "CODEXHOST_THREAD_ID";
21315
23580
  var HermesAdapter = class {
23581
+ commandCatalog = HERMES_COMMAND_CATALOG;
21316
23582
  harnessId = hermesHarnessId;
21317
23583
  sessionImport = {
21318
23584
  listCandidates: () => this.#listImportCandidates(),
@@ -21320,11 +23586,16 @@ var HermesAdapter = class {
21320
23586
  };
21321
23587
  #options;
21322
23588
  #inspectionCache = null;
23589
+ #lastInventory = null;
23590
+ #inventoryRead = null;
21323
23591
  #inspectionCacheScope = null;
21324
23592
  #sessions = /* @__PURE__ */ new Set();
21325
23593
  #warmTransports = /* @__PURE__ */ new Map();
21326
23594
  #transports = /* @__PURE__ */ new Set();
21327
23595
  #closed = false;
23596
+ #gatewayTransports = /* @__PURE__ */ new Set();
23597
+ #gatewayProbes = /* @__PURE__ */ new Map();
23598
+ #openingNativeIds = /* @__PURE__ */ new Set();
21328
23599
  constructor(options = {}) {
21329
23600
  this.#options = options;
21330
23601
  }
@@ -21334,6 +23605,29 @@ var HermesAdapter = class {
21334
23605
  return this.#inspectionCache;
21335
23606
  }
21336
23607
  const environment = this.#effectiveEnvironment();
23608
+ if (input.refresh)
23609
+ this.#gatewayProbes.clear();
23610
+ const gatewayPython = await this.#gatewayPython(cwd, environment);
23611
+ if (gatewayPython) {
23612
+ try {
23613
+ const catalog = catalogModelsFromInventory(await this.#readInventory());
23614
+ const inspection = {
23615
+ status: "ready",
23616
+ catalog: {
23617
+ models: catalog.models.map(({ ref, label }) => ({ ref, label })),
23618
+ thinkingOptions: hermesGatewayThinkingOptions,
23619
+ ...catalog.defaultModel ? { defaultModel: catalog.defaultModel } : {}
23620
+ },
23621
+ permissionModes: gatewayPermissionModes(),
23622
+ capabilities: gatewayCapabilities
23623
+ };
23624
+ this.#inspectionCache = inspection;
23625
+ this.#inspectionCacheScope = cwd;
23626
+ return inspection;
23627
+ } catch (error51) {
23628
+ return inspectionFromTransportError(error51);
23629
+ }
23630
+ }
21337
23631
  const transport = await this.#takeTransport(cwd, environment);
21338
23632
  let retainedForOpen = false;
21339
23633
  try {
@@ -21378,13 +23672,26 @@ var HermesAdapter = class {
21378
23672
  }
21379
23673
  }
21380
23674
  async #readInventory() {
23675
+ if (this.#inventoryRead)
23676
+ return this.#inventoryRead;
21381
23677
  const executable = resolveHermesExecutable({
21382
23678
  ...this.#options.command ? { command: this.#options.command } : {},
21383
23679
  ...this.#options.environment ? { environment: this.#options.environment } : {}
21384
23680
  });
21385
- return readHermesModelInventory(executable, 2e4, {
23681
+ this.#inventoryRead = readHermesModelInventory(executable, 2e4, {
21386
23682
  ...this.#options.environment ? { environment: this.#options.environment } : {}
23683
+ }).then((inventory) => {
23684
+ if (!this.#closed)
23685
+ this.#lastInventory = inventory;
23686
+ return inventory;
23687
+ }).catch((error51) => {
23688
+ if (error51 instanceof HermesInventoryTimeoutError && this.#lastInventory)
23689
+ return this.#lastInventory;
23690
+ throw error51;
23691
+ }).finally(() => {
23692
+ this.#inventoryRead = null;
21387
23693
  });
23694
+ return this.#inventoryRead;
21388
23695
  }
21389
23696
  async open(input) {
21390
23697
  if (this.#closed) {
@@ -21394,6 +23701,23 @@ var HermesAdapter = class {
21394
23701
  if (typeof cwd !== "string" || cwd.trim().length === 0) {
21395
23702
  return failure("invalidRequest", "open requires a cwd");
21396
23703
  }
23704
+ const nativeRef = input.kind === "create" ? null : input.kind === "resume" ? input.nativeRef : input.sourceRef;
23705
+ const gatewayEnvironment = {
23706
+ ...this.#options.environment ?? process.env,
23707
+ ...input.environment ?? {}
23708
+ };
23709
+ const environment = this.#effectiveEnvironment(input.environment);
23710
+ if (input.kind === "resume" && (this.#openingNativeIds.has(input.nativeRef.nativeSessionId) || [...this.#sessions].some((s) => s.initialState.nativeRef?.nativeSessionId === input.nativeRef.nativeSessionId)))
23711
+ return failure("sessionBusy", "This Hermes Session already has an owner", true);
23712
+ if (!nativeRef || isGatewayRef(nativeRef)) {
23713
+ const python = await this.#gatewayPython(cwd, gatewayEnvironment);
23714
+ if (this.#closed)
23715
+ return failure("invalidState", "Hermes Adapter is closed");
23716
+ if (python)
23717
+ return this.#openGateway(input, python, gatewayEnvironment);
23718
+ if (nativeRef)
23719
+ return failure("unavailable", "Hermes gateway Session requires an available gateway with exclusive turn support");
23720
+ }
21397
23721
  let transportOpen;
21398
23722
  let permissionModeId;
21399
23723
  if (input.kind === "create") {
@@ -21417,7 +23741,6 @@ var HermesAdapter = class {
21417
23741
  if (permissionModeId && !isHermesModeId(permissionModeId)) {
21418
23742
  return failure("invalidRequest", "Permission Mode does not belong to Hermes");
21419
23743
  }
21420
- const environment = this.#effectiveEnvironment(input.environment);
21421
23744
  const transport = await this.#takeTransport(cwd, environment);
21422
23745
  if (this.#closed) {
21423
23746
  await this.#releaseTransport(transport);
@@ -21493,16 +23816,80 @@ var HermesAdapter = class {
21493
23816
  return failure("nativeFailure", error51 instanceof Error ? error51.message : "Hermes Session open failed");
21494
23817
  }
21495
23818
  }
23819
+ async #gatewayPython(cwd, environment) {
23820
+ const key = `${cwd}:${this.#transportScope(cwd, environment)}`;
23821
+ let pending = this.#gatewayProbes.get(key);
23822
+ if (!pending) {
23823
+ pending = (async () => {
23824
+ try {
23825
+ return await HermesGatewayTransport.probe(resolveHermesExecutable({
23826
+ ...this.#options.command ? { command: this.#options.command } : {},
23827
+ environment
23828
+ }), cwd, environment);
23829
+ } catch {
23830
+ return null;
23831
+ }
23832
+ })();
23833
+ this.#gatewayProbes.set(key, pending);
23834
+ }
23835
+ return pending;
23836
+ }
23837
+ async #openGateway(input, python, environment) {
23838
+ if (this.#closed)
23839
+ return failure("invalidState", "Hermes Adapter is closed");
23840
+ const ref = input.kind === "resume" ? input.nativeRef : null;
23841
+ if (input.kind === "fork" || input.kind === "rollbackLastTurn") {
23842
+ const source = [...this.#sessions].find((s) => s.initialState.nativeRef?.nativeSessionId === input.sourceRef.nativeSessionId);
23843
+ if (source?.busy)
23844
+ return failure("sessionBusy", "Cannot derive a Hermes Session while its source Turn is active", true);
23845
+ }
23846
+ const owned = (id) => this.#openingNativeIds.has(id) || [...this.#sessions].some((session) => session.initialState.nativeRef?.nativeSessionId === id);
23847
+ if (ref && owned(ref.nativeSessionId))
23848
+ return failure("sessionBusy", "This Hermes Session already has an owner", true);
23849
+ if (ref)
23850
+ this.#openingNativeIds.add(ref.nativeSessionId);
23851
+ const transport = new HermesGatewayTransport(python, input.cwd, environment, this.#options.commandTimeoutMs);
23852
+ this.#gatewayTransports.add(transport);
23853
+ try {
23854
+ let inheritedMode;
23855
+ if (input.kind === "fork") {
23856
+ const source = [...this.#sessions].find((s) => s.initialState.nativeRef?.nativeSessionId === input.sourceRef.nativeSessionId);
23857
+ const snapshot = await source?.readSnapshot();
23858
+ if (snapshot?.ok)
23859
+ inheritedMode = snapshot.value.state?.effectivePermissionModeId;
23860
+ }
23861
+ const session = await openGatewaySession(input, transport, (settled) => {
23862
+ this.#sessions.delete(settled);
23863
+ this.#gatewayTransports.delete(transport);
23864
+ }, inheritedMode);
23865
+ if (this.#closed) {
23866
+ await session.close();
23867
+ return failure("invalidState", "Hermes Adapter is closed");
23868
+ }
23869
+ this.#sessions.add(session);
23870
+ return { ok: true, value: session };
23871
+ } catch (error51) {
23872
+ await transport.close().catch(() => void 0);
23873
+ this.#gatewayTransports.delete(transport);
23874
+ return failure(error51 instanceof HermesGatewayHistoryError ? error51.code : "nativeFailure", error51 instanceof Error ? error51.message : "Hermes gateway open failed");
23875
+ } finally {
23876
+ if (ref)
23877
+ this.#openingNativeIds.delete(ref.nativeSessionId);
23878
+ }
23879
+ }
21496
23880
  async close() {
21497
23881
  if (this.#closed)
21498
23882
  return;
21499
23883
  this.#closed = true;
21500
23884
  this.#inspectionCache = null;
23885
+ this.#lastInventory = null;
21501
23886
  const sessions = [...this.#sessions];
21502
23887
  this.#sessions.clear();
21503
23888
  this.#warmTransports.clear();
21504
23889
  await Promise.all(sessions.map((session) => session.close().catch(() => void 0)));
21505
23890
  await Promise.all([...this.#transports].map((transport) => this.#releaseTransport(transport)));
23891
+ await Promise.all([...this.#gatewayTransports].map((transport) => transport.close()));
23892
+ this.#gatewayTransports.clear();
21506
23893
  }
21507
23894
  #effectiveEnvironment(environment) {
21508
23895
  const merged = { ...this.#options.environment ?? process.env, ...environment ?? {} };