@hermespilot/link 0.4.8 → 0.5.0

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.
@@ -2110,13 +2110,13 @@ async function ensureHermesApiServerConfigUnlocked(profileName = "default", conf
2110
2110
  const envHost = envOverrides.host?.trim() ? envOverrides.host : null;
2111
2111
  const configPort = readApiServerPort(configOnly.port);
2112
2112
  const envPort = readApiServerPort(envOverrides.port);
2113
- const desiredHost = configHost ?? envHost ?? DEFAULT_HERMES_API_SERVER_HOST;
2113
+ const desiredHost = envHost ?? configHost ?? DEFAULT_HERMES_API_SERVER_HOST;
2114
2114
  const desiredPort = await resolveDesiredApiServerPort({
2115
2115
  profileName,
2116
2116
  configPort,
2117
2117
  envPort
2118
2118
  });
2119
- const desiredKey = configKey ?? envKey ?? randomBytes(32).toString("base64url");
2119
+ const desiredKey = envKey ?? configKey ?? randomBytes(32).toString("base64url");
2120
2120
  let changed = false;
2121
2121
  let enabledAdded = false;
2122
2122
  let hostAdded = false;
@@ -2210,7 +2210,7 @@ async function repairHermesApiServerConfigUnlocked(profileName = "default", conf
2210
2210
  await readHermesApiServerEnvOverrides(profileName),
2211
2211
  true
2212
2212
  );
2213
- const freshPort = await nextProfileApiServerPort(profileName);
2213
+ const freshPort = profileName === "default" ? DEFAULT_HERMES_API_SERVER_PORT : await nextProfileApiServerPort(profileName);
2214
2214
  const freshKey = randomBytes(32).toString("base64url");
2215
2215
  apiServer.enabled = true;
2216
2216
  extra.host = DEFAULT_HERMES_API_SERVER_HOST;
@@ -2239,7 +2239,7 @@ async function repairHermesApiServerConfigUnlocked(profileName = "default", conf
2239
2239
  hostAdded: previous.host !== DEFAULT_HERMES_API_SERVER_HOST,
2240
2240
  portAdded: previous.port !== freshPort,
2241
2241
  backupPath,
2242
- notice: "\u5DF2\u4E3A Hermes API Server \u91CD\u65B0\u5206\u914D\u672C\u673A\u7AEF\u53E3\u5E76\u8F6E\u6362 key\uFF0C\u7528\u4E8E\u4FEE\u590D\u65E7 Gateway \u6216 key \u4E0D\u4E00\u81F4\u5BFC\u81F4\u7684 401\u3002"
2242
+ notice: profileName === "default" ? "\u5DF2\u4E3A Hermes API Server \u4FDD\u6301\u9ED8\u8BA4\u7AEF\u53E3\u5E76\u8F6E\u6362 key\uFF0C\u7528\u4E8E\u4FEE\u590D\u65E7 Gateway \u6216 key \u4E0D\u4E00\u81F4\u5BFC\u81F4\u7684 401\u3002" : "\u5DF2\u4E3A Hermes API Server \u91CD\u65B0\u5206\u914D\u672C\u673A\u7AEF\u53E3\u5E76\u8F6E\u6362 key\uFF0C\u7528\u4E8E\u4FEE\u590D\u65E7 Gateway \u6216 key \u4E0D\u4E00\u81F4\u5BFC\u81F4\u7684 401\u3002"
2243
2243
  };
2244
2244
  }
2245
2245
  async function readHermesConfigDocument(configPath) {
@@ -3579,16 +3579,14 @@ function readApiServerPort(value) {
3579
3579
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
3580
3580
  }
3581
3581
  async function resolveDesiredApiServerPort(input) {
3582
+ const effectivePort = input.envPort ?? input.configPort;
3582
3583
  if (shouldAssignDedicatedProfileApiServerPort(
3583
3584
  input.profileName,
3584
- input.configPort ?? void 0
3585
+ effectivePort ?? void 0
3585
3586
  )) {
3586
- if (input.profileName !== "default" && input.envPort !== null && input.envPort !== DEFAULT_HERMES_API_SERVER_PORT) {
3587
- return input.envPort;
3588
- }
3589
3587
  return nextProfileApiServerPort(input.profileName);
3590
3588
  }
3591
- return input.configPort ?? input.envPort ?? DEFAULT_HERMES_API_SERVER_PORT;
3589
+ return effectivePort ?? DEFAULT_HERMES_API_SERVER_PORT;
3592
3590
  }
3593
3591
  async function nextProfileApiServerPort(profileName) {
3594
3592
  const usedPorts = await readConfiguredApiServerPorts(profileName);
@@ -3877,6 +3875,9 @@ function buildNotice(flags) {
3877
3875
  if (flags.keyAdded) {
3878
3876
  fields.push("key");
3879
3877
  }
3878
+ if (fields.length === 0) {
3879
+ return "\u5DF2\u540C\u6B65 Hermes API Server config.yaml \u4E0E Profile .env \u7684\u6709\u6548\u914D\u7F6E\u3002";
3880
+ }
3880
3881
  return `\u5DF2\u4E3A Hermes API Server \u81EA\u52A8\u8865\u5145 ${fields.join("\u3001")}\uFF1B\u672A\u8986\u76D6\u5DF2\u6709 port/host/key\u3002`;
3881
3882
  }
3882
3883
  function toRecord(value) {
@@ -4133,7 +4134,7 @@ import os2 from "os";
4133
4134
  import path5 from "path";
4134
4135
 
4135
4136
  // src/constants.ts
4136
- var LINK_VERSION = "0.4.8";
4137
+ var LINK_VERSION = "0.5.0";
4137
4138
  var LINK_COMMAND = "hermeslink";
4138
4139
  var LINK_DEFAULT_PORT = 52379;
4139
4140
  var LINK_RUNTIME_DIR_NAME = ".hermeslink";
@@ -7746,7 +7747,7 @@ var ConversationMaintenanceCoordinator = class {
7746
7747
  hermesSessionIds,
7747
7748
  manifest.profile_name_snapshot ?? manifest.profile ?? "default"
7748
7749
  );
7749
- if (snapshot.runs.length > 0 && hermesDeleteResults.every((result) => result.status === "not_found")) {
7750
+ if (hermesDeleteResults.some((result) => result.status === "unknown")) {
7750
7751
  throw new LinkHttpError(
7751
7752
  502,
7752
7753
  "hermes_session_delete_not_confirmed",
@@ -13129,8 +13130,15 @@ function normalizeHermesResponseEvent(event) {
13129
13130
  return normalizeResponseCompleted(event);
13130
13131
  case "response.failed":
13131
13132
  return normalizeResponseFailed(event);
13133
+ case "response.output_text.done": {
13134
+ const delta = readDelta(event.payload);
13135
+ return delta ? {
13136
+ ...event,
13137
+ payloadType: "message.delta",
13138
+ payload: { type: "message.delta", delta }
13139
+ } : null;
13140
+ }
13132
13141
  case "response.created":
13133
- case "response.output_text.done":
13134
13142
  return null;
13135
13143
  default:
13136
13144
  return null;
@@ -13160,6 +13168,14 @@ function normalizeResponseOutputItemAdded(event) {
13160
13168
  }
13161
13169
  function normalizeResponseOutputItemDone(event) {
13162
13170
  const item = toRecord10(event.payload.item);
13171
+ if (readString12(item, "type") === "message") {
13172
+ const delta = extractResponseAssistantText({ output: [item] });
13173
+ return delta ? {
13174
+ ...event,
13175
+ payloadType: "message.delta",
13176
+ payload: { type: "message.delta", delta }
13177
+ } : null;
13178
+ }
13163
13179
  if (readString12(item, "type") !== "function_call_output") {
13164
13180
  return null;
13165
13181
  }
@@ -13234,6 +13250,30 @@ function readErrorMessage2(payload) {
13234
13250
  function readDelta(payload) {
13235
13251
  return readText2(payload, "delta") ?? readText2(payload, "text") ?? readText2(payload, "content");
13236
13252
  }
13253
+ function extractResponseAssistantText(value) {
13254
+ if (typeof value === "string") {
13255
+ return value.trim().length > 0 ? value : null;
13256
+ }
13257
+ const payload = toRecord10(value);
13258
+ const response = toRecord10(payload.response ?? value);
13259
+ const directText = readText2(response, "output_text");
13260
+ if (directText?.trim()) {
13261
+ return directText;
13262
+ }
13263
+ const output = response.output;
13264
+ if (Array.isArray(output)) {
13265
+ const messages = output.map(readResponseOutputItemText).filter((text) => Boolean(text?.trim()));
13266
+ if (messages.length > 0) {
13267
+ return messages.join("\n\n");
13268
+ }
13269
+ }
13270
+ const choicesText = readAssistantTextFromChoices(response);
13271
+ if (choicesText) {
13272
+ return choicesText;
13273
+ }
13274
+ const fallbackText = readResponseMessageText(response);
13275
+ return fallbackText?.trim() ? fallbackText : null;
13276
+ }
13237
13277
  function emptyHermesResponseMessage() {
13238
13278
  return "Hermes \u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u5185\u5BB9\u3002\u8BF7\u5148\u770B Gateway / \u6A21\u578B\u914D\u7F6E\u662F\u5426\u6B63\u5E38\uFF0C\u6216\u67E5\u770B\u4E0B\u65B9\u5931\u8D25\u8BE6\u60C5\u3002";
13239
13279
  }
@@ -13303,6 +13343,17 @@ function readFirstChoice(payload) {
13303
13343
  }
13304
13344
  return toRecord10(choices[0]);
13305
13345
  }
13346
+ function readAssistantTextFromChoices(payload) {
13347
+ const choices = payload.choices;
13348
+ if (!Array.isArray(choices)) {
13349
+ return null;
13350
+ }
13351
+ const messages = choices.map(toRecord10).map((choice) => toRecord10(choice.message ?? choice.delta)).filter((message) => {
13352
+ const role = readString12(message, "role");
13353
+ return !role || role === "assistant";
13354
+ }).map(readResponseMessageText).filter((text) => Boolean(text?.trim()));
13355
+ return messages.length > 0 ? messages.join("\n\n") : null;
13356
+ }
13306
13357
  function readInteger2(payload, key) {
13307
13358
  const value = payload[key];
13308
13359
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -13322,6 +13373,50 @@ function readText2(payload, key) {
13322
13373
  const value = payload[key];
13323
13374
  return typeof value === "string" && value.length > 0 ? value : null;
13324
13375
  }
13376
+ function readResponseOutputItemText(value) {
13377
+ if (typeof value === "string") {
13378
+ return value;
13379
+ }
13380
+ const item = toRecord10(value);
13381
+ const type = readString12(item, "type");
13382
+ const role = readString12(item, "role");
13383
+ if (type && type !== "message" && type !== "output_text" && type !== "text") {
13384
+ return null;
13385
+ }
13386
+ if (role && role !== "assistant") {
13387
+ return null;
13388
+ }
13389
+ return readResponseMessageText(item);
13390
+ }
13391
+ function readResponseMessageText(payload) {
13392
+ const contentText = readResponseContentText(payload.content);
13393
+ return contentText ?? readText2(payload, "output_text") ?? readText2(payload, "text") ?? readText2(payload, "content") ?? readText2(payload, "refusal");
13394
+ }
13395
+ function readResponseContentText(value) {
13396
+ if (typeof value === "string") {
13397
+ return value;
13398
+ }
13399
+ if (!Array.isArray(value)) {
13400
+ const record = toRecord10(value);
13401
+ return readText2(record, "text") ?? readText2(record, "content") ?? readText2(record, "output_text") ?? readText2(record, "refusal");
13402
+ }
13403
+ const chunks = value.map((partValue) => {
13404
+ if (typeof partValue === "string") {
13405
+ return partValue;
13406
+ }
13407
+ const part = toRecord10(partValue);
13408
+ const type = readString12(part, "type");
13409
+ if (type && !isVisibleResponseTextPart(type)) {
13410
+ return null;
13411
+ }
13412
+ return readText2(part, "text") ?? readText2(part, "content") ?? readText2(part, "output_text") ?? readText2(part, "refusal");
13413
+ }).filter((text2) => Boolean(text2));
13414
+ const text = chunks.join("");
13415
+ return text.trim() ? text : null;
13416
+ }
13417
+ function isVisibleResponseTextPart(type) {
13418
+ return type === "output_text" || type === "text" || type === "message_text" || type === "refusal";
13419
+ }
13325
13420
  function readResponseItemOutput(value) {
13326
13421
  if (typeof value === "string") {
13327
13422
  return value;
@@ -13502,6 +13597,11 @@ var ConversationRunLifecycle = class {
13502
13597
  error: error instanceof Error ? error.message : String(error)
13503
13598
  });
13504
13599
  });
13600
+ await this.appendAssistantTextFromCompletedResponse(
13601
+ conversationId,
13602
+ runId,
13603
+ event
13604
+ );
13505
13605
  await this.importMediaReferencesForEvent(conversationId, runId, event);
13506
13606
  if (!await this.runHasAssistantOutput(conversationId, runId)) {
13507
13607
  await this.failRun(
@@ -13547,7 +13647,7 @@ var ConversationRunLifecycle = class {
13547
13647
  conversationId,
13548
13648
  runId,
13549
13649
  await this.buildEmptyHermesResponseMessage({
13550
- source: "final-empty-response"
13650
+ source: "stream-ended-without-terminal-event"
13551
13651
  })
13552
13652
  );
13553
13653
  }
@@ -13687,6 +13787,31 @@ ${attachmentLines.join("\n")}`
13687
13787
  }
13688
13788
  return messageText(assistant).length > 0 || (assistant.agent_events?.length ?? 0) > 0 || (assistant.approvals?.length ?? 0) > 0 || assistant.parts.some((part) => part.type !== "text");
13689
13789
  }
13790
+ async appendAssistantTextFromCompletedResponse(conversationId, runId, event) {
13791
+ const terminalText = extractResponseAssistantText(event.payload);
13792
+ if (!terminalText?.trim()) {
13793
+ return;
13794
+ }
13795
+ await this.deps.withConversationLock(conversationId, async () => {
13796
+ const snapshot = await this.deps.readSnapshot(conversationId);
13797
+ const run = snapshot.runs.find((item) => item.id === runId);
13798
+ const assistant = snapshot.messages.find(
13799
+ (item) => item.id === run?.assistant_message_id
13800
+ );
13801
+ if (!assistant) {
13802
+ return;
13803
+ }
13804
+ const currentText = assistant.parts.find((part) => part.type === "text")?.text ?? "";
13805
+ const delta = normalizeTerminalResponseTextDelta(
13806
+ currentText,
13807
+ terminalText
13808
+ );
13809
+ if (!delta) {
13810
+ return;
13811
+ }
13812
+ await this.appendAssistantDelta(conversationId, runId, delta, event);
13813
+ });
13814
+ }
13690
13815
  async buildEmptyHermesResponseMessage(input) {
13691
13816
  const runtime = await readCurrentConversationRuntime(this.deps.paths).catch(
13692
13817
  () => null
@@ -13707,6 +13832,14 @@ ${attachmentLines.join("\n")}`
13707
13832
  );
13708
13833
  }
13709
13834
  }
13835
+ if (input?.source === "stream-ended-without-terminal-event") {
13836
+ details.unshift(
13837
+ "Hermes \u7684\u8FD0\u884C\u4E8B\u4EF6\u6D41\u63D0\u524D\u7ED3\u675F\uFF0CLink \u6CA1\u6709\u6536\u5230\u5B8C\u6210\u6216\u5931\u8D25\u4E8B\u4EF6\u3002"
13838
+ );
13839
+ details.push(
13840
+ "\u8FD9\u66F4\u50CF\u662F Gateway \u6216 provider \u7684\u6D41\u5F0F\u4E8B\u4EF6\u4E2D\u65AD\uFF0C\u4E0D\u662F\u6A21\u578B\u660E\u786E\u5B8C\u6210\u4E86\u7A7A\u56DE\u590D\u3002"
13841
+ );
13842
+ }
13710
13843
  return details.length > 0 ? `Hermes \u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u5185\u5BB9\u3002
13711
13844
  ${details.join("\n")}` : emptyHermesResponseMessage();
13712
13845
  }
@@ -14295,6 +14428,29 @@ function isVoicePart(part) {
14295
14428
  function normalizeToolName(value) {
14296
14429
  return value.trim().toLowerCase().replace(/[\s-]+/gu, "_");
14297
14430
  }
14431
+ function normalizeTerminalResponseTextDelta(currentText, terminalText) {
14432
+ if (!terminalText.trim()) {
14433
+ return "";
14434
+ }
14435
+ if (!currentText) {
14436
+ return terminalText;
14437
+ }
14438
+ if (terminalText === currentText) {
14439
+ return "";
14440
+ }
14441
+ if (terminalText.startsWith(currentText)) {
14442
+ return terminalText.slice(currentText.length);
14443
+ }
14444
+ const normalizedCurrent = currentText.trim();
14445
+ const normalizedTerminal = terminalText.trim();
14446
+ if (!normalizedCurrent || normalizedTerminal === normalizedCurrent) {
14447
+ return "";
14448
+ }
14449
+ if (normalizedTerminal.startsWith(normalizedCurrent)) {
14450
+ return normalizedTerminal.slice(normalizedCurrent.length);
14451
+ }
14452
+ return "";
14453
+ }
14298
14454
  function appendTextBlock2(message, delta, updatedAt) {
14299
14455
  if (!delta) {
14300
14456
  return;
package/dist/cli/index.js CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  startDaemonProcess,
37
37
  startLinkService,
38
38
  stopDaemonProcess
39
- } from "../chunk-B42L327D.js";
39
+ } from "../chunk-VIBGAYU2.js";
40
40
 
41
41
  // src/cli/index.ts
42
42
  import { Command } from "commander";
package/dist/http/app.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createApp
3
- } from "../chunk-B42L327D.js";
3
+ } from "../chunk-VIBGAYU2.js";
4
4
  export {
5
5
  createApp
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hermespilot/link",
3
- "version": "0.4.8",
3
+ "version": "0.5.0",
4
4
  "private": false,
5
5
  "description": "Hermes Link companion service and CLI for connecting hermes-agent through HermesPilot",
6
6
  "license": "MIT",