@nextclaw/nextclaw-ncp-runtime-adapter-hermes-http 0.2.7 → 0.2.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"hermes-http-adapter-message.utils.d.ts","names":[],"sources":["../src/hermes-http-adapter-message.utils.ts"],"mappings":";;;;KAiEY,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,QAMA,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":";;;;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"}
@@ -303,7 +303,7 @@ var HermesReasoningDeltaTranslator = class {
303
303
  if (this.shouldFlushPendingText(event)) yield* this.flushPendingText(event);
304
304
  yield event;
305
305
  }
306
- yield* this.flushSegments();
306
+ this.flushSegments();
307
307
  };
308
308
  emitNormalizedTextDelta = function* (event) {
309
309
  const segments = this.normalizer.push(event.payload.delta);
@@ -334,7 +334,7 @@ var HermesReasoningDeltaTranslator = class {
334
334
  const sessionId = this.readEventSessionId(event);
335
335
  const messageId = this.readEventMessageId(event);
336
336
  if (!sessionId || !messageId) {
337
- yield* this.flushSegments();
337
+ this.flushSegments();
338
338
  return;
339
339
  }
340
340
  const segments = this.normalizer.finish();
@@ -361,7 +361,7 @@ var HermesReasoningDeltaTranslator = class {
361
361
  };
362
362
  }
363
363
  };
364
- flushSegments = function* () {
364
+ flushSegments = () => {
365
365
  this.normalizer.finish();
366
366
  };
367
367
  shouldFlushPendingText = (event) => event.type === NcpEventType.MessageTextEnd || event.type === NcpEventType.MessageCompleted || event.type === NcpEventType.MessageFailed || event.type === NcpEventType.RunFinished || event.type === NcpEventType.RunError;
@@ -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 NcpProviderRuntimeRoute,\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 yield* 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 yield* 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 = function* (\n this: HermesReasoningDeltaTranslator,\n ): Generator<NcpEndpointEvent> {\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":";;AAkBA,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,SAAS,mBACP,UACA,KACoB;CACpB,MAAM,QAAQ,WAAW;AACzB,KAAI,OAAO,UAAU,SACnB;CAEF,MAAM,UAAU,MAAM,MAAM;AAC5B,QAAO,QAAQ,SAAS,IAAI,UAAU,KAAA;;AAGxC,SAAS,iBAAiB,OAAoD;AAC5E,KAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,CAC7D;CAEF,MAAM,MAA8B,EAAE;AACtC,MAAK,MAAM,CAAC,UAAU,eAAe,OAAO,QAAQ,MAAM,EAAE;AAC1D,MAAI,OAAO,eAAe,SACxB;EAEF,MAAM,aAAa,WAAW,MAAM;AACpC,MAAI,CAAC,WACH;AAEF,MAAI,YAAY;;AAElB,QAAO,OAAO,KAAK,IAAI,CAAC,SAAS,IAAI,MAAM,KAAA;;AAW7C,MAAM,uBAAuB;AAE7B,SAAgB,wBACd,UACiC;CACjC,MAAM,QAAQ,SAAS;AACvB,KAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,CAC7D;CAEF,MAAM,cAAc;CAEpB,MAAM,QACJ,mBAAmB,aAAa,QAAQ,IACxC,mBAAmB,aAAa,qBAAqB;AACvD,KAAI,CAAC,MACH;AAGF,QAAO;EACL;EACA,QAAQ,mBAAmB,aAAa,SAAS;EACjD,SAAS,mBAAmB,aAAa,UAAU;EACnD,GAAG,sBACD,iBAAiB,YAAY,QAAQ,IACrC,iBAAiB,YAAY,aAAa,IAC1C,EAAE,CACH;EACF;;AAGH,SAAgB,oBAAoB,QAGV;CACxB,MAAM,EAAE,UAAU,iBAAiB;CACnC,MAAM,WAAkC,EAAE;AAC1C,KAAI,aACF,UAAS,KAAK;EACZ,MAAM;EACN,SAAS;EACV,CAAC;CAEJ,MAAM,UAAU,mBAAmB,SAAS,QAAQ;AACpD,UAAS,KAAK;EACZ,MAAM,cAAc,SAAS,QAAQ,KAAK;EAC1C;EACD,CAAC;AACF,QAAO;;AAGT,SAAgB,uBAAuB,QAMxB;CACb,MAAM,EAAE,WAAW,WAAW,MAAM,WAAW,aAAa;AAC5D,QAAO;EACL,IAAI;EACJ;EACA,MAAM;EACN,QAAQ;EACR,OAAO,CACL;GACE,MAAM;GACN;GACD,CACF;EACD;EACA,GAAI,WAAW,EAAE,UAAU,GAAG,EAAE;EACjC;;AAGH,SAAgB,gCAAgC,QAMjC;CACb,MAAM,EAAE,WAAW,WAAW,OAAO,WAAW,aAAa;AAC7D,QAAO;EACL,IAAI;EACJ;EACA,MAAM;EACN,QAAQ;EACR,OAAO,gBAAgB,MAAM;EAC7B;EACA,GAAI,WAAW,EAAE,UAAU,GAAG,EAAE;EACjC;;AAGH,IAAa,gCAAb,MAA2C;CACzC,QAA2C,EAAE;CAC7C,wCAAyC,IAAI,KAAqB;CAElE,cAAc,UAAkC;AAC9C,UAAQ,MAAM,MAAd;GACE,KAAK,aAAa;AAChB,SAAK,eAAe,MAAM,QAAQ,MAAM;AACxC;GACF,KAAK,aAAa;AAChB,SAAK,oBAAoB,MAAM,QAAQ,MAAM;AAC7C;GACF,KAAK,aAAa;AAChB,SAAK,yBAAyB;KAC5B,MAAM;KACN,YAAY,MAAM,QAAQ;KAC1B,UAAU,MAAM,QAAQ;KACxB,OAAO;KACP,MAAM;KACP,CAAC;AACF;GACF,KAAK,aAAa;AAChB,SAAK,uBACH,MAAM,QAAQ,YACd,MAAM,QAAQ,MACf;AACD;GACF,KAAK,aAAa;AAChB,SAAK,yBAAyB;KAC5B,MAAM;KACN,YAAY,MAAM,QAAQ;KAC1B,UAAU,KAAK,aAAa,MAAM,QAAQ,WAAW;KACrD,OAAO;KACP,MAAM,MAAM,QAAQ;KACrB,CAAC;AACF;GACF,KAAK,aAAa;AAChB,SAAK,yBAAyB;KAC5B,MAAM;KACN,YAAY,MAAM,QAAQ;KAC1B,UAAU,KAAK,aAAa,MAAM,QAAQ,WAAW;KACrD,OAAO;KACP,MAAM,KAAK,aAAa,MAAM,QAAQ,WAAW;KAClD,CAAC;AACF;GACF,QACE;;;CAIN,iBAA0B,KAAK,MAAM,SAAS;CAE9C,mBAAqC,gBAAgB,KAAK,MAAM;CAEhE,kBAA0B,UAAwB;AAChD,MAAI,CAAC,MACH;EAEF,MAAM,WAAW,KAAK,MAAM,GAAG,GAAG;AAClC,MAAI,UAAU,SAAS,QAAQ;AAC7B,YAAS,OAAO,GAAG,SAAS,OAAO;AACnC;;AAEF,OAAK,MAAM,KAAK;GACd,MAAM;GACN,MAAM;GACP,CAAC;;CAGJ,uBAA+B,UAAwB;AACrD,MAAI,CAAC,MACH;EAEF,MAAM,WAAW,KAAK,MAAM,GAAG,GAAG;AAClC,MAAI,UAAU,SAAS,aAAa;AAClC,YAAS,OAAO,GAAG,SAAS,OAAO;AACnC;;AAEF,OAAK,MAAM,KAAK;GACd,MAAM;GACN,MAAM;GACP,CAAC;;CAGJ,0BAAkC,YAAoB,UAAwB;EAC5E,MAAM,cAAc,KAAK,aAAa,WAAW;AACjD,OAAK,yBAAyB;GAC5B,MAAM;GACN;GACA,UAAU,KAAK,aAAa,WAAW;GACvC,OAAO;GACP,MAAM,GAAG,cAAc;GACxB,CAAC;;CAGJ,4BACE,aACS;EACT,MAAM,uBAAuB,SAAS,YAAY,MAAM;AACxD,MAAI,CAAC,sBAAsB;AACzB,QAAK,MAAM,KAAK,EACd,GAAG,UACJ,CAAC;AACF;;EAGF,MAAM,gBAAgB,KAAK,sBAAsB,IAAI,qBAAqB;AAC1E,MAAI,OAAO,kBAAkB,UAAU;GACrC,MAAM,eAAe,KAAK,MAAM;AAChC,OAAI,cAAc,SAAS,kBACzB;AAEF,QAAK,MAAM,iBAAiB;IAC1B,GAAG;IACH,GAAG;IACH,YAAY;IACZ,UAAU,SAAS,YAAY,aAAa;IAC5C,MAAM,SAAS,QAAQ,aAAa;IACrC;AACD;;AAGF,OAAK,sBAAsB,IAAI,sBAAsB,KAAK,MAAM,OAAO;AACvE,OAAK,MAAM,KAAK;GACd,GAAG;GACH,YAAY;GACb,CAAC;;CAGJ,gBAAwB,eAA+B;EACrD,MAAM,OAAO,KAAK,aAAa,WAAW;AAC1C,MAAI,CAAC,KACH,QAAO;AAET,SAAO,OAAO,KAAK,SAAS,WACxB,KAAK,OACL,KAAK,UAAU,KAAK,QAAQ,GAAG;;CAGrC,gBAAwB,eAA+B;AAErD,SADa,KAAK,aAAa,WAAW,EAC7B,YAAY;;CAG3B,gBACE,eACiC;EACjC,MAAM,gBAAgB,KAAK,sBAAsB,IAAI,WAAW;AAChE,MAAI,OAAO,kBAAkB,SAC3B,QAAO;EAET,MAAM,OAAO,KAAK,MAAM;AACxB,SAAO,MAAM,SAAS,oBAAoB,OAAO;;;AAIrD,IAAa,kCAAb,MAA6C;CAC3C,oBAA4B;CAC5B,wBAAgC;CAChC,gBAAwB;CAExB,YAAY,iBAEV,QACkC;AAClC,aAAW,MAAM,SAAS,QAAQ;AAChC,OAAI,KAAK,0BAA0B,MAAM,EAAE;AACzC,SAAK,wBAAwB;AAC7B,WAAO,KAAK,iBAAiB;KAC3B,WAAW,KAAK,mBAAmB,MAAM;KACzC,WAAW,KAAK,mBAAmB,MAAM;KACzC,OAAO;KACR,CAAC;AACF,UAAM;AACN;;AAGF,OACE,CAAC,KAAK,yBACN,MAAM,SAAS,aAAa,kBAC5B;AACA,SAAK,qBAAqB,MAAM,QAAQ;AACxC,WAAO,KAAK,iBAAiB;KAC3B,WAAW,MAAM,QAAQ;KACzB,WAAW,MAAM,QAAQ;KACzB,OAAO;KACR,CAAC;AACF;;AAGF,OACE,CAAC,KAAK,yBACN,KAAK,uBAAuB,MAAM,CAElC,QAAO,KAAK,iBAAiB;IAC3B,WAAW,KAAK,mBAAmB,MAAM;IACzC,WAAW,KAAK,mBAAmB,MAAM;IACzC,OAAO;IACR,CAAC;AAGJ,SAAM;;;CAIV,mBAA2B,WAEzB,QAK6B;AAC7B,MAAI,CAAC,KAAK,kBACR;EAGF,MAAM,mBAAmB,KAAK,kBAAkB,YAAY,KAAK;EACjE,MAAM,gBACJ,OAAO,QACH,KAAK,oBACL,oBAAoB,IAClB,KAAK,kBAAkB,MAAM,GAAG,mBAAmB,EAAE,GACrD,KAAK,kBAAkB,SAAS,IAAI,GAClC,KACA,KAAK;AACf,MAAI,CAAC,cACH;AAGF,OAAK,oBAAoB,KAAK,kBAAkB,MAAM,cAAc,OAAO;AAC3E,OAAK,MAAM,WAAW,sBAAsB,cAAc,EAAE;GAC1D,MAAM,kBAAkB,2BAA2B,QAAQ;AAC3D,OAAI,mBAAmB,OAAO,WAAW;IACvC,MAAM,aAAa,sBAAsB,EAAE,KAAK;AAChD,UAAM;KACJ,MAAM,aAAa;KACnB,SAAS;MACP,WAAW,OAAO;MAClB,GAAI,OAAO,YAAY,EAAE,WAAW,OAAO,WAAW,GAAG,EAAE;MAC3D;MACA,UAAU,gBAAgB;MAC3B;KACF;AACD,UAAM;KACJ,MAAM,aAAa;KACnB,SAAS;MACP,WAAW,OAAO;MAClB;MACA,MAAM,gBAAgB;MACvB;KACF;AACD,UAAM;KACJ,MAAM,aAAa;KACnB,SAAS;MACP,WAAW,OAAO;MAClB;MACD;KACF;AACD;;AAGF,OAAI,CAAC,WAAW,CAAC,OAAO,aAAa,CAAC,OAAO,UAC3C;AAEF,SAAM;IACJ,MAAM,aAAa;IACnB,SAAS;KACP,WAAW,OAAO;KAClB,WAAW,OAAO;KAClB,OAAO;KACR;IACF;;;CAIL,0BAAkC,UAChC,MAAM,SAAS,aAAa,kBAC5B,MAAM,SAAS,aAAa,oBAC5B,MAAM,SAAS,aAAa,iBAC5B,MAAM,SAAS,aAAa,eAC5B,MAAM,SAAS,aAAa;CAE9B,6BAAqC,UACnC,MAAM,SAAS,aAAa,wBAC5B,MAAM,SAAS,aAAa,uBAC5B,MAAM,SAAS,aAAa,4BAC5B,MAAM,SAAS,aAAa,sBAC5B,MAAM,SAAS,aAAa;CAE9B,sBAA8B,UAAgD;AAC5E,MAAI,EAAE,aAAa,UAAU,CAAC,MAAM,WAAW,OAAO,MAAM,YAAY,SACtE;AAEF,SAAO,eAAe,MAAM,WAAW,OAAO,MAAM,QAAQ,cAAc,WACtE,MAAM,QAAQ,YACd,KAAA;;CAGN,sBAA8B,UAAgD;AAC5E,MAAI,EAAE,aAAa,UAAU,CAAC,MAAM,WAAW,OAAO,MAAM,YAAY,SACtE;AAEF,SAAO,eAAe,MAAM,WAAW,OAAO,MAAM,QAAQ,cAAc,WACtE,MAAM,QAAQ,YACd,KAAA;;;AAIR,IAAa,iCAAb,MAA4C;CAC1C,aAA8B,IAAI,iCAAiC,aAAa;CAEhF,YAAY,iBAEV,QACkC;AAClC,aAAW,MAAM,SAAS,QAAQ;AAChC,OAAI,MAAM,SAAS,aAAa,kBAAkB;AAChD,WAAO,KAAK,wBAAwB,MAAM;AAC1C;;AAGF,OAAI,KAAK,uBAAuB,MAAM,CACpC,QAAO,KAAK,iBAAiB,MAAM;AAGrC,SAAM;;AAGR,SAAO,KAAK,eAAe;;CAG7B,0BAAkC,WAEhC,OAC6B;EAC7B,MAAM,WAAW,KAAK,WAAW,KAAK,MAAM,QAAQ,MAAM;AAC1D,OAAK,MAAM,WAAW,UAAU;AAC9B,OAAI,CAAC,QAAQ,KACX;AAEF,OAAI,QAAQ,SAAS,aAAa;AAChC,UAAM;KACJ,MAAM,aAAa;KACnB,SAAS;MACP,WAAW,MAAM,QAAQ;MACzB,WAAW,MAAM,QAAQ;MACzB,OAAO,QAAQ;MAChB;KACF;AACD;;AAEF,SAAM;IACJ,MAAM,aAAa;IACnB,SAAS;KACP,WAAW,MAAM,QAAQ;KACzB,WAAW,MAAM,QAAQ;KACzB,OAAO,QAAQ;KAChB;IACF;;;CAIL,mBAA2B,WAEzB,OAC6B;EAC7B,MAAM,YAAY,KAAK,mBAAmB,MAAM;EAChD,MAAM,YAAY,KAAK,mBAAmB,MAAM;AAChD,MAAI,CAAC,aAAa,CAAC,WAAW;AAC5B,UAAO,KAAK,eAAe;AAC3B;;EAGF,MAAM,WAAW,KAAK,WAAW,QAAQ;AACzC,OAAK,MAAM,WAAW,UAAU;AAC9B,OAAI,CAAC,QAAQ,KACX;AAEF,OAAI,QAAQ,SAAS,aAAa;AAChC,UAAM;KACJ,MAAM,aAAa;KACnB,SAAS;MACP;MACA;MACA,OAAO,QAAQ;MAChB;KACF;AACD;;AAEF,SAAM;IACJ,MAAM,aAAa;IACnB,SAAS;KACP;KACA;KACA,OAAO,QAAQ;KAChB;IACF;;;CAIL,gBAAwB,aAEO;AAC7B,OAAK,WAAW,QAAQ;;CAG1B,0BAAkC,UAChC,MAAM,SAAS,aAAa,kBAC5B,MAAM,SAAS,aAAa,oBAC5B,MAAM,SAAS,aAAa,iBAC5B,MAAM,SAAS,aAAa,eAC5B,MAAM,SAAS,aAAa;CAE9B,sBAA8B,UAAgD;AAC5E,MAAI,EAAE,aAAa,UAAU,CAAC,MAAM,WAAW,OAAO,MAAM,YAAY,SACtE;AAEF,SAAO,eAAe,MAAM,WAAW,OAAO,MAAM,QAAQ,cAAc,WACtE,MAAM,QAAQ,YACd,KAAA;;CAGN,sBAA8B,UAAgD;AAC5E,MAAI,EAAE,aAAa,UAAU,CAAC,MAAM,WAAW,OAAO,MAAM,YAAY,SACtE;AAEF,SAAO,eAAe,MAAM,WAAW,OAAO,MAAM,QAAQ,cAAc,WACtE,MAAM,QAAQ,YACd,KAAA;;;AAIR,SAAgB,mBAAmB,QAGxB;CACT,MAAM,EAAE,UAAU,kBAAkB;CACpC,MAAM,gBAAgB,wBAAwB,SAAS;CACvD,MAAM,WACJ,SAAS,YACT,OAAO,SAAS,aAAa,YAC7B,CAAC,MAAM,QAAQ,SAAS,SAAS,GAC5B,SAAS,WACV,KAAA;AAEN,QACE,eAAe,SACf,mBAAmB,UAAU,kBAAkB,IAC/C,mBAAmB,UAAU,iBAAiB,IAC9C,mBAAmB,UAAU,QAAQ,IACrC;;AAIJ,SAAgB,8BAA8B,OAAuB;CACnE,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,CAAC,QACH,QAAO;CAGT,MAAM,aAAa,QAAQ,QAAQ,IAAI;AACvC,KAAI,cAAc,EAChB,QAAO;CAGT,MAAM,SAAS,QAAQ,MAAM,aAAa,EAAE,CAAC,MAAM;AACnD,KAAI,CAAC,OACH,QAAO;AAGT,KAAI,OAAO,SAAS,IAAI,CACtB,QAAO;AAGT,QAAO;;AAGT,SAAgB,oBAAoB,QAIb;AACrB,KAAI,OAAO,YAAY,qBACrB,QAAO;CAET,MAAM,QAAQ,OAAO,MAAM,MAAM,CAAC,aAAa;AAG/C,KAFiB,aAAa,OAAO,QAAQ,CAEhC,SAAS,gBAAgB,IAAI,MAAM,WAAW,SAAS,CAClE,QAAO;AAET,QAAO;;AAGT,SAAgB,WACd,OACA,MACU;AACV,QAAO;EACL;EACA,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;EAChE;;AAGH,SAAgB,cAAc,OAAiC;AAC7D,QAAO,2BAA2B,KAAK,UAAU,MAAM,CAAC;;AAG1D,SAAgB,gBAAgB,OAAkD;AAChF,QAAO,uBAAuB,KAAK,UAAU,MAAM,CAAC;;AAGtD,SAAS,aAAa,OAA0C;AAC9D,KAAI,CAAC,MACH,QAAO;AAET,KAAI;AACF,SAAO,IAAI,IAAI,MAAM,CAAC,SAAS,MAAM,CAAC,aAAa;SAC7C;AACN,SAAO;;;AAIX,SAAS,sBACP,SACkD;CAClD,MAAM,cAAc,EAAE,GAAG,SAAS;CAClC,MAAM,UAAU,YAAY;AAC5B,QAAO,YAAY;AACnB,QAAO;EACL,SAAS;EACT,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;EAC/B;;AAGH,SAAS,mBAAmB,SAA6B;CACvD,MAAM,OAAO,QAAQ,MAClB,KAAK,SAAS;AACb,MAAI,KAAK,SAAS,UAAU,KAAK,SAAS,eAAe,KAAK,SAAS,YACrE,QAAO,KAAK;AAEd,SAAO;GACP,CACD,KAAK,KAAK,CACV,MAAM;AACT,QAAO,KAAK,SAAS,IAAI,OAAO;;AAGlC,SAAS,sBAAsB,MAAwB;AACrD,QAAO,KAAK,MAAM,qBAAqB,IAAI,EAAE;;AAG/C,SAAS,2BAA2B,SAA+C;CACjF,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;;AAGT,SAAS,cAAc,MAAuD;AAC5E,KAAI,SAAS,eAAe,SAAS,SACnC,QAAO;AAET,QAAO"}
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 +1 @@
1
- {"version":3,"file":"hermes-http-adapter.service.d.ts","names":[],"sources":["../src/hermes-http-adapter.service.ts"],"mappings":";;;;cAmmBa,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":";;;;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"}
@@ -229,7 +229,6 @@ var HermesHttpAdapterRouteService = class {
229
229
  createRunEvents = async function* (params) {
230
230
  const { response, run, signal } = params;
231
231
  const sessionId = run.envelope.sessionId;
232
- this.readProviderRoute(run.envelope);
233
232
  const metadata = { hermesModel: normalizeHermesRequestedModel(resolveHermesModel({
234
233
  envelope: run.envelope,
235
234
  fallbackModel: this.config.model
@@ -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 providerRoute = this.readProviderRoute(run.envelope);\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 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/nextclaw-ncp-runtime-adapter-hermes-http",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
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": "0.5.27",
23
- "@nextclaw/ncp-http-agent-client": "0.3.39",
24
- "@nextclaw/ncp-agent-runtime": "0.3.38"
22
+ "@nextclaw/ncp": "0.5.28",
23
+ "@nextclaw/ncp-agent-runtime": "0.3.39",
24
+ "@nextclaw/ncp-http-agent-client": "0.3.40"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^20.17.6",