@nextclaw/nextclaw-ncp-runtime-adapter-hermes-http 0.2.16 → 0.3.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.
@@ -0,0 +1,88 @@
1
+ import { NcpEventType, createNcpEndpointEvent } from "@nextclaw/ncp";
2
+ //#region src/hermes-http-adapter-events.utils.ts
3
+ function createHermesRunHandle(run) {
4
+ const handle = {
5
+ sessionId: run.envelope.sessionId,
6
+ userMessageId: run.envelope.message.id,
7
+ assistantMessageId: run.messageId,
8
+ runId: run.runId
9
+ };
10
+ if (run.envelope.correlationId) handle.correlationId = run.envelope.correlationId;
11
+ return handle;
12
+ }
13
+ function createHermesMessageAcceptedEvent(run) {
14
+ return createNcpEndpointEvent({
15
+ type: NcpEventType.MessageAccepted,
16
+ payload: {
17
+ messageId: run.messageId,
18
+ correlationId: run.envelope.correlationId
19
+ }
20
+ });
21
+ }
22
+ function createHermesRunStartedEvent(params) {
23
+ const { run, startedAt } = params;
24
+ return createNcpEndpointEvent({
25
+ type: NcpEventType.RunStarted,
26
+ payload: {
27
+ sessionId: run.envelope.sessionId,
28
+ messageId: run.messageId,
29
+ runId: run.runId,
30
+ startedAt
31
+ }
32
+ }, startedAt);
33
+ }
34
+ function createHermesMessageCompletedEvent(params) {
35
+ const { message, metadata, run } = params;
36
+ return createNcpEndpointEvent({
37
+ type: NcpEventType.MessageCompleted,
38
+ payload: {
39
+ sessionId: run.envelope.sessionId,
40
+ message,
41
+ correlationId: run.envelope.correlationId,
42
+ metadata
43
+ }
44
+ });
45
+ }
46
+ function createHermesRunFinishedEvent(params) {
47
+ const { endedAt, run, startedAt } = params;
48
+ return createNcpEndpointEvent({
49
+ type: NcpEventType.RunFinished,
50
+ payload: {
51
+ sessionId: run.envelope.sessionId,
52
+ messageId: run.messageId,
53
+ runId: run.runId,
54
+ startedAt,
55
+ endedAt
56
+ }
57
+ }, endedAt);
58
+ }
59
+ function createHermesMessageFailedEvent(params) {
60
+ const { error, run } = params;
61
+ return createNcpEndpointEvent({
62
+ type: NcpEventType.MessageFailed,
63
+ payload: {
64
+ sessionId: run.envelope.sessionId,
65
+ messageId: run.messageId,
66
+ correlationId: run.envelope.correlationId,
67
+ error
68
+ }
69
+ });
70
+ }
71
+ function createHermesRunErrorEvent(params) {
72
+ const { endedAt, error, run, startedAt } = params;
73
+ return createNcpEndpointEvent({
74
+ type: NcpEventType.RunError,
75
+ payload: {
76
+ sessionId: run.envelope.sessionId,
77
+ messageId: run.messageId,
78
+ runId: run.runId,
79
+ error: error.message,
80
+ startedAt,
81
+ endedAt
82
+ }
83
+ }, endedAt);
84
+ }
85
+ //#endregion
86
+ export { createHermesMessageAcceptedEvent, createHermesMessageCompletedEvent, createHermesMessageFailedEvent, createHermesRunErrorEvent, createHermesRunFinishedEvent, createHermesRunHandle, createHermesRunStartedEvent };
87
+
88
+ //# sourceMappingURL=hermes-http-adapter-events.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermes-http-adapter-events.utils.js","names":[],"sources":["../src/hermes-http-adapter-events.utils.ts"],"sourcesContent":["import {\n createNcpEndpointEvent,\n NcpEventType,\n type NcpEndpointEvent,\n type NcpError,\n type NcpMessage,\n type NcpRunHandle,\n} from \"@nextclaw/ncp\";\nimport type { HermesHttpAdapterRun } from \"./hermes-http-adapter.types.js\";\n\nexport function createHermesRunHandle(run: HermesHttpAdapterRun): NcpRunHandle {\n const handle: NcpRunHandle = {\n sessionId: run.envelope.sessionId,\n userMessageId: run.envelope.message.id,\n assistantMessageId: run.messageId,\n runId: run.runId,\n };\n if (run.envelope.correlationId) {\n handle.correlationId = run.envelope.correlationId;\n }\n return handle;\n}\n\nexport function createHermesMessageAcceptedEvent(run: HermesHttpAdapterRun): NcpEndpointEvent {\n return createNcpEndpointEvent({\n type: NcpEventType.MessageAccepted,\n payload: {\n messageId: run.messageId,\n correlationId: run.envelope.correlationId,\n },\n });\n}\n\nexport function createHermesRunStartedEvent(params: {\n run: HermesHttpAdapterRun;\n startedAt: string;\n}): NcpEndpointEvent {\n const { run, startedAt } = params;\n return createNcpEndpointEvent({\n type: NcpEventType.RunStarted,\n payload: {\n sessionId: run.envelope.sessionId,\n messageId: run.messageId,\n runId: run.runId,\n startedAt,\n },\n }, startedAt);\n}\n\nexport function createHermesMessageCompletedEvent(params: {\n message: NcpMessage;\n metadata: Record<string, unknown>;\n run: HermesHttpAdapterRun;\n}): NcpEndpointEvent {\n const { message, metadata, run } = params;\n return createNcpEndpointEvent({\n type: NcpEventType.MessageCompleted,\n payload: {\n sessionId: run.envelope.sessionId,\n message,\n correlationId: run.envelope.correlationId,\n metadata,\n },\n });\n}\n\nexport function createHermesRunFinishedEvent(params: {\n endedAt: string;\n run: HermesHttpAdapterRun;\n startedAt: string;\n}): NcpEndpointEvent {\n const { endedAt, run, startedAt } = params;\n return createNcpEndpointEvent({\n type: NcpEventType.RunFinished,\n payload: {\n sessionId: run.envelope.sessionId,\n messageId: run.messageId,\n runId: run.runId,\n startedAt,\n endedAt,\n },\n }, endedAt);\n}\n\nexport function createHermesMessageFailedEvent(params: {\n error: NcpError;\n run: HermesHttpAdapterRun;\n}): NcpEndpointEvent {\n const { error, run } = params;\n return createNcpEndpointEvent({\n type: NcpEventType.MessageFailed,\n payload: {\n sessionId: run.envelope.sessionId,\n messageId: run.messageId,\n correlationId: run.envelope.correlationId,\n error,\n },\n });\n}\n\nexport function createHermesRunErrorEvent(params: {\n endedAt: string;\n error: NcpError;\n run: HermesHttpAdapterRun;\n startedAt: string;\n}): NcpEndpointEvent {\n const { endedAt, error, run, startedAt } = params;\n return createNcpEndpointEvent({\n type: NcpEventType.RunError,\n payload: {\n sessionId: run.envelope.sessionId,\n messageId: run.messageId,\n runId: run.runId,\n error: error.message,\n startedAt,\n endedAt,\n },\n }, endedAt);\n}\n"],"mappings":";;AAUA,SAAgB,sBAAsB,KAAyC;CAC7E,MAAM,SAAuB;EAC3B,WAAW,IAAI,SAAS;EACxB,eAAe,IAAI,SAAS,QAAQ;EACpC,oBAAoB,IAAI;EACxB,OAAO,IAAI;EACZ;AACD,KAAI,IAAI,SAAS,cACf,QAAO,gBAAgB,IAAI,SAAS;AAEtC,QAAO;;AAGT,SAAgB,iCAAiC,KAA6C;AAC5F,QAAO,uBAAuB;EAC5B,MAAM,aAAa;EACnB,SAAS;GACP,WAAW,IAAI;GACf,eAAe,IAAI,SAAS;GAC7B;EACF,CAAC;;AAGJ,SAAgB,4BAA4B,QAGvB;CACnB,MAAM,EAAE,KAAK,cAAc;AAC3B,QAAO,uBAAuB;EAC5B,MAAM,aAAa;EACnB,SAAS;GACP,WAAW,IAAI,SAAS;GACxB,WAAW,IAAI;GACf,OAAO,IAAI;GACX;GACD;EACF,EAAE,UAAU;;AAGf,SAAgB,kCAAkC,QAI7B;CACnB,MAAM,EAAE,SAAS,UAAU,QAAQ;AACnC,QAAO,uBAAuB;EAC5B,MAAM,aAAa;EACnB,SAAS;GACP,WAAW,IAAI,SAAS;GACxB;GACA,eAAe,IAAI,SAAS;GAC5B;GACD;EACF,CAAC;;AAGJ,SAAgB,6BAA6B,QAIxB;CACnB,MAAM,EAAE,SAAS,KAAK,cAAc;AACpC,QAAO,uBAAuB;EAC5B,MAAM,aAAa;EACnB,SAAS;GACP,WAAW,IAAI,SAAS;GACxB,WAAW,IAAI;GACf,OAAO,IAAI;GACX;GACA;GACD;EACF,EAAE,QAAQ;;AAGb,SAAgB,+BAA+B,QAG1B;CACnB,MAAM,EAAE,OAAO,QAAQ;AACvB,QAAO,uBAAuB;EAC5B,MAAM,aAAa;EACnB,SAAS;GACP,WAAW,IAAI,SAAS;GACxB,WAAW,IAAI;GACf,eAAe,IAAI,SAAS;GAC5B;GACD;EACF,CAAC;;AAGJ,SAAgB,0BAA0B,QAKrB;CACnB,MAAM,EAAE,SAAS,OAAO,KAAK,cAAc;AAC3C,QAAO,uBAAuB;EAC5B,MAAM,aAAa;EACnB,SAAS;GACP,WAAW,IAAI,SAAS;GACxB,WAAW,IAAI;GACf,OAAO,IAAI;GACX,OAAO,MAAM;GACb;GACA;GACD;EACF,EAAE,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"hermes-http-adapter-message.utils.d.ts","names":[],"sources":["../src/hermes-http-adapter-message.utils.ts"],"mappings":";;;;KAgEY,mBAAA;EACV,KAAA;EACA,MAAA;EACA,OAAA;EACA,OAAA,EAAS,MAAA;EACT,OAAA;AAAA;AAAA,iBAKc,uBAAA,CACd,QAAA,EAAU,kBAAA,GACT,mBAAA;AAAA,iBA0Ba,mBAAA,CAAoB,MAAA;EAClC,QAAA,EAAU,kBAAA;EACV,YAAA;AAAA,IACE,mBAAA;AAAA,iBAiBY,sBAAA,CAAuB,MAAA;EACrC,SAAA;EACA,SAAA;EACA,IAAA;EACA,SAAA;EACA,QAAA,GAAW,MAAA;AAAA,IACT,UAAA;AAAA,iBAkBY,+BAAA,CAAgC,MAAA;EAC9C,SAAA;EACA,SAAA;EACA,KAAA,EAAO,cAAA;EACP,SAAA;EACA,QAAA,GAAW,MAAA;AAAA,IACT,UAAA;AAAA,cAaS,6BAAA;EAAA,iBACM,KAAA;EAAA,iBACA,qBAAA;EAEjB,UAAA,GAAc,KAAA,EAAO,gBAAA;EA8CrB,QAAA;EAEA,UAAA,QAAiB,cAAA;EAAA,QAET,cAAA;EAAA,QAeA,mBAAA;EAAA,QAeA,sBAAA;EAAA,QAWA,wBAAA;EAAA,QAkCA,YAAA;EAAA,QAUA,YAAA;EAAA,QAKA,YAAA;AAAA;AAAA,cAYG,+BAAA;EAAA,QACH,iBAAA;EAAA,QACA,qBAAA;EAAA,QACA,aAAA;EAER,SAAA,GACE,IAAA,EAAM,+BAAA,EACN,MAAA,EAAQ,aAAA,CAAc,gBAAA,MACrB,cAAA,CAAe,gBAAA;EAAA,QAyCV,gBAAA;EAAA,QAuEA,sBAAA;EAAA,QAOA,yBAAA;EAAA,QAOA,kBAAA;EAAA,QASA,kBAAA;AAAA;AAAA,cAUG,8BAAA;EAAA,iBACM,UAAA;EAEjB,SAAA,GACE,IAAA,EAAM,8BAAA,EACN,MAAA,EAAQ,aAAA,CAAc,gBAAA,MACrB,cAAA,CAAe,gBAAA;EAAA,QAiBV,uBAAA;EAAA,QA+BA,gBAAA;EAAA,QAsCA,aAAA;EAAA,QAIA,sBAAA;EAAA,QAOA,kBAAA;EAAA,QASA,kBAAA;AAAA;AAAA,iBAUM,kBAAA,CAAmB,MAAA;EACjC,QAAA,EAAU,kBAAA;EACV,aAAA;AAAA;AAAA,iBAoBc,6BAAA,CAA8B,KAAA;AAAA,iBAuB9B,mBAAA,CAAoB,MAAA;EAClC,KAAA;EACA,OAAA;EACA,OAAA,GAAU,mBAAA;AAAA;AAAA,iBAcI,UAAA,CACd,KAAA,WACA,IAAA,EAAM,QAAA,WACL,QAAA;AAAA,iBAOa,aAAA,CAAc,KAAA,EAAO,gBAAA;AAAA,iBAIrB,eAAA,CAAgB,KAAA;EAAS,IAAA;EAAc,OAAA;AAAA"}
1
+ {"version":3,"file":"hermes-http-adapter-message.utils.d.ts","names":[],"sources":["../src/hermes-http-adapter-message.utils.ts"],"mappings":";;;;KA4CY,mBAAA;EACV,KAAA;EACA,MAAA;EACA,OAAA;EACA,OAAA,EAAS,MAAA;EACT,OAAA;AAAA;AAAA,iBAKc,uBAAA,CACd,QAAA,EAAU,kBAAA,GACT,mBAAA;AAAA,iBA0Ba,mBAAA,CAAoB,MAAA;EAClC,QAAA,EAAU,kBAAA;EACV,YAAA;AAAA,IACE,mBAAA;AAAA,iBAiBY,sBAAA,CAAuB,MAAA;EACrC,SAAA;EACA,SAAA;EACA,IAAA;EACA,SAAA;EACA,QAAA,GAAW,MAAA;AAAA,IACT,UAAA;AAAA,iBAkBY,+BAAA,CAAgC,MAAA;EAC9C,SAAA;EACA,SAAA;EACA,KAAA,EAAO,cAAA;EACP,SAAA;EACA,QAAA,GAAW,MAAA;AAAA,IACT,UAAA;AAAA,cAaS,6BAAA;EAAA,iBACM,KAAA;EAAA,iBACA,qBAAA;EAEjB,UAAA,GAAc,KAAA,EAAO,gBAAA;EA8CrB,QAAA;EAEA,UAAA,QAAiB,cAAA;EAAA,QAET,cAAA;EAAA,QAeA,mBAAA;EAAA,QAeA,sBAAA;EAAA,QAWA,wBAAA;EAAA,QAkCA,YAAA;EAAA,QAUA,YAAA;EAAA,QAKA,YAAA;AAAA;AAAA,cAYG,+BAAA;EAAA,QACH,iBAAA;EAAA,QACA,qBAAA;EAAA,QACA,aAAA;EAER,SAAA,GACE,IAAA,EAAM,+BAAA,EACN,MAAA,EAAQ,aAAA,CAAc,gBAAA,MACrB,cAAA,CAAe,gBAAA;EAAA,QAyCV,gBAAA;EAAA,QAwEA,sBAAA;EAAA,QAOA,yBAAA;EAAA,QAOA,kBAAA;EAAA,QASA,kBAAA;AAAA;AAAA,cAUG,8BAAA;EAAA,iBACM,UAAA;EAEjB,SAAA,GACE,IAAA,EAAM,8BAAA,EACN,MAAA,EAAQ,aAAA,CAAc,gBAAA,MACrB,cAAA,CAAe,gBAAA;EAAA,QAiBV,uBAAA;EAAA,QA+BA,gBAAA;EAAA,QAsCA,aAAA;EAAA,QAIA,sBAAA;EAAA,QAOA,kBAAA;EAAA,QASA,kBAAA;AAAA;AAAA,iBAUM,kBAAA,CAAmB,MAAA;EACjC,QAAA,EAAU,kBAAA;EACV,aAAA;AAAA;AAAA,iBAoBc,6BAAA,CAA8B,KAAA;AAAA,iBAuB9B,mBAAA,CAAoB,MAAA;EAClC,KAAA;EACA,OAAA;EACA,OAAA,GAAU,mBAAA;AAAA;AAAA,iBAcI,UAAA,CACd,KAAA,WACA,IAAA,EAAM,QAAA,WACL,QAAA;AAAA,iBAOa,aAAA,CAAc,KAAA,EAAO,gBAAA;AAAA,iBAIrB,eAAA,CAAgB,KAAA;EAAS,IAAA;EAAc,OAAA;AAAA"}
@@ -1,14 +1,6 @@
1
- import { NcpAssistantTextStreamNormalizer, NcpEventType } from "@nextclaw/ncp";
1
+ import { matchHermesInlineToolTrace } from "./hermes-inline-tool-trace.utils.js";
2
+ import { NcpAssistantTextStreamNormalizer, NcpEventType, createNcpEndpointEvent } from "@nextclaw/ncp";
2
3
  //#region src/hermes-http-adapter-message.utils.ts
3
- const HERMES_INLINE_TOOL_TRACE_PATTERNS = [{
4
- icon: "🔎",
5
- toolName: "search_files",
6
- buildArgs: (rawArgs) => JSON.stringify({ pattern: rawArgs })
7
- }, {
8
- icon: "💻",
9
- toolName: "terminal",
10
- buildArgs: (rawArgs) => JSON.stringify({ command: rawArgs })
11
- }];
12
4
  function readMetadataString(metadata, key) {
13
5
  const value = metadata?.[key];
14
6
  if (typeof value !== "string") return;
@@ -236,49 +228,50 @@ var HermesInlineToolTraceTranslator = class {
236
228
  };
237
229
  flushPendingText = function* (params) {
238
230
  if (!this.pendingTextBuffer) return;
231
+ const { flush, messageId, sessionId } = params;
239
232
  const lastNewlineIndex = this.pendingTextBuffer.lastIndexOf("\n");
240
- const flushableText = params.flush ? this.pendingTextBuffer : lastNewlineIndex >= 0 ? this.pendingTextBuffer.slice(0, lastNewlineIndex + 1) : this.pendingTextBuffer.includes("`") ? "" : this.pendingTextBuffer;
233
+ const flushableText = flush ? this.pendingTextBuffer : lastNewlineIndex >= 0 ? this.pendingTextBuffer.slice(0, lastNewlineIndex + 1) : this.pendingTextBuffer.includes("`") ? "" : this.pendingTextBuffer;
241
234
  if (!flushableText) return;
242
235
  this.pendingTextBuffer = this.pendingTextBuffer.slice(flushableText.length);
243
236
  for (const segment of splitIntoLineSegments(flushableText)) {
244
237
  const inlineToolTrace = matchHermesInlineToolTrace(segment);
245
- if (inlineToolTrace && params.sessionId) {
238
+ if (inlineToolTrace && sessionId) {
246
239
  const toolCallId = `hermes-inline-tool-${++this.toolCallCount}`;
247
- yield {
240
+ yield createNcpEndpointEvent({
248
241
  type: NcpEventType.MessageToolCallStart,
249
242
  payload: {
250
- sessionId: params.sessionId,
251
- ...params.messageId ? { messageId: params.messageId } : {},
243
+ sessionId,
244
+ messageId,
252
245
  toolCallId,
253
246
  toolName: inlineToolTrace.toolName
254
247
  }
255
- };
256
- yield {
248
+ });
249
+ yield createNcpEndpointEvent({
257
250
  type: NcpEventType.MessageToolCallArgs,
258
251
  payload: {
259
- sessionId: params.sessionId,
252
+ sessionId,
260
253
  toolCallId,
261
254
  args: inlineToolTrace.args
262
255
  }
263
- };
264
- yield {
256
+ });
257
+ yield createNcpEndpointEvent({
265
258
  type: NcpEventType.MessageToolCallEnd,
266
259
  payload: {
267
- sessionId: params.sessionId,
260
+ sessionId,
268
261
  toolCallId
269
262
  }
270
- };
263
+ });
271
264
  continue;
272
265
  }
273
- if (!segment || !params.sessionId || !params.messageId) continue;
274
- yield {
266
+ if (!segment || !sessionId || !messageId) continue;
267
+ yield createNcpEndpointEvent({
275
268
  type: NcpEventType.MessageTextDelta,
276
269
  payload: {
277
- sessionId: params.sessionId,
278
- messageId: params.messageId,
270
+ sessionId,
271
+ messageId,
279
272
  delta: segment
280
273
  }
281
- };
274
+ });
282
275
  }
283
276
  };
284
277
  shouldFlushPendingText = (event) => event.type === NcpEventType.MessageTextEnd || event.type === NcpEventType.MessageCompleted || event.type === NcpEventType.MessageFailed || event.type === NcpEventType.RunFinished || event.type === NcpEventType.RunError;
@@ -310,24 +303,24 @@ var HermesReasoningDeltaTranslator = class {
310
303
  for (const segment of segments) {
311
304
  if (!segment.text) continue;
312
305
  if (segment.type === "reasoning") {
313
- yield {
306
+ yield createNcpEndpointEvent({
314
307
  type: NcpEventType.MessageReasoningDelta,
315
308
  payload: {
316
309
  sessionId: event.payload.sessionId,
317
310
  messageId: event.payload.messageId,
318
311
  delta: segment.text
319
312
  }
320
- };
313
+ });
321
314
  continue;
322
315
  }
323
- yield {
316
+ yield createNcpEndpointEvent({
324
317
  type: NcpEventType.MessageTextDelta,
325
318
  payload: {
326
319
  sessionId: event.payload.sessionId,
327
320
  messageId: event.payload.messageId,
328
321
  delta: segment.text
329
322
  }
330
- };
323
+ });
331
324
  }
332
325
  };
333
326
  flushPendingText = function* (event) {
@@ -341,24 +334,24 @@ var HermesReasoningDeltaTranslator = class {
341
334
  for (const segment of segments) {
342
335
  if (!segment.text) continue;
343
336
  if (segment.type === "reasoning") {
344
- yield {
337
+ yield createNcpEndpointEvent({
345
338
  type: NcpEventType.MessageReasoningDelta,
346
339
  payload: {
347
340
  sessionId,
348
341
  messageId,
349
342
  delta: segment.text
350
343
  }
351
- };
344
+ });
352
345
  continue;
353
346
  }
354
- yield {
347
+ yield createNcpEndpointEvent({
355
348
  type: NcpEventType.MessageTextDelta,
356
349
  payload: {
357
350
  sessionId,
358
351
  messageId,
359
352
  delta: segment.text
360
353
  }
361
- };
354
+ });
362
355
  }
363
356
  };
364
357
  flushSegments = () => {
@@ -435,21 +428,6 @@ function extractMessageText(message) {
435
428
  function splitIntoLineSegments(text) {
436
429
  return text.match(/[^\n]*\n|[^\n]+$/gu) ?? [];
437
430
  }
438
- function matchHermesInlineToolTrace(segment) {
439
- const trimmed = segment.trim();
440
- if (!trimmed.startsWith("`") || !trimmed.endsWith("`")) return null;
441
- const body = trimmed.slice(1, -1).trim();
442
- for (const candidate of HERMES_INLINE_TOOL_TRACE_PATTERNS) {
443
- if (!body.startsWith(candidate.icon)) continue;
444
- const rawArgs = body.slice(candidate.icon.length).trim();
445
- if (!rawArgs) return null;
446
- return {
447
- toolName: candidate.toolName,
448
- args: candidate.buildArgs(rawArgs)
449
- };
450
- }
451
- return null;
452
- }
453
431
  function normalizeRole(role) {
454
432
  if (role === "assistant" || role === "system") return role;
455
433
  return "user";
@@ -1 +1 @@
1
- {"version":3,"file":"hermes-http-adapter-message.utils.js","names":[],"sources":["../src/hermes-http-adapter-message.utils.ts"],"sourcesContent":["import {\n NcpAssistantTextStreamNormalizer,\n type NcpEndpointEvent,\n type NcpError,\n type NcpMessage,\n type NcpMessagePart,\n type NcpRequestEnvelope,\n type NcpToolInvocationPart,\n NcpEventType,\n} from \"@nextclaw/ncp\";\nimport type { HermesOpenAIMessage } from \"./hermes-http-adapter.types.js\";\n\ntype HermesInlineToolTrace = {\n toolName: string;\n args: string;\n};\n\nconst HERMES_INLINE_TOOL_TRACE_PATTERNS: Array<{\n icon: string;\n toolName: string;\n buildArgs: (rawArgs: string) => string;\n}> = [\n {\n icon: \"🔎\",\n toolName: \"search_files\",\n buildArgs: (rawArgs) => JSON.stringify({ pattern: rawArgs }),\n },\n {\n icon: \"💻\",\n toolName: \"terminal\",\n buildArgs: (rawArgs) => JSON.stringify({ command: rawArgs }),\n },\n];\n\nfunction readMetadataString(\n metadata: Record<string, unknown> | undefined,\n key: string,\n): string | undefined {\n const value = metadata?.[key];\n if (typeof value !== \"string\") {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction readStringRecord(value: unknown): Record<string, string> | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return undefined;\n }\n const out: Record<string, string> = {};\n for (const [entryKey, entryValue] of Object.entries(value)) {\n if (typeof entryValue !== \"string\") {\n continue;\n }\n const normalized = entryValue.trim();\n if (!normalized) {\n continue;\n }\n out[entryKey] = normalized;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nexport type HermesProviderRoute = {\n model: string;\n apiKey?: string;\n apiBase?: string;\n headers: Record<string, string>;\n apiMode?: \"chat_completions\" | \"codex_responses\" | \"anthropic_messages\";\n};\n\nconst NARP_API_MODE_HEADER = \"x-nextclaw-narp-api-mode\";\n\nexport function readHermesProviderRoute(\n envelope: NcpRequestEnvelope,\n): HermesProviderRoute | undefined {\n const route = envelope.providerRoute;\n if (!route || typeof route !== \"object\" || Array.isArray(route)) {\n return undefined;\n }\n const routeRecord = route as Record<string, unknown>;\n\n const model =\n readMetadataString(routeRecord, \"model\") ??\n readMetadataString(routeRecord, \"providerLocalModel\");\n if (!model) {\n return undefined;\n }\n\n return {\n model,\n apiKey: readMetadataString(routeRecord, \"apiKey\"),\n apiBase: readMetadataString(routeRecord, \"apiBase\"),\n ...extractRuntimeHeaders(\n readStringRecord(routeRecord.headers) ??\n readStringRecord(routeRecord.extraHeaders) ??\n {},\n ),\n };\n}\n\nexport function buildHermesMessages(params: {\n envelope: NcpRequestEnvelope;\n systemPrompt?: string;\n}): HermesOpenAIMessage[] {\n const { envelope, systemPrompt } = params;\n const messages: HermesOpenAIMessage[] = [];\n if (systemPrompt) {\n messages.push({\n role: \"system\",\n content: systemPrompt,\n });\n }\n const content = extractMessageText(envelope.message);\n messages.push({\n role: normalizeRole(envelope.message.role),\n content,\n });\n return messages;\n}\n\nexport function createAssistantMessage(params: {\n sessionId: string;\n messageId: string;\n text: string;\n timestamp: string;\n metadata?: Record<string, unknown>;\n}): NcpMessage {\n const { sessionId, messageId, text, timestamp, metadata } = params;\n return {\n id: messageId,\n sessionId,\n role: \"assistant\",\n status: \"final\",\n parts: [\n {\n type: \"text\",\n text,\n },\n ],\n timestamp,\n ...(metadata ? { metadata } : {}),\n };\n}\n\nexport function createAssistantMessageFromParts(params: {\n sessionId: string;\n messageId: string;\n parts: NcpMessagePart[];\n timestamp: string;\n metadata?: Record<string, unknown>;\n}): NcpMessage {\n const { sessionId, messageId, parts, timestamp, metadata } = params;\n return {\n id: messageId,\n sessionId,\n role: \"assistant\",\n status: \"final\",\n parts: structuredClone(parts),\n timestamp,\n ...(metadata ? { metadata } : {}),\n };\n}\n\nexport class HermesAssistantEventCollector {\n private readonly parts: NcpMessagePart[] = [];\n private readonly toolPartIndexByCallId = new Map<string, number>();\n\n applyEvent = (event: NcpEndpointEvent): void => {\n switch (event.type) {\n case NcpEventType.MessageTextDelta:\n this.appendTextPart(event.payload.delta);\n return;\n case NcpEventType.MessageReasoningDelta:\n this.appendReasoningPart(event.payload.delta);\n return;\n case NcpEventType.MessageToolCallStart:\n this.upsertToolInvocationPart({\n type: \"tool-invocation\",\n toolCallId: event.payload.toolCallId,\n toolName: event.payload.toolName,\n state: \"partial-call\",\n args: \"\",\n });\n return;\n case NcpEventType.MessageToolCallArgsDelta:\n this.applyToolCallArgsDelta(\n event.payload.toolCallId,\n event.payload.delta,\n );\n return;\n case NcpEventType.MessageToolCallArgs:\n this.upsertToolInvocationPart({\n type: \"tool-invocation\",\n toolCallId: event.payload.toolCallId,\n toolName: this.readToolName(event.payload.toolCallId),\n state: \"partial-call\",\n args: event.payload.args,\n });\n return;\n case NcpEventType.MessageToolCallEnd:\n this.upsertToolInvocationPart({\n type: \"tool-invocation\",\n toolCallId: event.payload.toolCallId,\n toolName: this.readToolName(event.payload.toolCallId),\n state: \"call\",\n args: this.readToolArgs(event.payload.toolCallId),\n });\n return;\n default:\n return;\n }\n };\n\n hasParts = (): boolean => this.parts.length > 0;\n\n buildParts = (): NcpMessagePart[] => structuredClone(this.parts);\n\n private appendTextPart = (delta: string): void => {\n if (!delta) {\n return;\n }\n const lastPart = this.parts.at(-1);\n if (lastPart?.type === \"text\") {\n lastPart.text = `${lastPart.text}${delta}`;\n return;\n }\n this.parts.push({\n type: \"text\",\n text: delta,\n });\n };\n\n private appendReasoningPart = (delta: string): void => {\n if (!delta) {\n return;\n }\n const lastPart = this.parts.at(-1);\n if (lastPart?.type === \"reasoning\") {\n lastPart.text = `${lastPart.text}${delta}`;\n return;\n }\n this.parts.push({\n type: \"reasoning\",\n text: delta,\n });\n };\n\n private applyToolCallArgsDelta = (toolCallId: string, delta: string): void => {\n const currentArgs = this.readToolArgs(toolCallId);\n this.upsertToolInvocationPart({\n type: \"tool-invocation\",\n toolCallId,\n toolName: this.readToolName(toolCallId),\n state: \"partial-call\",\n args: `${currentArgs}${delta}`,\n });\n };\n\n private upsertToolInvocationPart = (\n nextPart: NcpToolInvocationPart,\n ): void => {\n const normalizedToolCallId = nextPart.toolCallId?.trim();\n if (!normalizedToolCallId) {\n this.parts.push({\n ...nextPart,\n });\n return;\n }\n\n const existingIndex = this.toolPartIndexByCallId.get(normalizedToolCallId);\n if (typeof existingIndex === \"number\") {\n const existingPart = this.parts[existingIndex];\n if (existingPart?.type !== \"tool-invocation\") {\n return;\n }\n this.parts[existingIndex] = {\n ...existingPart,\n ...nextPart,\n toolCallId: normalizedToolCallId,\n toolName: nextPart.toolName || existingPart.toolName,\n args: nextPart.args ?? existingPart.args,\n };\n return;\n }\n\n this.toolPartIndexByCallId.set(normalizedToolCallId, this.parts.length);\n this.parts.push({\n ...nextPart,\n toolCallId: normalizedToolCallId,\n });\n };\n\n private readToolArgs = (toolCallId: string): string => {\n const part = this.findToolPart(toolCallId);\n if (!part) {\n return \"\";\n }\n return typeof part.args === \"string\"\n ? part.args\n : JSON.stringify(part.args ?? \"\");\n };\n\n private readToolName = (toolCallId: string): string => {\n const part = this.findToolPart(toolCallId);\n return part?.toolName ?? \"unknown\";\n };\n\n private findToolPart = (\n toolCallId: string,\n ): NcpToolInvocationPart | null => {\n const existingIndex = this.toolPartIndexByCallId.get(toolCallId);\n if (typeof existingIndex !== \"number\") {\n return null;\n }\n const part = this.parts[existingIndex];\n return part?.type === \"tool-invocation\" ? part : null;\n };\n}\n\nexport class HermesInlineToolTraceTranslator {\n private pendingTextBuffer = \"\";\n private sawStructuredToolCall = false;\n private toolCallCount = 0;\n\n translate = async function* (\n this: HermesInlineToolTraceTranslator,\n events: AsyncIterable<NcpEndpointEvent>,\n ): AsyncGenerator<NcpEndpointEvent> {\n for await (const event of events) {\n if (this.isStructuredToolCallEvent(event)) {\n this.sawStructuredToolCall = true;\n yield* this.flushPendingText({\n sessionId: this.readEventSessionId(event),\n messageId: this.readEventMessageId(event),\n flush: true,\n });\n yield event;\n continue;\n }\n\n if (\n !this.sawStructuredToolCall &&\n event.type === NcpEventType.MessageTextDelta\n ) {\n this.pendingTextBuffer += event.payload.delta;\n yield* this.flushPendingText({\n sessionId: event.payload.sessionId,\n messageId: event.payload.messageId,\n flush: false,\n });\n continue;\n }\n\n if (\n !this.sawStructuredToolCall &&\n this.shouldFlushPendingText(event)\n ) {\n yield* this.flushPendingText({\n sessionId: this.readEventSessionId(event),\n messageId: this.readEventMessageId(event),\n flush: true,\n });\n }\n\n yield event;\n }\n };\n\n private flushPendingText = function* (\n this: HermesInlineToolTraceTranslator,\n params: {\n sessionId?: string;\n messageId?: string;\n flush: boolean;\n },\n ): Generator<NcpEndpointEvent> {\n if (!this.pendingTextBuffer) {\n return;\n }\n\n const lastNewlineIndex = this.pendingTextBuffer.lastIndexOf(\"\\n\");\n const flushableText =\n params.flush\n ? this.pendingTextBuffer\n : lastNewlineIndex >= 0\n ? this.pendingTextBuffer.slice(0, lastNewlineIndex + 1)\n : this.pendingTextBuffer.includes(\"`\")\n ? \"\"\n : this.pendingTextBuffer;\n if (!flushableText) {\n return;\n }\n\n this.pendingTextBuffer = this.pendingTextBuffer.slice(flushableText.length);\n for (const segment of splitIntoLineSegments(flushableText)) {\n const inlineToolTrace = matchHermesInlineToolTrace(segment);\n if (inlineToolTrace && params.sessionId) {\n const toolCallId = `hermes-inline-tool-${++this.toolCallCount}`;\n yield {\n type: NcpEventType.MessageToolCallStart,\n payload: {\n sessionId: params.sessionId,\n ...(params.messageId ? { messageId: params.messageId } : {}),\n toolCallId,\n toolName: inlineToolTrace.toolName,\n },\n };\n yield {\n type: NcpEventType.MessageToolCallArgs,\n payload: {\n sessionId: params.sessionId,\n toolCallId,\n args: inlineToolTrace.args,\n },\n };\n yield {\n type: NcpEventType.MessageToolCallEnd,\n payload: {\n sessionId: params.sessionId,\n toolCallId,\n },\n };\n continue;\n }\n\n if (!segment || !params.sessionId || !params.messageId) {\n continue;\n }\n yield {\n type: NcpEventType.MessageTextDelta,\n payload: {\n sessionId: params.sessionId,\n messageId: params.messageId,\n delta: segment,\n },\n };\n }\n };\n\n private shouldFlushPendingText = (event: NcpEndpointEvent): boolean =>\n event.type === NcpEventType.MessageTextEnd ||\n event.type === NcpEventType.MessageCompleted ||\n event.type === NcpEventType.MessageFailed ||\n event.type === NcpEventType.RunFinished ||\n event.type === NcpEventType.RunError;\n\n private isStructuredToolCallEvent = (event: NcpEndpointEvent): boolean =>\n event.type === NcpEventType.MessageToolCallStart ||\n event.type === NcpEventType.MessageToolCallArgs ||\n event.type === NcpEventType.MessageToolCallArgsDelta ||\n event.type === NcpEventType.MessageToolCallEnd ||\n event.type === NcpEventType.MessageToolCallResult;\n\n private readEventSessionId = (event: NcpEndpointEvent): string | undefined => {\n if (!(\"payload\" in event) || !event.payload || typeof event.payload !== \"object\") {\n return undefined;\n }\n return \"sessionId\" in event.payload && typeof event.payload.sessionId === \"string\"\n ? event.payload.sessionId\n : undefined;\n };\n\n private readEventMessageId = (event: NcpEndpointEvent): string | undefined => {\n if (!(\"payload\" in event) || !event.payload || typeof event.payload !== \"object\") {\n return undefined;\n }\n return \"messageId\" in event.payload && typeof event.payload.messageId === \"string\"\n ? event.payload.messageId\n : undefined;\n };\n}\n\nexport class HermesReasoningDeltaTranslator {\n private readonly normalizer = new NcpAssistantTextStreamNormalizer(\"think-tags\");\n\n translate = async function* (\n this: HermesReasoningDeltaTranslator,\n events: AsyncIterable<NcpEndpointEvent>,\n ): AsyncGenerator<NcpEndpointEvent> {\n for await (const event of events) {\n if (event.type === NcpEventType.MessageTextDelta) {\n yield* this.emitNormalizedTextDelta(event);\n continue;\n }\n\n if (this.shouldFlushPendingText(event)) {\n yield* this.flushPendingText(event);\n }\n\n yield event;\n }\n\n this.flushSegments();\n };\n\n private emitNormalizedTextDelta = function* (\n this: HermesReasoningDeltaTranslator,\n event: Extract<NcpEndpointEvent, { type: NcpEventType.MessageTextDelta }>,\n ): Generator<NcpEndpointEvent> {\n const segments = this.normalizer.push(event.payload.delta);\n for (const segment of segments) {\n if (!segment.text) {\n continue;\n }\n if (segment.type === \"reasoning\") {\n yield {\n type: NcpEventType.MessageReasoningDelta,\n payload: {\n sessionId: event.payload.sessionId,\n messageId: event.payload.messageId,\n delta: segment.text,\n },\n };\n continue;\n }\n yield {\n type: NcpEventType.MessageTextDelta,\n payload: {\n sessionId: event.payload.sessionId,\n messageId: event.payload.messageId,\n delta: segment.text,\n },\n };\n }\n };\n\n private flushPendingText = function* (\n this: HermesReasoningDeltaTranslator,\n event: NcpEndpointEvent,\n ): Generator<NcpEndpointEvent> {\n const sessionId = this.readEventSessionId(event);\n const messageId = this.readEventMessageId(event);\n if (!sessionId || !messageId) {\n this.flushSegments();\n return;\n }\n\n const segments = this.normalizer.finish();\n for (const segment of segments) {\n if (!segment.text) {\n continue;\n }\n if (segment.type === \"reasoning\") {\n yield {\n type: NcpEventType.MessageReasoningDelta,\n payload: {\n sessionId,\n messageId,\n delta: segment.text,\n },\n };\n continue;\n }\n yield {\n type: NcpEventType.MessageTextDelta,\n payload: {\n sessionId,\n messageId,\n delta: segment.text,\n },\n };\n }\n };\n\n private flushSegments = (): void => {\n this.normalizer.finish();\n };\n\n private shouldFlushPendingText = (event: NcpEndpointEvent): boolean =>\n event.type === NcpEventType.MessageTextEnd ||\n event.type === NcpEventType.MessageCompleted ||\n event.type === NcpEventType.MessageFailed ||\n event.type === NcpEventType.RunFinished ||\n event.type === NcpEventType.RunError;\n\n private readEventSessionId = (event: NcpEndpointEvent): string | undefined => {\n if (!(\"payload\" in event) || !event.payload || typeof event.payload !== \"object\") {\n return undefined;\n }\n return \"sessionId\" in event.payload && typeof event.payload.sessionId === \"string\"\n ? event.payload.sessionId\n : undefined;\n };\n\n private readEventMessageId = (event: NcpEndpointEvent): string | undefined => {\n if (!(\"payload\" in event) || !event.payload || typeof event.payload !== \"object\") {\n return undefined;\n }\n return \"messageId\" in event.payload && typeof event.payload.messageId === \"string\"\n ? event.payload.messageId\n : undefined;\n };\n}\n\nexport function resolveHermesModel(params: {\n envelope: NcpRequestEnvelope;\n fallbackModel: string;\n}): string {\n const { envelope, fallbackModel } = params;\n const providerRoute = readHermesProviderRoute(envelope);\n const metadata =\n envelope.metadata &&\n typeof envelope.metadata === \"object\" &&\n !Array.isArray(envelope.metadata)\n ? (envelope.metadata as Record<string, unknown>)\n : undefined;\n\n return (\n providerRoute?.model ??\n readMetadataString(metadata, \"preferred_model\") ??\n readMetadataString(metadata, \"preferredModel\") ??\n readMetadataString(metadata, \"model\") ??\n fallbackModel\n );\n}\n\nexport function normalizeHermesRequestedModel(model: string): string {\n const trimmed = model.trim();\n if (!trimmed) {\n return trimmed;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n if (slashIndex <= 0) {\n return trimmed;\n }\n\n const suffix = trimmed.slice(slashIndex + 1).trim();\n if (!suffix) {\n return trimmed;\n }\n\n if (suffix.includes(\":\")) {\n return suffix;\n }\n\n return trimmed;\n}\n\nexport function inferHermesProvider(params: {\n model: string;\n apiBase?: string | null;\n apiMode?: HermesProviderRoute[\"apiMode\"];\n}): string | undefined {\n if (params.apiMode === \"anthropic_messages\") {\n return \"anthropic\";\n }\n const model = params.model.trim().toLowerCase();\n const hostname = readHostname(params.apiBase);\n\n if (hostname.includes(\"anthropic.com\") || model.startsWith(\"claude\")) {\n return \"anthropic\";\n }\n return \"openai\";\n}\n\nexport function toNcpError(\n error: unknown,\n code: NcpError[\"code\"],\n): NcpError {\n return {\n code,\n message: error instanceof Error ? error.message : String(error),\n };\n}\n\nexport function toNcpSseFrame(event: NcpEndpointEvent): string {\n return `event: ncp-event\\ndata: ${JSON.stringify(event)}\\n\\n`;\n}\n\nexport function toErrorSseFrame(error: { code: string; message: string }): string {\n return `event: error\\ndata: ${JSON.stringify(error)}\\n\\n`;\n}\n\nfunction readHostname(value: string | null | undefined): string {\n if (!value) {\n return \"\";\n }\n try {\n return new URL(value).hostname.trim().toLowerCase();\n } catch {\n return \"\";\n }\n}\n\nfunction extractRuntimeHeaders(\n headers: Record<string, string>,\n): Pick<HermesProviderRoute, \"headers\" | \"apiMode\"> {\n const nextHeaders = { ...headers };\n const apiMode = nextHeaders[NARP_API_MODE_HEADER] as HermesProviderRoute[\"apiMode\"] | undefined;\n delete nextHeaders[NARP_API_MODE_HEADER];\n return {\n headers: nextHeaders,\n ...(apiMode ? { apiMode } : {}),\n };\n}\n\nfunction extractMessageText(message: NcpMessage): string {\n const text = message.parts\n .map((part) => {\n if (part.type === \"text\" || part.type === \"reasoning\" || part.type === \"rich-text\") {\n return part.text;\n }\n return \"\";\n })\n .join(\"\\n\")\n .trim();\n return text.length > 0 ? text : \"[empty message]\";\n}\n\nfunction splitIntoLineSegments(text: string): string[] {\n return text.match(/[^\\n]*\\n|[^\\n]+$/gu) ?? [];\n}\n\nfunction matchHermesInlineToolTrace(segment: string): HermesInlineToolTrace | null {\n const trimmed = segment.trim();\n if (!trimmed.startsWith(\"`\") || !trimmed.endsWith(\"`\")) {\n return null;\n }\n\n const body = trimmed.slice(1, -1).trim();\n for (const candidate of HERMES_INLINE_TOOL_TRACE_PATTERNS) {\n if (!body.startsWith(candidate.icon)) {\n continue;\n }\n const rawArgs = body.slice(candidate.icon.length).trim();\n if (!rawArgs) {\n return null;\n }\n return {\n toolName: candidate.toolName,\n args: candidate.buildArgs(rawArgs),\n };\n }\n\n return null;\n}\n\nfunction normalizeRole(role: NcpMessage[\"role\"]): HermesOpenAIMessage[\"role\"] {\n if (role === \"assistant\" || role === \"system\") {\n return role;\n }\n return \"user\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"hermes-http-adapter-message.utils.js","names":[],"sources":["../src/hermes-http-adapter-message.utils.ts"],"sourcesContent":["import {\n createNcpEndpointEvent,\n NcpAssistantTextStreamNormalizer,\n type NcpEndpointEvent,\n type NcpError,\n type NcpMessage,\n type NcpMessagePart,\n type NcpRequestEnvelope,\n type NcpToolInvocationPart,\n NcpEventType,\n} from \"@nextclaw/ncp\";\nimport type { HermesOpenAIMessage } from \"./hermes-http-adapter.types.js\";\nimport { matchHermesInlineToolTrace } from \"./hermes-inline-tool-trace.utils.js\";\n\nfunction readMetadataString(\n metadata: Record<string, unknown> | undefined,\n key: string,\n): string | undefined {\n const value = metadata?.[key];\n if (typeof value !== \"string\") {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction readStringRecord(value: unknown): Record<string, string> | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return undefined;\n }\n const out: Record<string, string> = {};\n for (const [entryKey, entryValue] of Object.entries(value)) {\n if (typeof entryValue !== \"string\") {\n continue;\n }\n const normalized = entryValue.trim();\n if (!normalized) {\n continue;\n }\n out[entryKey] = normalized;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nexport type HermesProviderRoute = {\n model: string;\n apiKey?: string;\n apiBase?: string;\n headers: Record<string, string>;\n apiMode?: \"chat_completions\" | \"codex_responses\" | \"anthropic_messages\";\n};\n\nconst NARP_API_MODE_HEADER = \"x-nextclaw-narp-api-mode\";\n\nexport function readHermesProviderRoute(\n envelope: NcpRequestEnvelope,\n): HermesProviderRoute | undefined {\n const route = envelope.providerRoute;\n if (!route || typeof route !== \"object\" || Array.isArray(route)) {\n return undefined;\n }\n const routeRecord = route as Record<string, unknown>;\n\n const model =\n readMetadataString(routeRecord, \"model\") ??\n readMetadataString(routeRecord, \"providerLocalModel\");\n if (!model) {\n return undefined;\n }\n\n return {\n model,\n apiKey: readMetadataString(routeRecord, \"apiKey\"),\n apiBase: readMetadataString(routeRecord, \"apiBase\"),\n ...extractRuntimeHeaders(\n readStringRecord(routeRecord.headers) ??\n readStringRecord(routeRecord.extraHeaders) ??\n {},\n ),\n };\n}\n\nexport function buildHermesMessages(params: {\n envelope: NcpRequestEnvelope;\n systemPrompt?: string;\n}): HermesOpenAIMessage[] {\n const { envelope, systemPrompt } = params;\n const messages: HermesOpenAIMessage[] = [];\n if (systemPrompt) {\n messages.push({\n role: \"system\",\n content: systemPrompt,\n });\n }\n const content = extractMessageText(envelope.message);\n messages.push({\n role: normalizeRole(envelope.message.role),\n content,\n });\n return messages;\n}\n\nexport function createAssistantMessage(params: {\n sessionId: string;\n messageId: string;\n text: string;\n timestamp: string;\n metadata?: Record<string, unknown>;\n}): NcpMessage {\n const { sessionId, messageId, text, timestamp, metadata } = params;\n return {\n id: messageId,\n sessionId,\n role: \"assistant\",\n status: \"final\",\n parts: [\n {\n type: \"text\",\n text,\n },\n ],\n timestamp,\n ...(metadata ? { metadata } : {}),\n };\n}\n\nexport function createAssistantMessageFromParts(params: {\n sessionId: string;\n messageId: string;\n parts: NcpMessagePart[];\n timestamp: string;\n metadata?: Record<string, unknown>;\n}): NcpMessage {\n const { sessionId, messageId, parts, timestamp, metadata } = params;\n return {\n id: messageId,\n sessionId,\n role: \"assistant\",\n status: \"final\",\n parts: structuredClone(parts),\n timestamp,\n ...(metadata ? { metadata } : {}),\n };\n}\n\nexport class HermesAssistantEventCollector {\n private readonly parts: NcpMessagePart[] = [];\n private readonly toolPartIndexByCallId = new Map<string, number>();\n\n applyEvent = (event: NcpEndpointEvent): void => {\n switch (event.type) {\n case NcpEventType.MessageTextDelta:\n this.appendTextPart(event.payload.delta);\n return;\n case NcpEventType.MessageReasoningDelta:\n this.appendReasoningPart(event.payload.delta);\n return;\n case NcpEventType.MessageToolCallStart:\n this.upsertToolInvocationPart({\n type: \"tool-invocation\",\n toolCallId: event.payload.toolCallId,\n toolName: event.payload.toolName,\n state: \"partial-call\",\n args: \"\",\n });\n return;\n case NcpEventType.MessageToolCallArgsDelta:\n this.applyToolCallArgsDelta(\n event.payload.toolCallId,\n event.payload.delta,\n );\n return;\n case NcpEventType.MessageToolCallArgs:\n this.upsertToolInvocationPart({\n type: \"tool-invocation\",\n toolCallId: event.payload.toolCallId,\n toolName: this.readToolName(event.payload.toolCallId),\n state: \"partial-call\",\n args: event.payload.args,\n });\n return;\n case NcpEventType.MessageToolCallEnd:\n this.upsertToolInvocationPart({\n type: \"tool-invocation\",\n toolCallId: event.payload.toolCallId,\n toolName: this.readToolName(event.payload.toolCallId),\n state: \"call\",\n args: this.readToolArgs(event.payload.toolCallId),\n });\n return;\n default:\n return;\n }\n };\n\n hasParts = (): boolean => this.parts.length > 0;\n\n buildParts = (): NcpMessagePart[] => structuredClone(this.parts);\n\n private appendTextPart = (delta: string): void => {\n if (!delta) {\n return;\n }\n const lastPart = this.parts.at(-1);\n if (lastPart?.type === \"text\") {\n lastPart.text = `${lastPart.text}${delta}`;\n return;\n }\n this.parts.push({\n type: \"text\",\n text: delta,\n });\n };\n\n private appendReasoningPart = (delta: string): void => {\n if (!delta) {\n return;\n }\n const lastPart = this.parts.at(-1);\n if (lastPart?.type === \"reasoning\") {\n lastPart.text = `${lastPart.text}${delta}`;\n return;\n }\n this.parts.push({\n type: \"reasoning\",\n text: delta,\n });\n };\n\n private applyToolCallArgsDelta = (toolCallId: string, delta: string): void => {\n const currentArgs = this.readToolArgs(toolCallId);\n this.upsertToolInvocationPart({\n type: \"tool-invocation\",\n toolCallId,\n toolName: this.readToolName(toolCallId),\n state: \"partial-call\",\n args: `${currentArgs}${delta}`,\n });\n };\n\n private upsertToolInvocationPart = (\n nextPart: NcpToolInvocationPart,\n ): void => {\n const normalizedToolCallId = nextPart.toolCallId?.trim();\n if (!normalizedToolCallId) {\n this.parts.push({\n ...nextPart,\n });\n return;\n }\n\n const existingIndex = this.toolPartIndexByCallId.get(normalizedToolCallId);\n if (typeof existingIndex === \"number\") {\n const existingPart = this.parts[existingIndex];\n if (existingPart?.type !== \"tool-invocation\") {\n return;\n }\n this.parts[existingIndex] = {\n ...existingPart,\n ...nextPart,\n toolCallId: normalizedToolCallId,\n toolName: nextPart.toolName || existingPart.toolName,\n args: nextPart.args ?? existingPart.args,\n };\n return;\n }\n\n this.toolPartIndexByCallId.set(normalizedToolCallId, this.parts.length);\n this.parts.push({\n ...nextPart,\n toolCallId: normalizedToolCallId,\n });\n };\n\n private readToolArgs = (toolCallId: string): string => {\n const part = this.findToolPart(toolCallId);\n if (!part) {\n return \"\";\n }\n return typeof part.args === \"string\"\n ? part.args\n : JSON.stringify(part.args ?? \"\");\n };\n\n private readToolName = (toolCallId: string): string => {\n const part = this.findToolPart(toolCallId);\n return part?.toolName ?? \"unknown\";\n };\n\n private findToolPart = (\n toolCallId: string,\n ): NcpToolInvocationPart | null => {\n const existingIndex = this.toolPartIndexByCallId.get(toolCallId);\n if (typeof existingIndex !== \"number\") {\n return null;\n }\n const part = this.parts[existingIndex];\n return part?.type === \"tool-invocation\" ? part : null;\n };\n}\n\nexport class HermesInlineToolTraceTranslator {\n private pendingTextBuffer = \"\";\n private sawStructuredToolCall = false;\n private toolCallCount = 0;\n\n translate = async function* (\n this: HermesInlineToolTraceTranslator,\n events: AsyncIterable<NcpEndpointEvent>,\n ): AsyncGenerator<NcpEndpointEvent> {\n for await (const event of events) {\n if (this.isStructuredToolCallEvent(event)) {\n this.sawStructuredToolCall = true;\n yield* this.flushPendingText({\n sessionId: this.readEventSessionId(event),\n messageId: this.readEventMessageId(event),\n flush: true,\n });\n yield event;\n continue;\n }\n\n if (\n !this.sawStructuredToolCall &&\n event.type === NcpEventType.MessageTextDelta\n ) {\n this.pendingTextBuffer += event.payload.delta;\n yield* this.flushPendingText({\n sessionId: event.payload.sessionId,\n messageId: event.payload.messageId,\n flush: false,\n });\n continue;\n }\n\n if (\n !this.sawStructuredToolCall &&\n this.shouldFlushPendingText(event)\n ) {\n yield* this.flushPendingText({\n sessionId: this.readEventSessionId(event),\n messageId: this.readEventMessageId(event),\n flush: true,\n });\n }\n\n yield event;\n }\n };\n\n private flushPendingText = function* (\n this: HermesInlineToolTraceTranslator,\n params: {\n sessionId?: string;\n messageId?: string;\n flush: boolean;\n },\n ): Generator<NcpEndpointEvent> {\n if (!this.pendingTextBuffer) {\n return;\n }\n\n const { flush, messageId, sessionId } = params;\n const lastNewlineIndex = this.pendingTextBuffer.lastIndexOf(\"\\n\");\n const flushableText =\n flush\n ? this.pendingTextBuffer\n : lastNewlineIndex >= 0\n ? this.pendingTextBuffer.slice(0, lastNewlineIndex + 1)\n : this.pendingTextBuffer.includes(\"`\")\n ? \"\"\n : this.pendingTextBuffer;\n if (!flushableText) {\n return;\n }\n\n this.pendingTextBuffer = this.pendingTextBuffer.slice(flushableText.length);\n for (const segment of splitIntoLineSegments(flushableText)) {\n const inlineToolTrace = matchHermesInlineToolTrace(segment);\n if (inlineToolTrace && sessionId) {\n const toolCallId = `hermes-inline-tool-${++this.toolCallCount}`;\n yield createNcpEndpointEvent({\n type: NcpEventType.MessageToolCallStart,\n payload: {\n sessionId,\n messageId,\n toolCallId,\n toolName: inlineToolTrace.toolName,\n },\n });\n yield createNcpEndpointEvent({\n type: NcpEventType.MessageToolCallArgs,\n payload: {\n sessionId,\n toolCallId,\n args: inlineToolTrace.args,\n },\n });\n yield createNcpEndpointEvent({\n type: NcpEventType.MessageToolCallEnd,\n payload: {\n sessionId,\n toolCallId,\n },\n });\n continue;\n }\n\n if (!segment || !sessionId || !messageId) {\n continue;\n }\n yield createNcpEndpointEvent({\n type: NcpEventType.MessageTextDelta,\n payload: {\n sessionId,\n messageId,\n delta: segment,\n },\n });\n }\n };\n\n private shouldFlushPendingText = (event: NcpEndpointEvent): boolean =>\n event.type === NcpEventType.MessageTextEnd ||\n event.type === NcpEventType.MessageCompleted ||\n event.type === NcpEventType.MessageFailed ||\n event.type === NcpEventType.RunFinished ||\n event.type === NcpEventType.RunError;\n\n private isStructuredToolCallEvent = (event: NcpEndpointEvent): boolean =>\n event.type === NcpEventType.MessageToolCallStart ||\n event.type === NcpEventType.MessageToolCallArgs ||\n event.type === NcpEventType.MessageToolCallArgsDelta ||\n event.type === NcpEventType.MessageToolCallEnd ||\n event.type === NcpEventType.MessageToolCallResult;\n\n private readEventSessionId = (event: NcpEndpointEvent): string | undefined => {\n if (!(\"payload\" in event) || !event.payload || typeof event.payload !== \"object\") {\n return undefined;\n }\n return \"sessionId\" in event.payload && typeof event.payload.sessionId === \"string\"\n ? event.payload.sessionId\n : undefined;\n };\n\n private readEventMessageId = (event: NcpEndpointEvent): string | undefined => {\n if (!(\"payload\" in event) || !event.payload || typeof event.payload !== \"object\") {\n return undefined;\n }\n return \"messageId\" in event.payload && typeof event.payload.messageId === \"string\"\n ? event.payload.messageId\n : undefined;\n };\n}\n\nexport class HermesReasoningDeltaTranslator {\n private readonly normalizer = new NcpAssistantTextStreamNormalizer(\"think-tags\");\n\n translate = async function* (\n this: HermesReasoningDeltaTranslator,\n events: AsyncIterable<NcpEndpointEvent>,\n ): AsyncGenerator<NcpEndpointEvent> {\n for await (const event of events) {\n if (event.type === NcpEventType.MessageTextDelta) {\n yield* this.emitNormalizedTextDelta(event);\n continue;\n }\n\n if (this.shouldFlushPendingText(event)) {\n yield* this.flushPendingText(event);\n }\n\n yield event;\n }\n\n this.flushSegments();\n };\n\n private emitNormalizedTextDelta = function* (\n this: HermesReasoningDeltaTranslator,\n event: Extract<NcpEndpointEvent, { type: NcpEventType.MessageTextDelta }>,\n ): Generator<NcpEndpointEvent> {\n const segments = this.normalizer.push(event.payload.delta);\n for (const segment of segments) {\n if (!segment.text) {\n continue;\n }\n if (segment.type === \"reasoning\") {\n yield createNcpEndpointEvent({\n type: NcpEventType.MessageReasoningDelta,\n payload: {\n sessionId: event.payload.sessionId,\n messageId: event.payload.messageId,\n delta: segment.text,\n },\n });\n continue;\n }\n yield createNcpEndpointEvent({\n type: NcpEventType.MessageTextDelta,\n payload: {\n sessionId: event.payload.sessionId,\n messageId: event.payload.messageId,\n delta: segment.text,\n },\n });\n }\n };\n\n private flushPendingText = function* (\n this: HermesReasoningDeltaTranslator,\n event: NcpEndpointEvent,\n ): Generator<NcpEndpointEvent> {\n const sessionId = this.readEventSessionId(event);\n const messageId = this.readEventMessageId(event);\n if (!sessionId || !messageId) {\n this.flushSegments();\n return;\n }\n\n const segments = this.normalizer.finish();\n for (const segment of segments) {\n if (!segment.text) {\n continue;\n }\n if (segment.type === \"reasoning\") {\n yield createNcpEndpointEvent({\n type: NcpEventType.MessageReasoningDelta,\n payload: {\n sessionId,\n messageId,\n delta: segment.text,\n },\n });\n continue;\n }\n yield createNcpEndpointEvent({\n type: NcpEventType.MessageTextDelta,\n payload: {\n sessionId,\n messageId,\n delta: segment.text,\n },\n });\n }\n };\n\n private flushSegments = (): void => {\n this.normalizer.finish();\n };\n\n private shouldFlushPendingText = (event: NcpEndpointEvent): boolean =>\n event.type === NcpEventType.MessageTextEnd ||\n event.type === NcpEventType.MessageCompleted ||\n event.type === NcpEventType.MessageFailed ||\n event.type === NcpEventType.RunFinished ||\n event.type === NcpEventType.RunError;\n\n private readEventSessionId = (event: NcpEndpointEvent): string | undefined => {\n if (!(\"payload\" in event) || !event.payload || typeof event.payload !== \"object\") {\n return undefined;\n }\n return \"sessionId\" in event.payload && typeof event.payload.sessionId === \"string\"\n ? event.payload.sessionId\n : undefined;\n };\n\n private readEventMessageId = (event: NcpEndpointEvent): string | undefined => {\n if (!(\"payload\" in event) || !event.payload || typeof event.payload !== \"object\") {\n return undefined;\n }\n return \"messageId\" in event.payload && typeof event.payload.messageId === \"string\"\n ? event.payload.messageId\n : undefined;\n };\n}\n\nexport function resolveHermesModel(params: {\n envelope: NcpRequestEnvelope;\n fallbackModel: string;\n}): string {\n const { envelope, fallbackModel } = params;\n const providerRoute = readHermesProviderRoute(envelope);\n const metadata =\n envelope.metadata &&\n typeof envelope.metadata === \"object\" &&\n !Array.isArray(envelope.metadata)\n ? (envelope.metadata as Record<string, unknown>)\n : undefined;\n\n return (\n providerRoute?.model ??\n readMetadataString(metadata, \"preferred_model\") ??\n readMetadataString(metadata, \"preferredModel\") ??\n readMetadataString(metadata, \"model\") ??\n fallbackModel\n );\n}\n\nexport function normalizeHermesRequestedModel(model: string): string {\n const trimmed = model.trim();\n if (!trimmed) {\n return trimmed;\n }\n\n const slashIndex = trimmed.indexOf(\"/\");\n if (slashIndex <= 0) {\n return trimmed;\n }\n\n const suffix = trimmed.slice(slashIndex + 1).trim();\n if (!suffix) {\n return trimmed;\n }\n\n if (suffix.includes(\":\")) {\n return suffix;\n }\n\n return trimmed;\n}\n\nexport function inferHermesProvider(params: {\n model: string;\n apiBase?: string | null;\n apiMode?: HermesProviderRoute[\"apiMode\"];\n}): string | undefined {\n if (params.apiMode === \"anthropic_messages\") {\n return \"anthropic\";\n }\n const model = params.model.trim().toLowerCase();\n const hostname = readHostname(params.apiBase);\n\n if (hostname.includes(\"anthropic.com\") || model.startsWith(\"claude\")) {\n return \"anthropic\";\n }\n return \"openai\";\n}\n\nexport function toNcpError(\n error: unknown,\n code: NcpError[\"code\"],\n): NcpError {\n return {\n code,\n message: error instanceof Error ? error.message : String(error),\n };\n}\n\nexport function toNcpSseFrame(event: NcpEndpointEvent): string {\n return `event: ncp-event\\ndata: ${JSON.stringify(event)}\\n\\n`;\n}\n\nexport function toErrorSseFrame(error: { code: string; message: string }): string {\n return `event: error\\ndata: ${JSON.stringify(error)}\\n\\n`;\n}\n\nfunction readHostname(value: string | null | undefined): string {\n if (!value) {\n return \"\";\n }\n try {\n return new URL(value).hostname.trim().toLowerCase();\n } catch {\n return \"\";\n }\n}\n\nfunction extractRuntimeHeaders(\n headers: Record<string, string>,\n): Pick<HermesProviderRoute, \"headers\" | \"apiMode\"> {\n const nextHeaders = { ...headers };\n const apiMode = nextHeaders[NARP_API_MODE_HEADER] as HermesProviderRoute[\"apiMode\"] | undefined;\n delete nextHeaders[NARP_API_MODE_HEADER];\n return {\n headers: nextHeaders,\n ...(apiMode ? { apiMode } : {}),\n };\n}\n\nfunction extractMessageText(message: NcpMessage): string {\n const text = message.parts\n .map((part) => {\n if (part.type === \"text\" || part.type === \"reasoning\" || part.type === \"rich-text\") {\n return part.text;\n }\n return \"\";\n })\n .join(\"\\n\")\n .trim();\n return text.length > 0 ? text : \"[empty message]\";\n}\n\nfunction splitIntoLineSegments(text: string): string[] {\n return text.match(/[^\\n]*\\n|[^\\n]+$/gu) ?? [];\n}\n\nfunction normalizeRole(role: NcpMessage[\"role\"]): HermesOpenAIMessage[\"role\"] {\n if (role === \"assistant\" || role === \"system\") {\n return role;\n }\n return \"user\";\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hermes-http-adapter.service.d.ts","names":[],"sources":["../src/hermes-http-adapter.service.ts"],"mappings":";;;;cAkmBa,uBAAA;EAAA,SACF,MAAA,EAAQ,+BAAA;EAAA,iBAEA,YAAA;EAAA,QACT,MAAA;cAEI,WAAA,GAAa,4BAAA;EAKzB,KAAA,QAAkB,OAAA;EAiBlB,IAAA,QAAiB,OAAA;AAAA"}
1
+ {"version":3,"file":"hermes-http-adapter.service.d.ts","names":[],"sources":["../src/hermes-http-adapter.service.ts"],"mappings":";;;;cA4jBa,uBAAA;EAAA,SACF,MAAA,EAAQ,+BAAA;EAAA,iBAEA,YAAA;EAAA,QACT,MAAA;cAEI,WAAA,GAAa,4BAAA;EAKzB,KAAA,QAAkB,OAAA;EAiBlB,IAAA,QAAiB,OAAA;AAAA"}
@@ -2,10 +2,11 @@ import { HermesHttpAdapterConfigResolver } from "./hermes-http-adapter-config.ut
2
2
  import { HermesAssistantEventCollector, HermesInlineToolTraceTranslator, HermesReasoningDeltaTranslator, buildHermesMessages, createAssistantMessageFromParts, inferHermesProvider, normalizeHermesRequestedModel, readHermesProviderRoute, resolveHermesModel, toErrorSseFrame, toNcpError, toNcpSseFrame } from "./hermes-http-adapter-message.utils.js";
3
3
  import { parseHermesOpenAIChatStream } from "./hermes-openai-stream-parser.utils.js";
4
4
  import { HermesHttpAdapterSessionStore } from "./hermes-http-adapter-session-store.service.js";
5
+ import { createHermesMessageAcceptedEvent, createHermesMessageCompletedEvent, createHermesMessageFailedEvent, createHermesRunErrorEvent, createHermesRunFinishedEvent, createHermesRunHandle, createHermesRunStartedEvent } from "./hermes-http-adapter-events.utils.js";
5
6
  import { createServer } from "node:http";
6
7
  import { randomUUID } from "node:crypto";
7
8
  import { DefaultNcpStreamEncoder } from "@nextclaw/ncp-agent-runtime";
8
- import { NcpEventType } from "@nextclaw/ncp";
9
+ import "@nextclaw/ncp";
9
10
  //#region src/hermes-http-adapter.service.ts
10
11
  function isUserVisibleAssistantEvent(event) {
11
12
  const eventType = event.type;
@@ -109,7 +110,10 @@ var HermesHttpAdapterRouteService = class {
109
110
  runId: randomUUID()
110
111
  };
111
112
  this.sessions.setPendingRun(run);
112
- this.writeJson(response, 200, { ok: true });
113
+ this.writeJson(response, 200, {
114
+ ok: true,
115
+ data: createHermesRunHandle(run)
116
+ });
113
117
  } catch (error) {
114
118
  this.writeJson(response, 409, {
115
119
  ok: false,
@@ -237,21 +241,12 @@ var HermesHttpAdapterRouteService = class {
237
241
  const messageCollector = new HermesAssistantEventCollector();
238
242
  const inlineToolTraceTranslator = new HermesInlineToolTraceTranslator();
239
243
  const reasoningDeltaTranslator = new HermesReasoningDeltaTranslator();
240
- yield {
241
- type: NcpEventType.MessageAccepted,
242
- payload: {
243
- messageId: run.messageId,
244
- correlationId: run.envelope.correlationId
245
- }
246
- };
247
- yield {
248
- type: NcpEventType.RunStarted,
249
- payload: {
250
- sessionId,
251
- messageId: run.messageId,
252
- runId: run.runId
253
- }
254
- };
244
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
245
+ yield createHermesMessageAcceptedEvent(run);
246
+ yield createHermesRunStartedEvent({
247
+ run,
248
+ startedAt
249
+ });
255
250
  try {
256
251
  for await (const event of reasoningDeltaTranslator.translate(inlineToolTraceTranslator.translate(this.streamEncoder.encode(parseHermesOpenAIChatStream(response.body), {
257
252
  sessionId,
@@ -264,74 +259,45 @@ var HermesHttpAdapterRouteService = class {
264
259
  yield event;
265
260
  }
266
261
  if (!emittedUserVisibleAssistantEvent || !messageCollector.hasParts()) throw new Error(`[hermes-http-adapter] Hermes completed without any assistant content for session ${sessionId}.`);
267
- const message = this.buildAssistantMessage({
268
- sessionId,
269
- messageId: run.messageId,
270
- parts: messageCollector.buildParts(),
271
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
272
- metadata
273
- });
274
- yield {
275
- type: NcpEventType.MessageCompleted,
276
- payload: {
277
- sessionId,
278
- message,
279
- correlationId: run.envelope.correlationId,
280
- metadata
281
- }
282
- };
283
- yield {
284
- type: NcpEventType.RunFinished,
285
- payload: {
262
+ yield createHermesMessageCompletedEvent({
263
+ message: this.buildAssistantMessage({
286
264
  sessionId,
287
265
  messageId: run.messageId,
288
- runId: run.runId
289
- }
290
- };
266
+ parts: messageCollector.buildParts(),
267
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
268
+ metadata
269
+ }),
270
+ metadata,
271
+ run
272
+ });
273
+ yield createHermesRunFinishedEvent({
274
+ endedAt: (/* @__PURE__ */ new Date()).toISOString(),
275
+ run,
276
+ startedAt
277
+ });
291
278
  } catch (error) {
292
- if (signal.aborted) {
293
- const abortError = toNcpError(error, "abort-error");
294
- yield {
295
- type: NcpEventType.MessageFailed,
296
- payload: {
297
- sessionId,
298
- messageId: run.messageId,
299
- correlationId: run.envelope.correlationId,
300
- error: abortError
301
- }
302
- };
303
- yield {
304
- type: NcpEventType.RunError,
305
- payload: {
306
- sessionId,
307
- messageId: run.messageId,
308
- runId: run.runId,
309
- error: abortError.message
310
- }
311
- };
312
- return;
313
- }
314
- const runtimeError = toNcpError(error, "runtime-error");
315
- yield {
316
- type: NcpEventType.MessageFailed,
317
- payload: {
318
- sessionId,
319
- messageId: run.messageId,
320
- correlationId: run.envelope.correlationId,
321
- error: runtimeError
322
- }
323
- };
324
- yield {
325
- type: NcpEventType.RunError,
326
- payload: {
327
- sessionId,
328
- messageId: run.messageId,
329
- runId: run.runId,
330
- error: runtimeError.message
331
- }
332
- };
279
+ yield* this.createRunErrorEvents({
280
+ error,
281
+ run,
282
+ startedAt,
283
+ fallbackCode: signal.aborted ? "abort-error" : "runtime-error"
284
+ });
333
285
  }
334
286
  };
287
+ createRunErrorEvents = function* (params) {
288
+ const { error, fallbackCode, run, startedAt } = params;
289
+ const ncpError = toNcpError(error, fallbackCode);
290
+ yield createHermesMessageFailedEvent({
291
+ error: ncpError,
292
+ run
293
+ });
294
+ yield createHermesRunErrorEvent({
295
+ endedAt: (/* @__PURE__ */ new Date()).toISOString(),
296
+ error: ncpError,
297
+ run,
298
+ startedAt
299
+ });
300
+ };
335
301
  ensureHermesSessionModel = async (params) => {
336
302
  const { requestedModel, sessionId, signal } = params;
337
303
  if ((this.sessions.readSelectedModel(sessionId) ?? this.config.model) === requestedModel) return;
@@ -1 +1 @@
1
- {"version":3,"file":"hermes-http-adapter.service.js","names":[],"sources":["../src/hermes-http-adapter.service.ts"],"sourcesContent":["import { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { randomUUID } from \"node:crypto\";\nimport { DefaultNcpStreamEncoder } from \"@nextclaw/ncp-agent-runtime\";\nimport {\n NcpEventType,\n type NcpEndpointEvent,\n type NcpRequestEnvelope,\n type OpenAITool,\n} from \"@nextclaw/ncp\";\nimport { HermesHttpAdapterConfigResolver } from \"./hermes-http-adapter-config.utils.js\";\nimport {\n buildHermesMessages,\n createAssistantMessageFromParts,\n HermesAssistantEventCollector,\n HermesInlineToolTraceTranslator,\n HermesReasoningDeltaTranslator,\n inferHermesProvider,\n normalizeHermesRequestedModel,\n readHermesProviderRoute,\n resolveHermesModel,\n type HermesProviderRoute,\n toErrorSseFrame,\n toNcpError,\n toNcpSseFrame,\n} from \"./hermes-http-adapter-message.utils.js\";\nimport { parseHermesOpenAIChatStream } from \"./hermes-openai-stream-parser.utils.js\";\nimport { HermesHttpAdapterSessionStore } from \"./hermes-http-adapter-session-store.service.js\";\nimport type {\n HermesAdapterAssistantMessageFactory,\n HermesHttpAdapterResolvedConfig,\n HermesHttpAdapterRun,\n HermesHttpAdapterRunResult,\n} from \"./hermes-http-adapter.types.js\";\nimport type { HermesHttpAdapterConfigInput } from \"./hermes-http-adapter-config.utils.js\";\n\nfunction isUserVisibleAssistantEvent(event: NcpEndpointEvent): boolean {\n const eventType = event.type;\n return (\n eventType.startsWith(\"message.text-\") ||\n eventType.startsWith(\"message.reasoning-\") ||\n eventType.startsWith(\"message.tool-call-\")\n );\n}\n\nclass HermesHttpAdapterRouteService {\n private readonly sessions = new HermesHttpAdapterSessionStore();\n private readonly streamEncoder = new DefaultNcpStreamEncoder({\n reasoningNormalizationMode: \"think-tags\",\n });\n private readonly fetchImpl: typeof fetch;\n\n constructor(\n private readonly config: HermesHttpAdapterResolvedConfig,\n private readonly buildAssistantMessage: HermesAdapterAssistantMessageFactory = createAssistantMessageFromParts,\n ) {\n this.fetchImpl = config.fetchImpl ?? fetch;\n }\n\n handle = async (\n request: IncomingMessage,\n response: ServerResponse,\n ): Promise<void> => {\n const requestUrl = new URL(request.url ?? \"/\", this.baseOrigin);\n const normalizedPath = requestUrl.pathname;\n\n if (request.method === \"GET\" && normalizedPath === \"/health\") {\n await this.handleHealth(response);\n return;\n }\n\n if (request.method === \"POST\" && normalizedPath === `${this.config.basePath}/send`) {\n await this.handleSend(request, response);\n return;\n }\n\n if (request.method === \"GET\" && normalizedPath === `${this.config.basePath}/stream`) {\n await this.handleStream(request, response, requestUrl);\n return;\n }\n\n if (request.method === \"POST\" && normalizedPath === `${this.config.basePath}/abort`) {\n await this.handleAbort(request, response);\n return;\n }\n\n this.writeJson(response, 404, {\n ok: false,\n error: {\n code: \"NOT_FOUND\",\n message: `Unknown path: ${normalizedPath}`,\n },\n });\n };\n\n dispose = (): void => {\n this.sessions.dispose();\n };\n\n private get baseOrigin(): string {\n return `http://${this.config.host}:${this.config.port}`;\n }\n\n private handleHealth = async (response: ServerResponse): Promise<void> => {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.healthcheckTimeoutMs);\n\n try {\n const upstream = await this.fetchImpl(this.config.healthcheckUrl, {\n method: \"GET\",\n signal: controller.signal,\n });\n if (!upstream.ok) {\n this.writeJson(response, 503, {\n status: \"degraded\",\n adapter: \"ok\",\n upstream: {\n status: \"error\",\n httpStatus: upstream.status,\n },\n });\n return;\n }\n this.writeJson(response, 200, {\n status: \"ok\",\n adapter: \"ok\",\n upstream: \"ok\",\n });\n } catch (error) {\n this.writeJson(response, 503, {\n status: \"degraded\",\n adapter: \"ok\",\n upstream: {\n status: \"unreachable\",\n message: error instanceof Error ? error.message : String(error),\n },\n });\n } finally {\n clearTimeout(timeout);\n }\n };\n\n private handleSend = async (\n request: IncomingMessage,\n response: ServerResponse,\n ): Promise<void> => {\n try {\n const envelope = await this.readJsonBody<NcpRequestEnvelope>(request);\n if (!envelope?.sessionId || !envelope.message) {\n this.writeJson(response, 400, {\n ok: false,\n error: {\n code: \"INVALID_BODY\",\n message: \"sessionId and message are required.\",\n },\n });\n return;\n }\n\n const run: HermesHttpAdapterRun = {\n envelope,\n messageId: randomUUID(),\n runId: randomUUID(),\n };\n this.sessions.setPendingRun(run);\n this.writeJson(response, 200, { ok: true });\n } catch (error) {\n this.writeJson(response, 409, {\n ok: false,\n error: {\n code: \"RUN_CONFLICT\",\n message: error instanceof Error ? error.message : String(error),\n },\n });\n }\n };\n\n private handleAbort = async (\n request: IncomingMessage,\n response: ServerResponse,\n ): Promise<void> => {\n try {\n const payload = await this.readJsonBody<{ sessionId?: string }>(request);\n const sessionId = typeof payload?.sessionId === \"string\" ? payload.sessionId : \"\";\n if (!sessionId.trim()) {\n this.writeJson(response, 400, {\n ok: false,\n error: {\n code: \"INVALID_BODY\",\n message: \"sessionId is required.\",\n },\n });\n return;\n }\n const aborted = this.sessions.abortSession(sessionId);\n this.writeJson(response, 200, { ok: true, aborted });\n } catch (error) {\n this.writeJson(response, 500, {\n ok: false,\n error: {\n code: \"ABORT_FAILED\",\n message: error instanceof Error ? error.message : String(error),\n },\n });\n }\n };\n\n private handleStream = async (\n request: IncomingMessage,\n response: ServerResponse,\n requestUrl: URL,\n ): Promise<void> => {\n const sessionId = requestUrl.searchParams.get(\"sessionId\")?.trim();\n if (!sessionId) {\n this.writeJson(response, 400, {\n ok: false,\n error: {\n code: \"INVALID_QUERY\",\n message: \"sessionId is required.\",\n },\n });\n return;\n }\n\n const connectionController = new AbortController();\n const runController = new AbortController();\n response.once(\"close\", () => {\n connectionController.abort(\"client disconnected\");\n runController.abort(\"client disconnected\");\n });\n\n response.writeHead(200, {\n \"content-type\": \"text/event-stream; charset=utf-8\",\n \"cache-control\": \"no-cache, no-transform\",\n connection: \"keep-alive\",\n \"x-accel-buffering\": \"no\",\n });\n\n try {\n const run = await this.sessions.waitForPendingRun(\n sessionId,\n connectionController.signal,\n this.config.streamWaitTimeoutMs,\n );\n this.sessions.setActiveAbortController(sessionId, runController);\n\n const streamResult = await this.startRun({\n run,\n signal: runController.signal,\n });\n\n for await (const event of streamResult.events) {\n if (connectionController.signal.aborted) {\n break;\n }\n response.write(toNcpSseFrame(event));\n }\n } catch (error) {\n if (!connectionController.signal.aborted) {\n response.write(\n toErrorSseFrame({\n code: \"STREAM_FAILED\",\n message: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n } finally {\n this.sessions.clearActiveAbortController(sessionId);\n response.end();\n }\n };\n\n private startRun = async (params: {\n run: HermesHttpAdapterRun;\n signal: AbortSignal;\n }): Promise<HermesHttpAdapterRunResult> => {\n const { run, signal } = params;\n const providerRoute = this.readProviderRoute(run.envelope);\n const requestedModel = normalizeHermesRequestedModel(resolveHermesModel({\n envelope: run.envelope,\n fallbackModel: this.config.model,\n }));\n if (!providerRoute) {\n await this.ensureHermesSessionModel({\n sessionId: run.envelope.sessionId,\n requestedModel,\n signal,\n });\n }\n\n const response = await this.fetchHermesChatCompletion({\n sessionId: run.envelope.sessionId,\n signal,\n stream: true,\n model: requestedModel,\n providerRoute,\n messages: buildHermesMessages({\n envelope: run.envelope,\n systemPrompt: this.config.systemPrompt,\n }),\n tools: run.envelope.tools,\n });\n if (!response.body) {\n throw new Error(\"[hermes-http-adapter] Hermes stream response body is missing.\");\n }\n this.sessions.setSelectedModel(run.envelope.sessionId, requestedModel);\n\n return {\n events: this.createRunEvents({\n response,\n run,\n signal,\n }),\n };\n };\n\n private createRunEvents = async function* (\n this: HermesHttpAdapterRouteService,\n params: {\n response: Response;\n run: HermesHttpAdapterRun;\n signal: AbortSignal;\n },\n ): AsyncGenerator<NcpEndpointEvent> {\n const { response, run, signal } = params;\n const sessionId = run.envelope.sessionId;\n const requestedModel = normalizeHermesRequestedModel(resolveHermesModel({\n envelope: run.envelope,\n fallbackModel: this.config.model,\n }));\n const metadata = {\n hermesModel: requestedModel,\n };\n let emittedUserVisibleAssistantEvent = false;\n const messageCollector = new HermesAssistantEventCollector();\n const inlineToolTraceTranslator = new HermesInlineToolTraceTranslator();\n const reasoningDeltaTranslator = new HermesReasoningDeltaTranslator();\n\n yield {\n type: NcpEventType.MessageAccepted,\n payload: {\n messageId: run.messageId,\n correlationId: run.envelope.correlationId,\n },\n };\n yield {\n type: NcpEventType.RunStarted,\n payload: {\n sessionId,\n messageId: run.messageId,\n runId: run.runId,\n },\n };\n\n try {\n for await (const event of reasoningDeltaTranslator.translate(\n inlineToolTraceTranslator.translate(\n this.streamEncoder.encode(\n parseHermesOpenAIChatStream(response.body as ReadableStream<Uint8Array>),\n {\n sessionId,\n messageId: run.messageId,\n runId: run.runId,\n correlationId: run.envelope.correlationId,\n },\n ),\n ),\n )) {\n messageCollector.applyEvent(event);\n if (isUserVisibleAssistantEvent(event)) {\n emittedUserVisibleAssistantEvent = true;\n }\n yield event;\n }\n\n if (!emittedUserVisibleAssistantEvent || !messageCollector.hasParts()) {\n throw new Error(\n `[hermes-http-adapter] Hermes completed without any assistant content for session ${sessionId}.`,\n );\n }\n\n const message = this.buildAssistantMessage({\n sessionId,\n messageId: run.messageId,\n parts: messageCollector.buildParts(),\n timestamp: new Date().toISOString(),\n metadata,\n });\n yield {\n type: NcpEventType.MessageCompleted,\n payload: {\n sessionId,\n message,\n correlationId: run.envelope.correlationId,\n metadata,\n },\n };\n yield {\n type: NcpEventType.RunFinished,\n payload: {\n sessionId,\n messageId: run.messageId,\n runId: run.runId,\n },\n };\n } catch (error) {\n if (signal.aborted) {\n const abortError = toNcpError(error, \"abort-error\");\n yield {\n type: NcpEventType.MessageFailed,\n payload: {\n sessionId,\n messageId: run.messageId,\n correlationId: run.envelope.correlationId,\n error: abortError,\n },\n };\n yield {\n type: NcpEventType.RunError,\n payload: {\n sessionId,\n messageId: run.messageId,\n runId: run.runId,\n error: abortError.message,\n },\n };\n return;\n }\n\n const runtimeError = toNcpError(error, \"runtime-error\");\n yield {\n type: NcpEventType.MessageFailed,\n payload: {\n sessionId,\n messageId: run.messageId,\n correlationId: run.envelope.correlationId,\n error: runtimeError,\n },\n };\n yield {\n type: NcpEventType.RunError,\n payload: {\n sessionId,\n messageId: run.messageId,\n runId: run.runId,\n error: runtimeError.message,\n },\n };\n }\n };\n\n private ensureHermesSessionModel = async (params: {\n sessionId: string;\n requestedModel: string;\n signal: AbortSignal;\n }): Promise<void> => {\n const { requestedModel, sessionId, signal } = params;\n const selectedModel = this.sessions.readSelectedModel(sessionId) ?? this.config.model;\n if (selectedModel === requestedModel) {\n return;\n }\n\n const response = await this.fetchHermesChatCompletion({\n sessionId,\n signal,\n stream: false,\n model: requestedModel,\n messages: [\n {\n role: \"user\",\n content: `/model ${requestedModel}`,\n },\n ],\n });\n\n const payload = await this.readJsonResponse<{\n choices?: Array<{\n message?: {\n content?: unknown;\n };\n }>;\n }>(response);\n const content = payload.choices?.[0]?.message?.content;\n if (typeof content === \"string\" && content.trim().startsWith(\"Error:\")) {\n throw new Error(\n `[hermes-http-adapter] failed to switch Hermes model to ${requestedModel}: ${content.trim()}`,\n );\n }\n\n this.sessions.setSelectedModel(sessionId, requestedModel);\n };\n\n private fetchHermesChatCompletion = async (params: {\n sessionId: string;\n signal: AbortSignal;\n stream: boolean;\n model: string;\n providerRoute?: HermesProviderRoute;\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>;\n tools?: ReadonlyArray<OpenAITool>;\n }): Promise<Response> => {\n const { messages, model, providerRoute, sessionId, signal, stream, tools } = params;\n const headers = new Headers({\n \"content-type\": \"application/json\",\n accept: stream ? \"text/event-stream\" : \"application/json\",\n });\n if (this.config.hermesApiKey) {\n headers.set(\"authorization\", `Bearer ${this.config.hermesApiKey}`);\n }\n\n const hermesSessionId = this.sessions.readHermesSessionId(sessionId);\n if (hermesSessionId) {\n headers.set(\"x-hermes-session-id\", hermesSessionId);\n }\n\n const response = await this.fetchImpl(this.config.chatCompletionsUrl, {\n method: \"POST\",\n headers,\n signal,\n body: JSON.stringify({\n model,\n stream,\n messages,\n ...(providerRoute\n ? {\n nextclaw_provider_route:\n this.toHermesProviderRoutePayload(providerRoute),\n }\n : {}),\n ...(tools && tools.length > 0 ? { tools } : {}),\n }),\n });\n if (!response.ok) {\n throw new Error(\n `[hermes-http-adapter] Hermes request failed with HTTP ${response.status}.`,\n );\n }\n\n const returnedSessionId = response.headers.get(\"x-hermes-session-id\")?.trim();\n if (returnedSessionId) {\n this.sessions.setHermesSessionId(sessionId, returnedSessionId);\n }\n\n return response;\n };\n\n private readProviderRoute = (\n envelope: NcpRequestEnvelope,\n ): HermesProviderRoute | undefined => {\n const providerRoute = readHermesProviderRoute(envelope);\n if (!providerRoute) {\n return undefined;\n }\n if (!providerRoute.apiBase) {\n throw new Error(\n `[hermes-http-adapter] missing provider apiBase for model \"${providerRoute.model}\". Configure the selected NextClaw provider before using Hermes.`,\n );\n }\n return providerRoute;\n };\n\n private toHermesProviderRoutePayload = (\n providerRoute: HermesProviderRoute,\n ): Record<string, unknown> => {\n const inferredProvider = inferHermesProvider(providerRoute);\n return {\n model: providerRoute.model,\n ...(inferredProvider ? { provider: inferredProvider } : {}),\n ...(providerRoute.apiKey ? { api_key: providerRoute.apiKey } : {}),\n ...(providerRoute.apiBase ? { base_url: providerRoute.apiBase } : {}),\n ...(providerRoute.apiMode ? { api_mode: providerRoute.apiMode } : {}),\n ...(Object.keys(providerRoute.headers).length > 0\n ? { extra_headers: providerRoute.headers }\n : {}),\n };\n };\n\n private readJsonResponse = async <T>(response: Response): Promise<T> => {\n try {\n return (await response.json()) as T;\n } catch (error) {\n throw new Error(\n `[hermes-http-adapter] expected JSON response from Hermes: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n };\n\n private readJsonBody = async <T>(request: IncomingMessage): Promise<T> => {\n const chunks: Buffer[] = [];\n for await (const chunk of request) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n const body = Buffer.concat(chunks).toString(\"utf8\");\n if (!body.trim()) {\n throw new Error(\"request body is empty\");\n }\n return JSON.parse(body) as T;\n };\n\n private writeJson = (\n response: ServerResponse,\n statusCode: number,\n payload: unknown,\n ): void => {\n response.writeHead(statusCode, {\n \"content-type\": \"application/json; charset=utf-8\",\n });\n response.end(JSON.stringify(payload));\n };\n}\n\nexport class HermesHttpAdapterServer {\n readonly config: HermesHttpAdapterResolvedConfig;\n\n private readonly routeService: HermesHttpAdapterRouteService;\n private server: Server | null = null;\n\n constructor(configInput: HermesHttpAdapterConfigInput = {}) {\n this.config = new HermesHttpAdapterConfigResolver(configInput).resolve();\n this.routeService = new HermesHttpAdapterRouteService(this.config);\n }\n\n start = async (): Promise<void> => {\n if (this.server) {\n return;\n }\n const nextServer = createServer((request, response) => {\n void this.routeService.handle(request, response);\n });\n await new Promise<void>((resolve, reject) => {\n nextServer.once(\"error\", reject);\n nextServer.listen(this.config.port, this.config.host, () => {\n nextServer.off(\"error\", reject);\n resolve();\n });\n });\n this.server = nextServer;\n };\n\n stop = async (): Promise<void> => {\n this.routeService.dispose();\n const activeServer = this.server;\n if (!activeServer) {\n return;\n }\n await new Promise<void>((resolve, reject) => {\n activeServer.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n this.server = null;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"hermes-http-adapter.service.js","names":[],"sources":["../src/hermes-http-adapter.service.ts"],"sourcesContent":["import { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { randomUUID } from \"node:crypto\";\nimport { DefaultNcpStreamEncoder } from \"@nextclaw/ncp-agent-runtime\";\nimport {\n type NcpEndpointEvent,\n type NcpRequestEnvelope,\n type OpenAITool,\n} from \"@nextclaw/ncp\";\nimport { HermesHttpAdapterConfigResolver } from \"./hermes-http-adapter-config.utils.js\";\nimport {\n buildHermesMessages,\n createAssistantMessageFromParts,\n HermesAssistantEventCollector,\n HermesInlineToolTraceTranslator,\n HermesReasoningDeltaTranslator,\n inferHermesProvider,\n normalizeHermesRequestedModel,\n readHermesProviderRoute,\n resolveHermesModel,\n type HermesProviderRoute,\n toErrorSseFrame,\n toNcpError,\n toNcpSseFrame,\n} from \"./hermes-http-adapter-message.utils.js\";\nimport { parseHermesOpenAIChatStream } from \"./hermes-openai-stream-parser.utils.js\";\nimport { HermesHttpAdapterSessionStore } from \"./hermes-http-adapter-session-store.service.js\";\nimport {\n createHermesMessageAcceptedEvent,\n createHermesMessageCompletedEvent,\n createHermesMessageFailedEvent,\n createHermesRunErrorEvent,\n createHermesRunFinishedEvent,\n createHermesRunHandle,\n createHermesRunStartedEvent,\n} from \"./hermes-http-adapter-events.utils.js\";\nimport type {\n HermesAdapterAssistantMessageFactory,\n HermesHttpAdapterResolvedConfig,\n HermesHttpAdapterRun,\n HermesHttpAdapterRunResult,\n} from \"./hermes-http-adapter.types.js\";\nimport type { HermesHttpAdapterConfigInput } from \"./hermes-http-adapter-config.utils.js\";\n\nfunction isUserVisibleAssistantEvent(event: NcpEndpointEvent): boolean {\n const eventType = event.type;\n return (\n eventType.startsWith(\"message.text-\") ||\n eventType.startsWith(\"message.reasoning-\") ||\n eventType.startsWith(\"message.tool-call-\")\n );\n}\n\nclass HermesHttpAdapterRouteService {\n private readonly sessions = new HermesHttpAdapterSessionStore();\n private readonly streamEncoder = new DefaultNcpStreamEncoder({\n reasoningNormalizationMode: \"think-tags\",\n });\n private readonly fetchImpl: typeof fetch;\n\n constructor(\n private readonly config: HermesHttpAdapterResolvedConfig,\n private readonly buildAssistantMessage: HermesAdapterAssistantMessageFactory = createAssistantMessageFromParts,\n ) {\n this.fetchImpl = config.fetchImpl ?? fetch;\n }\n\n handle = async (\n request: IncomingMessage,\n response: ServerResponse,\n ): Promise<void> => {\n const requestUrl = new URL(request.url ?? \"/\", this.baseOrigin);\n const normalizedPath = requestUrl.pathname;\n\n if (request.method === \"GET\" && normalizedPath === \"/health\") {\n await this.handleHealth(response);\n return;\n }\n\n if (request.method === \"POST\" && normalizedPath === `${this.config.basePath}/send`) {\n await this.handleSend(request, response);\n return;\n }\n\n if (request.method === \"GET\" && normalizedPath === `${this.config.basePath}/stream`) {\n await this.handleStream(request, response, requestUrl);\n return;\n }\n\n if (request.method === \"POST\" && normalizedPath === `${this.config.basePath}/abort`) {\n await this.handleAbort(request, response);\n return;\n }\n\n this.writeJson(response, 404, {\n ok: false,\n error: {\n code: \"NOT_FOUND\",\n message: `Unknown path: ${normalizedPath}`,\n },\n });\n };\n\n dispose = (): void => {\n this.sessions.dispose();\n };\n\n private get baseOrigin(): string {\n return `http://${this.config.host}:${this.config.port}`;\n }\n\n private handleHealth = async (response: ServerResponse): Promise<void> => {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.healthcheckTimeoutMs);\n\n try {\n const upstream = await this.fetchImpl(this.config.healthcheckUrl, {\n method: \"GET\",\n signal: controller.signal,\n });\n if (!upstream.ok) {\n this.writeJson(response, 503, {\n status: \"degraded\",\n adapter: \"ok\",\n upstream: {\n status: \"error\",\n httpStatus: upstream.status,\n },\n });\n return;\n }\n this.writeJson(response, 200, {\n status: \"ok\",\n adapter: \"ok\",\n upstream: \"ok\",\n });\n } catch (error) {\n this.writeJson(response, 503, {\n status: \"degraded\",\n adapter: \"ok\",\n upstream: {\n status: \"unreachable\",\n message: error instanceof Error ? error.message : String(error),\n },\n });\n } finally {\n clearTimeout(timeout);\n }\n };\n\n private handleSend = async (\n request: IncomingMessage,\n response: ServerResponse,\n ): Promise<void> => {\n try {\n const envelope = await this.readJsonBody<NcpRequestEnvelope>(request);\n if (!envelope?.sessionId || !envelope.message) {\n this.writeJson(response, 400, {\n ok: false,\n error: {\n code: \"INVALID_BODY\",\n message: \"sessionId and message are required.\",\n },\n });\n return;\n }\n\n const run: HermesHttpAdapterRun = {\n envelope,\n messageId: randomUUID(),\n runId: randomUUID(),\n };\n this.sessions.setPendingRun(run);\n this.writeJson(response, 200, { ok: true, data: createHermesRunHandle(run) });\n } catch (error) {\n this.writeJson(response, 409, {\n ok: false,\n error: {\n code: \"RUN_CONFLICT\",\n message: error instanceof Error ? error.message : String(error),\n },\n });\n }\n };\n\n private handleAbort = async (\n request: IncomingMessage,\n response: ServerResponse,\n ): Promise<void> => {\n try {\n const payload = await this.readJsonBody<{ sessionId?: string }>(request);\n const sessionId = typeof payload?.sessionId === \"string\" ? payload.sessionId : \"\";\n if (!sessionId.trim()) {\n this.writeJson(response, 400, {\n ok: false,\n error: {\n code: \"INVALID_BODY\",\n message: \"sessionId is required.\",\n },\n });\n return;\n }\n const aborted = this.sessions.abortSession(sessionId);\n this.writeJson(response, 200, { ok: true, aborted });\n } catch (error) {\n this.writeJson(response, 500, {\n ok: false,\n error: {\n code: \"ABORT_FAILED\",\n message: error instanceof Error ? error.message : String(error),\n },\n });\n }\n };\n\n private handleStream = async (\n request: IncomingMessage,\n response: ServerResponse,\n requestUrl: URL,\n ): Promise<void> => {\n const sessionId = requestUrl.searchParams.get(\"sessionId\")?.trim();\n if (!sessionId) {\n this.writeJson(response, 400, {\n ok: false,\n error: {\n code: \"INVALID_QUERY\",\n message: \"sessionId is required.\",\n },\n });\n return;\n }\n\n const connectionController = new AbortController();\n const runController = new AbortController();\n response.once(\"close\", () => {\n connectionController.abort(\"client disconnected\");\n runController.abort(\"client disconnected\");\n });\n\n response.writeHead(200, {\n \"content-type\": \"text/event-stream; charset=utf-8\",\n \"cache-control\": \"no-cache, no-transform\",\n connection: \"keep-alive\",\n \"x-accel-buffering\": \"no\",\n });\n\n try {\n const run = await this.sessions.waitForPendingRun(\n sessionId,\n connectionController.signal,\n this.config.streamWaitTimeoutMs,\n );\n this.sessions.setActiveAbortController(sessionId, runController);\n\n const streamResult = await this.startRun({\n run,\n signal: runController.signal,\n });\n\n for await (const event of streamResult.events) {\n if (connectionController.signal.aborted) {\n break;\n }\n response.write(toNcpSseFrame(event));\n }\n } catch (error) {\n if (!connectionController.signal.aborted) {\n response.write(\n toErrorSseFrame({\n code: \"STREAM_FAILED\",\n message: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n } finally {\n this.sessions.clearActiveAbortController(sessionId);\n response.end();\n }\n };\n\n private startRun = async (params: {\n run: HermesHttpAdapterRun;\n signal: AbortSignal;\n }): Promise<HermesHttpAdapterRunResult> => {\n const { run, signal } = params;\n const providerRoute = this.readProviderRoute(run.envelope);\n const requestedModel = normalizeHermesRequestedModel(resolveHermesModel({\n envelope: run.envelope,\n fallbackModel: this.config.model,\n }));\n if (!providerRoute) {\n await this.ensureHermesSessionModel({\n sessionId: run.envelope.sessionId,\n requestedModel,\n signal,\n });\n }\n\n const response = await this.fetchHermesChatCompletion({\n sessionId: run.envelope.sessionId,\n signal,\n stream: true,\n model: requestedModel,\n providerRoute,\n messages: buildHermesMessages({\n envelope: run.envelope,\n systemPrompt: this.config.systemPrompt,\n }),\n tools: run.envelope.tools,\n });\n if (!response.body) {\n throw new Error(\"[hermes-http-adapter] Hermes stream response body is missing.\");\n }\n this.sessions.setSelectedModel(run.envelope.sessionId, requestedModel);\n\n return {\n events: this.createRunEvents({\n response,\n run,\n signal,\n }),\n };\n };\n\n private createRunEvents = async function* (\n this: HermesHttpAdapterRouteService,\n params: {\n response: Response;\n run: HermesHttpAdapterRun;\n signal: AbortSignal;\n },\n ): AsyncGenerator<NcpEndpointEvent> {\n const { response, run, signal } = params;\n const sessionId = run.envelope.sessionId;\n const requestedModel = normalizeHermesRequestedModel(resolveHermesModel({\n envelope: run.envelope,\n fallbackModel: this.config.model,\n }));\n const metadata = {\n hermesModel: requestedModel,\n };\n let emittedUserVisibleAssistantEvent = false;\n const messageCollector = new HermesAssistantEventCollector();\n const inlineToolTraceTranslator = new HermesInlineToolTraceTranslator();\n const reasoningDeltaTranslator = new HermesReasoningDeltaTranslator();\n const startedAt = new Date().toISOString();\n\n yield createHermesMessageAcceptedEvent(run);\n yield createHermesRunStartedEvent({ run, startedAt });\n\n try {\n for await (const event of reasoningDeltaTranslator.translate(\n inlineToolTraceTranslator.translate(\n this.streamEncoder.encode(\n parseHermesOpenAIChatStream(response.body as ReadableStream<Uint8Array>),\n {\n sessionId,\n messageId: run.messageId,\n runId: run.runId,\n correlationId: run.envelope.correlationId,\n },\n ),\n ),\n )) {\n messageCollector.applyEvent(event);\n if (isUserVisibleAssistantEvent(event)) {\n emittedUserVisibleAssistantEvent = true;\n }\n yield event;\n }\n\n if (!emittedUserVisibleAssistantEvent || !messageCollector.hasParts()) {\n throw new Error(\n `[hermes-http-adapter] Hermes completed without any assistant content for session ${sessionId}.`,\n );\n }\n\n const message = this.buildAssistantMessage({\n sessionId,\n messageId: run.messageId,\n parts: messageCollector.buildParts(),\n timestamp: new Date().toISOString(),\n metadata,\n });\n yield createHermesMessageCompletedEvent({ message, metadata, run });\n const endedAt = new Date().toISOString();\n yield createHermesRunFinishedEvent({ endedAt, run, startedAt });\n } catch (error) {\n yield* this.createRunErrorEvents({\n error,\n run,\n startedAt,\n fallbackCode: signal.aborted ? \"abort-error\" : \"runtime-error\",\n });\n }\n };\n\n private createRunErrorEvents = function* (\n this: HermesHttpAdapterRouteService,\n params: {\n error: unknown;\n fallbackCode: \"abort-error\" | \"runtime-error\";\n run: HermesHttpAdapterRun;\n startedAt: string;\n },\n ): Generator<NcpEndpointEvent> {\n const { error, fallbackCode, run, startedAt } = params;\n const ncpError = toNcpError(error, fallbackCode);\n yield createHermesMessageFailedEvent({ error: ncpError, run });\n const endedAt = new Date().toISOString();\n yield createHermesRunErrorEvent({ endedAt, error: ncpError, run, startedAt });\n };\n\n private ensureHermesSessionModel = async (params: {\n sessionId: string;\n requestedModel: string;\n signal: AbortSignal;\n }): Promise<void> => {\n const { requestedModel, sessionId, signal } = params;\n const selectedModel = this.sessions.readSelectedModel(sessionId) ?? this.config.model;\n if (selectedModel === requestedModel) {\n return;\n }\n\n const response = await this.fetchHermesChatCompletion({\n sessionId,\n signal,\n stream: false,\n model: requestedModel,\n messages: [\n {\n role: \"user\",\n content: `/model ${requestedModel}`,\n },\n ],\n });\n\n const payload = await this.readJsonResponse<{\n choices?: Array<{\n message?: {\n content?: unknown;\n };\n }>;\n }>(response);\n const content = payload.choices?.[0]?.message?.content;\n if (typeof content === \"string\" && content.trim().startsWith(\"Error:\")) {\n throw new Error(\n `[hermes-http-adapter] failed to switch Hermes model to ${requestedModel}: ${content.trim()}`,\n );\n }\n\n this.sessions.setSelectedModel(sessionId, requestedModel);\n };\n\n private fetchHermesChatCompletion = async (params: {\n sessionId: string;\n signal: AbortSignal;\n stream: boolean;\n model: string;\n providerRoute?: HermesProviderRoute;\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>;\n tools?: ReadonlyArray<OpenAITool>;\n }): Promise<Response> => {\n const { messages, model, providerRoute, sessionId, signal, stream, tools } = params;\n const headers = new Headers({\n \"content-type\": \"application/json\",\n accept: stream ? \"text/event-stream\" : \"application/json\",\n });\n if (this.config.hermesApiKey) {\n headers.set(\"authorization\", `Bearer ${this.config.hermesApiKey}`);\n }\n\n const hermesSessionId = this.sessions.readHermesSessionId(sessionId);\n if (hermesSessionId) {\n headers.set(\"x-hermes-session-id\", hermesSessionId);\n }\n\n const response = await this.fetchImpl(this.config.chatCompletionsUrl, {\n method: \"POST\",\n headers,\n signal,\n body: JSON.stringify({\n model,\n stream,\n messages,\n ...(providerRoute\n ? {\n nextclaw_provider_route:\n this.toHermesProviderRoutePayload(providerRoute),\n }\n : {}),\n ...(tools && tools.length > 0 ? { tools } : {}),\n }),\n });\n if (!response.ok) {\n throw new Error(\n `[hermes-http-adapter] Hermes request failed with HTTP ${response.status}.`,\n );\n }\n\n const returnedSessionId = response.headers.get(\"x-hermes-session-id\")?.trim();\n if (returnedSessionId) {\n this.sessions.setHermesSessionId(sessionId, returnedSessionId);\n }\n\n return response;\n };\n\n private readProviderRoute = (\n envelope: NcpRequestEnvelope,\n ): HermesProviderRoute | undefined => {\n const providerRoute = readHermesProviderRoute(envelope);\n if (!providerRoute) {\n return undefined;\n }\n if (!providerRoute.apiBase) {\n throw new Error(\n `[hermes-http-adapter] missing provider apiBase for model \"${providerRoute.model}\". Configure the selected NextClaw provider before using Hermes.`,\n );\n }\n return providerRoute;\n };\n\n private toHermesProviderRoutePayload = (\n providerRoute: HermesProviderRoute,\n ): Record<string, unknown> => {\n const inferredProvider = inferHermesProvider(providerRoute);\n return {\n model: providerRoute.model,\n ...(inferredProvider ? { provider: inferredProvider } : {}),\n ...(providerRoute.apiKey ? { api_key: providerRoute.apiKey } : {}),\n ...(providerRoute.apiBase ? { base_url: providerRoute.apiBase } : {}),\n ...(providerRoute.apiMode ? { api_mode: providerRoute.apiMode } : {}),\n ...(Object.keys(providerRoute.headers).length > 0\n ? { extra_headers: providerRoute.headers }\n : {}),\n };\n };\n\n private readJsonResponse = async <T>(response: Response): Promise<T> => {\n try {\n return (await response.json()) as T;\n } catch (error) {\n throw new Error(\n `[hermes-http-adapter] expected JSON response from Hermes: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n };\n\n private readJsonBody = async <T>(request: IncomingMessage): Promise<T> => {\n const chunks: Buffer[] = [];\n for await (const chunk of request) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n const body = Buffer.concat(chunks).toString(\"utf8\");\n if (!body.trim()) {\n throw new Error(\"request body is empty\");\n }\n return JSON.parse(body) as T;\n };\n\n private writeJson = (\n response: ServerResponse,\n statusCode: number,\n payload: unknown,\n ): void => {\n response.writeHead(statusCode, {\n \"content-type\": \"application/json; charset=utf-8\",\n });\n response.end(JSON.stringify(payload));\n };\n}\n\nexport class HermesHttpAdapterServer {\n readonly config: HermesHttpAdapterResolvedConfig;\n\n private readonly routeService: HermesHttpAdapterRouteService;\n private server: Server | null = null;\n\n constructor(configInput: HermesHttpAdapterConfigInput = {}) {\n this.config = new HermesHttpAdapterConfigResolver(configInput).resolve();\n this.routeService = new HermesHttpAdapterRouteService(this.config);\n }\n\n start = async (): Promise<void> => {\n if (this.server) {\n return;\n }\n const nextServer = createServer((request, response) => {\n void this.routeService.handle(request, response);\n });\n await new Promise<void>((resolve, reject) => {\n nextServer.once(\"error\", reject);\n nextServer.listen(this.config.port, this.config.host, () => {\n nextServer.off(\"error\", reject);\n resolve();\n });\n });\n this.server = nextServer;\n };\n\n stop = async (): Promise<void> => {\n this.routeService.dispose();\n const activeServer = this.server;\n if (!activeServer) {\n return;\n }\n await new Promise<void>((resolve, reject) => {\n activeServer.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n this.server = null;\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ //#region src/hermes-inline-tool-trace.utils.ts
2
+ const HERMES_INLINE_TOOL_TRACE_PATTERNS = [{
3
+ icon: "🔎",
4
+ toolName: "search_files",
5
+ buildArgs: (rawArgs) => JSON.stringify({ pattern: rawArgs })
6
+ }, {
7
+ icon: "💻",
8
+ toolName: "terminal",
9
+ buildArgs: (rawArgs) => JSON.stringify({ command: rawArgs })
10
+ }];
11
+ function matchHermesInlineToolTrace(segment) {
12
+ const trimmed = segment.trim();
13
+ if (!trimmed.startsWith("`") || !trimmed.endsWith("`")) return null;
14
+ const body = trimmed.slice(1, -1).trim();
15
+ for (const candidate of HERMES_INLINE_TOOL_TRACE_PATTERNS) {
16
+ if (!body.startsWith(candidate.icon)) continue;
17
+ const rawArgs = body.slice(candidate.icon.length).trim();
18
+ if (!rawArgs) return null;
19
+ return {
20
+ toolName: candidate.toolName,
21
+ args: candidate.buildArgs(rawArgs)
22
+ };
23
+ }
24
+ return null;
25
+ }
26
+ //#endregion
27
+ export { matchHermesInlineToolTrace };
28
+
29
+ //# sourceMappingURL=hermes-inline-tool-trace.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermes-inline-tool-trace.utils.js","names":[],"sources":["../src/hermes-inline-tool-trace.utils.ts"],"sourcesContent":["export type HermesInlineToolTrace = {\n toolName: string;\n args: string;\n};\n\nconst HERMES_INLINE_TOOL_TRACE_PATTERNS: Array<{\n icon: string;\n toolName: string;\n buildArgs: (rawArgs: string) => string;\n}> = [\n {\n icon: \"🔎\",\n toolName: \"search_files\",\n buildArgs: (rawArgs) => JSON.stringify({ pattern: rawArgs }),\n },\n {\n icon: \"💻\",\n toolName: \"terminal\",\n buildArgs: (rawArgs) => JSON.stringify({ command: rawArgs }),\n },\n];\n\nexport function matchHermesInlineToolTrace(segment: string): HermesInlineToolTrace | null {\n const trimmed = segment.trim();\n if (!trimmed.startsWith(\"`\") || !trimmed.endsWith(\"`\")) {\n return null;\n }\n\n const body = trimmed.slice(1, -1).trim();\n for (const candidate of HERMES_INLINE_TOOL_TRACE_PATTERNS) {\n if (!body.startsWith(candidate.icon)) {\n continue;\n }\n const rawArgs = body.slice(candidate.icon.length).trim();\n if (!rawArgs) {\n return null;\n }\n return {\n toolName: candidate.toolName,\n args: candidate.buildArgs(rawArgs),\n };\n }\n\n return null;\n}\n"],"mappings":";AAKA,MAAM,oCAID,CACH;CACE,MAAM;CACN,UAAU;CACV,YAAY,YAAY,KAAK,UAAU,EAAE,SAAS,SAAS,CAAC;CAC7D,EACD;CACE,MAAM;CACN,UAAU;CACV,YAAY,YAAY,KAAK,UAAU,EAAE,SAAS,SAAS,CAAC;CAC7D,CACF;AAED,SAAgB,2BAA2B,SAA+C;CACxF,MAAM,UAAU,QAAQ,MAAM;AAC9B,KAAI,CAAC,QAAQ,WAAW,IAAI,IAAI,CAAC,QAAQ,SAAS,IAAI,CACpD,QAAO;CAGT,MAAM,OAAO,QAAQ,MAAM,GAAG,GAAG,CAAC,MAAM;AACxC,MAAK,MAAM,aAAa,mCAAmC;AACzD,MAAI,CAAC,KAAK,WAAW,UAAU,KAAK,CAClC;EAEF,MAAM,UAAU,KAAK,MAAM,UAAU,KAAK,OAAO,CAAC,MAAM;AACxD,MAAI,CAAC,QACH,QAAO;AAET,SAAO;GACL,UAAU,UAAU;GACpB,MAAM,UAAU,UAAU,QAAQ;GACnC;;AAGH,QAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/nextclaw-ncp-runtime-adapter-hermes-http",
3
- "version": "0.2.16",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "description": "Standalone NCP HTTP adapter server that bridges NextClaw http-runtime sessions to Hermes API Server.",
6
6
  "type": "module",
@@ -19,9 +19,9 @@
19
19
  "README.md"
20
20
  ],
21
21
  "dependencies": {
22
- "@nextclaw/ncp-agent-runtime": "0.3.47",
23
- "@nextclaw/ncp": "0.6.6",
24
- "@nextclaw/ncp-http-agent-client": "0.3.48"
22
+ "@nextclaw/ncp": "0.7.1",
23
+ "@nextclaw/ncp-agent-runtime": "0.4.1",
24
+ "@nextclaw/ncp-http-agent-client": "0.4.1"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^20.17.6",