@codexhost/cli-linux-arm64 0.9.1-test.2 → 0.9.1

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