@hivemagnus/magnus-server 0.0.0-stage → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/.env.example +171 -0
  2. package/LICENSE +202 -0
  3. package/README.md +169 -2
  4. package/agent-version.json +1 -0
  5. package/dist/ai/HEARTBEAT.md +56 -0
  6. package/dist/ai/IDENTITY.md +54 -0
  7. package/dist/ai/MEMORY.md +40 -0
  8. package/dist/ai/TOOLS.md +538 -0
  9. package/dist/ai/USER.md +32 -0
  10. package/dist/cli/args.js +62 -0
  11. package/dist/cli/env-catalog.js +131 -0
  12. package/dist/cli/preflight.js +89 -0
  13. package/dist/cli.js +175 -0
  14. package/dist/config/cors.js +16 -0
  15. package/dist/config/env.js +392 -0
  16. package/dist/config/features.js +16 -0
  17. package/dist/config/index.js +3 -0
  18. package/dist/config/listener-security.js +38 -0
  19. package/dist/config/version.js +31 -0
  20. package/dist/db/client.js +44 -0
  21. package/dist/db/generated/prisma/browser.js +18 -0
  22. package/dist/db/generated/prisma/client.js +35 -0
  23. package/dist/db/generated/prisma/commonInputTypes.js +11 -0
  24. package/dist/db/generated/prisma/enums.js +42 -0
  25. package/dist/db/generated/prisma/internal/class.js +50 -0
  26. package/dist/db/generated/prisma/internal/prismaNamespace.js +1598 -0
  27. package/dist/db/generated/prisma/internal/prismaNamespaceBrowser.js +1569 -0
  28. package/dist/db/generated/prisma/models/A2ATask.js +2 -0
  29. package/dist/db/generated/prisma/models/AdmissionFence.js +2 -0
  30. package/dist/db/generated/prisma/models/Agent.js +2 -0
  31. package/dist/db/generated/prisma/models/AiConversation.js +2 -0
  32. package/dist/db/generated/prisma/models/AiDashboard.js +2 -0
  33. package/dist/db/generated/prisma/models/AiMemory.js +2 -0
  34. package/dist/db/generated/prisma/models/AiMessage.js +2 -0
  35. package/dist/db/generated/prisma/models/AiScheduledOp.js +2 -0
  36. package/dist/db/generated/prisma/models/AlertConditionState.js +2 -0
  37. package/dist/db/generated/prisma/models/AlertRule.js +2 -0
  38. package/dist/db/generated/prisma/models/AlertViolation.js +2 -0
  39. package/dist/db/generated/prisma/models/AnomalyEvent.js +2 -0
  40. package/dist/db/generated/prisma/models/ApplicationObservation.js +2 -0
  41. package/dist/db/generated/prisma/models/ArdentInstallation.js +2 -0
  42. package/dist/db/generated/prisma/models/ArdentProfile.js +2 -0
  43. package/dist/db/generated/prisma/models/ArdentScopeEnrollment.js +2 -0
  44. package/dist/db/generated/prisma/models/CommandResourceFence.js +2 -0
  45. package/dist/db/generated/prisma/models/CommandResourceLease.js +2 -0
  46. package/dist/db/generated/prisma/models/CommandResult.js +2 -0
  47. package/dist/db/generated/prisma/models/CronJob.js +2 -0
  48. package/dist/db/generated/prisma/models/CronJobExecution.js +2 -0
  49. package/dist/db/generated/prisma/models/DeployJob.js +2 -0
  50. package/dist/db/generated/prisma/models/DriftScan.js +2 -0
  51. package/dist/db/generated/prisma/models/EngineeringMetricSample.js +2 -0
  52. package/dist/db/generated/prisma/models/EngineeringRecoveryExercise.js +2 -0
  53. package/dist/db/generated/prisma/models/ExecutionRun.js +2 -0
  54. package/dist/db/generated/prisma/models/FilePatchTask.js +2 -0
  55. package/dist/db/generated/prisma/models/GpuSnapshot.js +2 -0
  56. package/dist/db/generated/prisma/models/HealthSnapshot.js +2 -0
  57. package/dist/db/generated/prisma/models/HermesCoreRollout.js +2 -0
  58. package/dist/db/generated/prisma/models/HermesCoreRolloutHost.js +2 -0
  59. package/dist/db/generated/prisma/models/HermesCredentialPool.js +2 -0
  60. package/dist/db/generated/prisma/models/HermesCredentialPoolAttachment.js +2 -0
  61. package/dist/db/generated/prisma/models/HermesCronJob.js +2 -0
  62. package/dist/db/generated/prisma/models/HermesHook.js +2 -0
  63. package/dist/db/generated/prisma/models/HermesInstallation.js +2 -0
  64. package/dist/db/generated/prisma/models/HermesMcpServer.js +2 -0
  65. package/dist/db/generated/prisma/models/HermesMcpServerAttachment.js +2 -0
  66. package/dist/db/generated/prisma/models/HermesMessagingBinding.js +2 -0
  67. package/dist/db/generated/prisma/models/HermesOperationJob.js +2 -0
  68. package/dist/db/generated/prisma/models/HermesPluginInstall.js +2 -0
  69. package/dist/db/generated/prisma/models/HermesProfile.js +2 -0
  70. package/dist/db/generated/prisma/models/HermesProviderConfig.js +2 -0
  71. package/dist/db/generated/prisma/models/HermesRegistryClient.js +2 -0
  72. package/dist/db/generated/prisma/models/HermesRegistryInventory.js +2 -0
  73. package/dist/db/generated/prisma/models/HermesRegistryReceipt.js +2 -0
  74. package/dist/db/generated/prisma/models/HermesRegistryRevision.js +2 -0
  75. package/dist/db/generated/prisma/models/HermesRegistryTarget.js +2 -0
  76. package/dist/db/generated/prisma/models/HermesRole.js +2 -0
  77. package/dist/db/generated/prisma/models/HermesRoleApplication.js +2 -0
  78. package/dist/db/generated/prisma/models/HermesSkillAssignment.js +2 -0
  79. package/dist/db/generated/prisma/models/HermesToolsetPolicy.js +2 -0
  80. package/dist/db/generated/prisma/models/IncidentLesson.js +2 -0
  81. package/dist/db/generated/prisma/models/LibraryCategory.js +2 -0
  82. package/dist/db/generated/prisma/models/LibraryFile.js +2 -0
  83. package/dist/db/generated/prisma/models/LogEntry.js +2 -0
  84. package/dist/db/generated/prisma/models/MagnusReleaseCanary.js +2 -0
  85. package/dist/db/generated/prisma/models/MaintenanceWindow.js +2 -0
  86. package/dist/db/generated/prisma/models/ManagedCronDefinition.js +2 -0
  87. package/dist/db/generated/prisma/models/ManagedCronOccurrence.js +2 -0
  88. package/dist/db/generated/prisma/models/Node.js +2 -0
  89. package/dist/db/generated/prisma/models/Notification.js +2 -0
  90. package/dist/db/generated/prisma/models/NotificationPreference.js +2 -0
  91. package/dist/db/generated/prisma/models/RemediationExecution.js +2 -0
  92. package/dist/db/generated/prisma/models/RemediationRule.js +2 -0
  93. package/dist/db/generated/prisma/models/RemoteCommand.js +2 -0
  94. package/dist/db/generated/prisma/models/ResourceDrainParticipant.js +2 -0
  95. package/dist/db/generated/prisma/models/ResourceEnrollment.js +2 -0
  96. package/dist/db/generated/prisma/models/ResourceParticipantWork.js +2 -0
  97. package/dist/db/generated/prisma/models/Role.js +2 -0
  98. package/dist/db/generated/prisma/models/RoleApplication.js +2 -0
  99. package/dist/db/generated/prisma/models/RolloutTarget.js +2 -0
  100. package/dist/db/generated/prisma/models/SecurityExportOutbox.js +2 -0
  101. package/dist/db/generated/prisma/models/SecurityFusionAction.js +2 -0
  102. package/dist/db/generated/prisma/models/SecurityFusionActionApproval.js +2 -0
  103. package/dist/db/generated/prisma/models/SecurityFusionCase.js +2 -0
  104. package/dist/db/generated/prisma/models/SecurityFusionCaseActivity.js +2 -0
  105. package/dist/db/generated/prisma/models/SecurityFusionCaseEntity.js +2 -0
  106. package/dist/db/generated/prisma/models/SecurityFusionCaseEvent.js +2 -0
  107. package/dist/db/generated/prisma/models/SecurityFusionCaseRelationship.js +2 -0
  108. package/dist/db/generated/prisma/models/SecurityFusionCollector.js +2 -0
  109. package/dist/db/generated/prisma/models/SecurityFusionCommandOutbox.js +2 -0
  110. package/dist/db/generated/prisma/models/SecurityFusionDeletionReceipt.js +2 -0
  111. package/dist/db/generated/prisma/models/SecurityFusionDelivery.js +2 -0
  112. package/dist/db/generated/prisma/models/SecurityFusionEntity.js +2 -0
  113. package/dist/db/generated/prisma/models/SecurityFusionEntityAlias.js +2 -0
  114. package/dist/db/generated/prisma/models/SecurityFusionEvent.js +2 -0
  115. package/dist/db/generated/prisma/models/SecurityFusionEventEntity.js +2 -0
  116. package/dist/db/generated/prisma/models/SecurityFusionEvidence.js +2 -0
  117. package/dist/db/generated/prisma/models/SecurityFusionInvestigation.js +2 -0
  118. package/dist/db/generated/prisma/models/SecurityFusionNonce.js +2 -0
  119. package/dist/db/generated/prisma/models/SecurityFusionReceiptChainHead.js +2 -0
  120. package/dist/db/generated/prisma/models/SecurityFusionRuleVersion.js +2 -0
  121. package/dist/db/generated/prisma/models/SecurityFusionSourceState.js +2 -0
  122. package/dist/db/generated/prisma/models/SecurityServiceState.js +2 -0
  123. package/dist/db/generated/prisma/models/SharedResourceFence.js +2 -0
  124. package/dist/db/generated/prisma/models/SharedResourceLease.js +2 -0
  125. package/dist/db/generated/prisma/models/SharedResourceLeaseResource.js +2 -0
  126. package/dist/db/generated/prisma/models/SkillInstallation.js +2 -0
  127. package/dist/db/generated/prisma/models/SkillRegistryEntry.js +2 -0
  128. package/dist/db/generated/prisma/models/SkillTag.js +2 -0
  129. package/dist/db/generated/prisma/models/SkillVersion.js +2 -0
  130. package/dist/db/generated/prisma/models/TeamApprovalDecision.js +2 -0
  131. package/dist/db/generated/prisma/models/User.js +2 -0
  132. package/dist/db/generated/prisma/models/WebhookDelivery.js +2 -0
  133. package/dist/db/generated/prisma/models/WebhookEndpoint.js +2 -0
  134. package/dist/db/generated/prisma/models.js +2 -0
  135. package/dist/db/index.js +3 -0
  136. package/dist/db/read-retry.js +35 -0
  137. package/dist/db/seed.js +348 -0
  138. package/dist/index.js +162 -0
  139. package/dist/middleware/auth.js +361 -0
  140. package/dist/middleware/body-limit.js +14 -0
  141. package/dist/middleware/correlation-id.js +13 -0
  142. package/dist/middleware/error-handler.js +28 -0
  143. package/dist/middleware/iam-token.js +21 -0
  144. package/dist/middleware/rate-limit.js +78 -0
  145. package/dist/middleware/request-logger.js +85 -0
  146. package/dist/middleware/security-headers.js +23 -0
  147. package/dist/middleware/tracing.js +120 -0
  148. package/dist/middleware/user-status.js +33 -0
  149. package/dist/openapi/spec.js +2656 -0
  150. package/dist/queue/redis.js +20 -0
  151. package/dist/queue/requeue.js +27 -0
  152. package/dist/queue/workers.js +342 -0
  153. package/dist/routes/ai-schedule.js +73 -0
  154. package/dist/routes/alerts.js +79 -0
  155. package/dist/routes/anomalies.js +101 -0
  156. package/dist/routes/ardent.js +82 -0
  157. package/dist/routes/assistant.js +417 -0
  158. package/dist/routes/audit.js +138 -0
  159. package/dist/routes/codex.js +65 -0
  160. package/dist/routes/commands.js +226 -0
  161. package/dist/routes/credentials.js +58 -0
  162. package/dist/routes/cron.js +292 -0
  163. package/dist/routes/dependencies.js +49 -0
  164. package/dist/routes/deploy.js +18 -0
  165. package/dist/routes/discovery.js +168 -0
  166. package/dist/routes/docs.js +24 -0
  167. package/dist/routes/drift.js +126 -0
  168. package/dist/routes/events-stream.js +87 -0
  169. package/dist/routes/files.js +214 -0
  170. package/dist/routes/fleet-checks.js +21 -0
  171. package/dist/routes/health.js +133 -0
  172. package/dist/routes/heartbeat.js +545 -0
  173. package/dist/routes/hermes-registry.js +278 -0
  174. package/dist/routes/hermes.js +61 -0
  175. package/dist/routes/library.js +402 -0
  176. package/dist/routes/logs.js +172 -0
  177. package/dist/routes/managed-cron.js +23 -0
  178. package/dist/routes/mcp.js +39 -0
  179. package/dist/routes/metrics.js +187 -0
  180. package/dist/routes/nodes.js +951 -0
  181. package/dist/routes/notifications.js +130 -0
  182. package/dist/routes/ops.js +258 -0
  183. package/dist/routes/orchestra.js +430 -0
  184. package/dist/routes/predictions.js +23 -0
  185. package/dist/routes/profiling.js +68 -0
  186. package/dist/routes/quick-command.js +72 -0
  187. package/dist/routes/remediation.js +111 -0
  188. package/dist/routes/resource-leases.js +297 -0
  189. package/dist/routes/roles.js +370 -0
  190. package/dist/routes/rollout.js +124 -0
  191. package/dist/routes/scan.js +116 -0
  192. package/dist/routes/security-backup.js +58 -0
  193. package/dist/routes/security-fusion.js +605 -0
  194. package/dist/routes/security-integration.js +71 -0
  195. package/dist/routes/self-update.js +129 -0
  196. package/dist/routes/shared-resource-leases.js +78 -0
  197. package/dist/routes/skills.js +485 -0
  198. package/dist/routes/stats.js +96 -0
  199. package/dist/routes/team-approvals.js +23 -0
  200. package/dist/routes/terminal-ticket.js +15 -0
  201. package/dist/routes/terminal.js +17 -0
  202. package/dist/routes/topology.js +37 -0
  203. package/dist/routes/users.js +219 -0
  204. package/dist/routes/webhooks.js +137 -0
  205. package/dist/schemas/command-reconciliation.js +14 -0
  206. package/dist/schemas/commands.js +51 -0
  207. package/dist/schemas/cron.js +33 -0
  208. package/dist/schemas/deploy.js +20 -0
  209. package/dist/schemas/files.js +14 -0
  210. package/dist/schemas/hermes.js +69 -0
  211. package/dist/schemas/index.js +11 -0
  212. package/dist/schemas/library.js +48 -0
  213. package/dist/schemas/logs.js +35 -0
  214. package/dist/schemas/managed-cron.js +127 -0
  215. package/dist/schemas/node-responses.js +87 -0
  216. package/dist/schemas/node-session-receipt.js +84 -0
  217. package/dist/schemas/nodes.js +36 -0
  218. package/dist/schemas/orchestra.js +118 -0
  219. package/dist/schemas/resource-participant-reconciliation.js +99 -0
  220. package/dist/schemas/roles.js +54 -0
  221. package/dist/schemas/shared-resource-lease.js +132 -0
  222. package/dist/schemas/skills.js +44 -0
  223. package/dist/schemas/users.js +28 -0
  224. package/dist/scripts/run-disposable-recovery-exercise.js +39 -0
  225. package/dist/sdk/index.js +2 -0
  226. package/dist/sdk/node-api-client.js +474 -0
  227. package/dist/security/approval-assertion.js +66 -0
  228. package/dist/security/command-completion-registry.js +35 -0
  229. package/dist/security/command-request.js +12 -0
  230. package/dist/security/command-result-signature.js +34 -0
  231. package/dist/security/managed-http.js +45 -0
  232. package/dist/security/native-completion-registry.js +44 -0
  233. package/dist/server.js +178 -0
  234. package/dist/services/admission-fence.js +71 -0
  235. package/dist/services/ai-assistant-service.js +2523 -0
  236. package/dist/services/ai-correlation-service.js +225 -0
  237. package/dist/services/ai-export-service.js +141 -0
  238. package/dist/services/ai-insights-service.js +219 -0
  239. package/dist/services/ai-schedule-service.js +145 -0
  240. package/dist/services/ai-scratchpad-service.js +222 -0
  241. package/dist/services/ai-tools.js +1343 -0
  242. package/dist/services/alert-service.js +313 -0
  243. package/dist/services/anomaly-detector.js +419 -0
  244. package/dist/services/api-local-work.js +79 -0
  245. package/dist/services/application-probes.js +309 -0
  246. package/dist/services/ardent-scope-enrollment.js +123 -0
  247. package/dist/services/ardent-service.js +86 -0
  248. package/dist/services/audit-service.js +308 -0
  249. package/dist/services/codex-service.js +209 -0
  250. package/dist/services/command-lifecycle.js +60 -0
  251. package/dist/services/command-resource-leases.js +305 -0
  252. package/dist/services/control-host-node.js +242 -0
  253. package/dist/services/credential-rotation-service.js +294 -0
  254. package/dist/services/dependency-service.js +145 -0
  255. package/dist/services/disposable-recovery-runner.js +595 -0
  256. package/dist/services/docker-group-health.js +121 -0
  257. package/dist/services/engineering-assurance.js +492 -0
  258. package/dist/services/engineering-evidence-ledger.js +94 -0
  259. package/dist/services/engineering-metric-evidence.js +26 -0
  260. package/dist/services/event-bus.js +227 -0
  261. package/dist/services/fleet-check-log.js +102 -0
  262. package/dist/services/forecast-service.js +154 -0
  263. package/dist/services/health-retention.js +175 -0
  264. package/dist/services/heartbeat-stream.js +63 -0
  265. package/dist/services/hermes-catalog-import.js +62 -0
  266. package/dist/services/hermes-cli-parser.js +671 -0
  267. package/dist/services/hermes-core-rollout.js +90 -0
  268. package/dist/services/hermes-dashboard-endpoint.js +13 -0
  269. package/dist/services/hermes-plugin-source.js +149 -0
  270. package/dist/services/hermes-registry-service.js +258 -0
  271. package/dist/services/hermes-service.js +566 -0
  272. package/dist/services/hivemem-client.js +85 -0
  273. package/dist/services/host-local-writer.js +82 -0
  274. package/dist/services/incident-evidence.js +110 -0
  275. package/dist/services/incident-lessons.js +186 -0
  276. package/dist/services/llm-providers/anthropic.js +143 -0
  277. package/dist/services/llm-providers/index.js +7 -0
  278. package/dist/services/llm-providers/llama-cpp.js +237 -0
  279. package/dist/services/llm-providers/ollama.js +292 -0
  280. package/dist/services/llm-providers/openai-compatible.js +200 -0
  281. package/dist/services/llm-providers/provider-error.js +36 -0
  282. package/dist/services/llm-providers/registry.js +45 -0
  283. package/dist/services/llm-providers/types.js +2 -0
  284. package/dist/services/log-emitter.js +19 -0
  285. package/dist/services/log-retention.js +66 -0
  286. package/dist/services/maintenance-service.js +29 -0
  287. package/dist/services/managed-cron.js +493 -0
  288. package/dist/services/managed-ssh-admission.js +68 -0
  289. package/dist/services/monitor-pulse.js +10 -0
  290. package/dist/services/node-issue-monitor.js +71 -0
  291. package/dist/services/node-service.js +35 -0
  292. package/dist/services/notification-retention.js +51 -0
  293. package/dist/services/notification-service.js +94 -0
  294. package/dist/services/operational-observations.js +112 -0
  295. package/dist/services/operations-config.js +67 -0
  296. package/dist/services/ops-service.js +491 -0
  297. package/dist/services/orchestra-extras-service.js +370 -0
  298. package/dist/services/orchestra-resources-service.js +530 -0
  299. package/dist/services/os-scheduler-writer.js +73 -0
  300. package/dist/services/password-service.js +22 -0
  301. package/dist/services/prediction-service.js +284 -0
  302. package/dist/services/recovery-exercises.js +682 -0
  303. package/dist/services/remediation-service.js +325 -0
  304. package/dist/services/resource-enrollment.js +433 -0
  305. package/dist/services/resource-node-lock.js +18 -0
  306. package/dist/services/resource-participant-work.js +302 -0
  307. package/dist/services/role-service.js +388 -0
  308. package/dist/services/rollout-service.js +440 -0
  309. package/dist/services/security-export.js +185 -0
  310. package/dist/services/security-fusion-actions.js +22 -0
  311. package/dist/services/security-fusion-approvals.js +83 -0
  312. package/dist/services/security-fusion-evidence-integrity.js +144 -0
  313. package/dist/services/security-fusion-ingest.js +101 -0
  314. package/dist/services/security-fusion-outbox.js +185 -0
  315. package/dist/services/security-fusion-receipt-chain.js +134 -0
  316. package/dist/services/security-fusion-service.js +751 -0
  317. package/dist/services/security-fusion-verification.js +50 -0
  318. package/dist/services/self-update-service.js +200 -0
  319. package/dist/services/shared-resource-leases.js +413 -0
  320. package/dist/services/skill-publication-bundle.js +90 -0
  321. package/dist/services/skill-publication-settlement.js +36 -0
  322. package/dist/services/skill-publication-state.js +40 -0
  323. package/dist/services/skill-registry-service.js +327 -0
  324. package/dist/services/skill-service.js +148 -0
  325. package/dist/services/ssh-exec-service.js +139 -0
  326. package/dist/services/ssh-host-verification.js +27 -0
  327. package/dist/services/tailscale-node-key-service.js +75 -0
  328. package/dist/services/team-approvals.js +118 -0
  329. package/dist/services/topology-service.js +138 -0
  330. package/dist/services/transfer-authority-snapshot.js +151 -0
  331. package/dist/services/webhook-credential-migration.js +97 -0
  332. package/dist/services/webhook-security.js +242 -0
  333. package/dist/services/webhook-service.js +494 -0
  334. package/dist/types/auth.js +3 -0
  335. package/dist/types/index.js +3 -0
  336. package/dist/types/library.js +3 -0
  337. package/dist/types/notifications.js +3 -0
  338. package/dist/types/roles.js +3 -0
  339. package/dist/types/shared-contracts.js +3 -0
  340. package/dist/utils/env.js +141 -0
  341. package/dist/utils/heartbeat-peer.js +19 -0
  342. package/dist/utils/idempotency.js +45 -0
  343. package/dist/utils/ip.js +25 -0
  344. package/dist/utils/json-fields.js +94 -0
  345. package/dist/utils/local-tls.js +39 -0
  346. package/dist/utils/magnus-home.js +15 -0
  347. package/dist/utils/node-url.js +59 -0
  348. package/dist/utils/redact.js +26 -0
  349. package/dist/utils/safe-equal.js +13 -0
  350. package/dist/utils/sentinel-nodes.js +16 -0
  351. package/dist/utils/validate.js +41 -0
  352. package/package.json +119 -4
  353. package/prisma/migrations/20261031000000_baseline/migration.sql +2935 -0
  354. package/prisma/migrations/migration_lock.toml +3 -0
  355. package/prisma/schema.prisma +2185 -0
  356. package/prisma.config.ts +14 -0
@@ -0,0 +1,237 @@
1
+ import { createHttpProviderError, LlmProviderError, normalizeProviderError } from './provider-error.js';
2
+ function joinContent(content) {
3
+ if (typeof content === 'string')
4
+ return content;
5
+ if (Array.isArray(content)) {
6
+ return content
7
+ .map((entry) => {
8
+ if (typeof entry === 'string')
9
+ return entry;
10
+ if (entry && typeof entry === 'object' && 'text' in entry) {
11
+ return String(entry.text ?? '');
12
+ }
13
+ return '';
14
+ })
15
+ .join('');
16
+ }
17
+ return '';
18
+ }
19
+ export class LlamaCppProvider {
20
+ baseUrl;
21
+ id = 'llamacpp';
22
+ name = 'llama.cpp';
23
+ supportsStreaming = true;
24
+ supportsToolCalling = true;
25
+ supportsStructuredOutputs = true;
26
+ constructor(baseUrl = process.env['LLAMACPP_URL'] ?? process.env['VLLM_URL'] ?? 'http://127.0.0.1:8080') {
27
+ this.baseUrl = baseUrl;
28
+ }
29
+ isConfigured() {
30
+ return Boolean(this.baseUrl);
31
+ }
32
+ get apiBase() {
33
+ return this.baseUrl.replace(/\/+$/, '');
34
+ }
35
+ async chat(request) {
36
+ if (!this.isConfigured()) {
37
+ throw new LlmProviderError(this.id, 'configuration', 'llama.cpp is not configured', false);
38
+ }
39
+ const payload = {
40
+ model: request.model,
41
+ messages: request.messages.map((m) => {
42
+ if (m.role === 'assistant' && m.tool_calls?.length) {
43
+ return {
44
+ role: 'assistant',
45
+ content: m.content || null,
46
+ tool_calls: m.tool_calls.map((tc) => ({
47
+ id: tc.id,
48
+ type: 'function',
49
+ function: {
50
+ name: tc.name,
51
+ arguments: JSON.stringify(tc.arguments ?? {}),
52
+ },
53
+ })),
54
+ };
55
+ }
56
+ if (m.role === 'tool') {
57
+ return { role: 'tool', content: m.content, tool_call_id: m.tool_call_id };
58
+ }
59
+ return { role: m.role, content: m.content };
60
+ }),
61
+ temperature: request.temperature ?? 0.3,
62
+ max_tokens: request.maxTokens ?? 4096,
63
+ stream: false,
64
+ };
65
+ if (request.tools?.length) {
66
+ payload.tools = request.tools.map((tool) => ({
67
+ type: tool.type,
68
+ function: {
69
+ name: tool.function.name,
70
+ description: tool.function.description,
71
+ parameters: tool.function.parameters,
72
+ },
73
+ }));
74
+ payload.tool_choice = 'auto';
75
+ }
76
+ if (request.jsonSchema) {
77
+ payload.response_format = {
78
+ type: 'json_schema',
79
+ json_schema: {
80
+ name: 'structured_output',
81
+ schema: request.jsonSchema,
82
+ strict: true,
83
+ },
84
+ };
85
+ }
86
+ try {
87
+ const response = await fetch(`${this.apiBase}/v1/chat/completions`, {
88
+ method: 'POST',
89
+ headers: { 'Content-Type': 'application/json' },
90
+ body: JSON.stringify(payload),
91
+ signal: AbortSignal.timeout(300_000),
92
+ });
93
+ if (!response.ok) {
94
+ const text = await response.text().catch(() => '');
95
+ throw createHttpProviderError(this.id, response.status, `llama.cpp API error: ${response.status} ${text.slice(0, 300)}`);
96
+ }
97
+ const data = (await response.json());
98
+ const message = data.choices?.[0]?.message;
99
+ const nativeToolCalls = (message?.tool_calls ?? []).flatMap((tc) => {
100
+ const name = tc.function?.name;
101
+ if (!name)
102
+ return [];
103
+ let args = {};
104
+ try {
105
+ args = tc.function?.arguments ? JSON.parse(tc.function.arguments) : {};
106
+ }
107
+ catch {
108
+ args = {};
109
+ }
110
+ return [{ id: tc.id, name, arguments: args }];
111
+ });
112
+ return {
113
+ providerId: this.id,
114
+ providerName: this.name,
115
+ model: data.model ?? request.model,
116
+ content: joinContent(message?.content),
117
+ thinking: '',
118
+ evalCount: data.usage?.total_tokens ?? 0,
119
+ nativeToolCalls,
120
+ usedStructuredOutput: Boolean(request.jsonSchema),
121
+ };
122
+ }
123
+ catch (error) {
124
+ if (error instanceof LlmProviderError)
125
+ throw error;
126
+ if (error instanceof Error && error.name === 'TimeoutError') {
127
+ throw new LlmProviderError(this.id, 'connectivity', 'llama.cpp request timed out', true);
128
+ }
129
+ throw normalizeProviderError(this.id, error);
130
+ }
131
+ }
132
+ async *streamChat(request) {
133
+ if (!this.isConfigured()) {
134
+ throw new LlmProviderError(this.id, 'configuration', 'llama.cpp is not configured', false);
135
+ }
136
+ const payload = {
137
+ model: request.model,
138
+ messages: request.messages.map((m) => ({ role: m.role, content: m.content })),
139
+ temperature: request.temperature ?? 0.3,
140
+ max_tokens: request.maxTokens ?? 4096,
141
+ stream: true,
142
+ stream_options: { include_usage: true },
143
+ };
144
+ const response = await fetch(`${this.apiBase}/v1/chat/completions`, {
145
+ method: 'POST',
146
+ headers: { 'Content-Type': 'application/json' },
147
+ body: JSON.stringify(payload),
148
+ signal: AbortSignal.timeout(300_000),
149
+ });
150
+ if (!response.ok || !response.body) {
151
+ const text = await response.text().catch(() => '');
152
+ throw createHttpProviderError(this.id, response.status, `llama.cpp stream error: ${response.status} ${text.slice(0, 200)}`);
153
+ }
154
+ const reader = response.body.getReader();
155
+ const decoder = new TextDecoder();
156
+ let buffer = '';
157
+ while (true) {
158
+ const { done, value } = await reader.read();
159
+ if (done)
160
+ break;
161
+ buffer += decoder.decode(value, { stream: true });
162
+ const lines = buffer.split('\n');
163
+ buffer = lines.pop() ?? '';
164
+ for (const line of lines) {
165
+ const trimmed = line.trim();
166
+ if (!trimmed || trimmed === 'data: [DONE]')
167
+ continue;
168
+ if (!trimmed.startsWith('data: '))
169
+ continue;
170
+ try {
171
+ const chunk = JSON.parse(trimmed.slice(6));
172
+ const delta = chunk.choices?.[0]?.delta?.content;
173
+ if (delta) {
174
+ yield { type: 'content', text: delta };
175
+ }
176
+ if (chunk.usage?.total_tokens) {
177
+ yield { type: 'usage', evalCount: chunk.usage.total_tokens };
178
+ }
179
+ }
180
+ catch {
181
+ // skip malformed SSE lines
182
+ }
183
+ }
184
+ }
185
+ yield { type: 'done' };
186
+ }
187
+ async healthCheck() {
188
+ const checkedAt = new Date().toISOString();
189
+ if (!this.isConfigured()) {
190
+ return {
191
+ id: this.id,
192
+ name: this.name,
193
+ configured: false,
194
+ healthy: false,
195
+ baseUrl: this.baseUrl,
196
+ error: 'LLAMACPP_URL is not configured',
197
+ checkedAt,
198
+ supportsStreaming: this.supportsStreaming,
199
+ supportsToolCalling: this.supportsToolCalling,
200
+ supportsStructuredOutputs: this.supportsStructuredOutputs,
201
+ };
202
+ }
203
+ try {
204
+ const response = await fetch(`${this.apiBase}/v1/models`, { signal: AbortSignal.timeout(5_000) });
205
+ if (!response.ok) {
206
+ throw createHttpProviderError(this.id, response.status, `llama.cpp health check failed: ${response.status}`);
207
+ }
208
+ return {
209
+ id: this.id,
210
+ name: this.name,
211
+ configured: true,
212
+ healthy: true,
213
+ baseUrl: this.baseUrl,
214
+ checkedAt,
215
+ supportsStreaming: this.supportsStreaming,
216
+ supportsToolCalling: this.supportsToolCalling,
217
+ supportsStructuredOutputs: this.supportsStructuredOutputs,
218
+ };
219
+ }
220
+ catch (error) {
221
+ const normalized = normalizeProviderError(this.id, error);
222
+ return {
223
+ id: this.id,
224
+ name: this.name,
225
+ configured: true,
226
+ healthy: false,
227
+ baseUrl: this.baseUrl,
228
+ error: normalized.message,
229
+ checkedAt,
230
+ supportsStreaming: this.supportsStreaming,
231
+ supportsToolCalling: this.supportsToolCalling,
232
+ supportsStructuredOutputs: this.supportsStructuredOutputs,
233
+ };
234
+ }
235
+ }
236
+ }
237
+ //# sourceMappingURL=llama-cpp.js.map
@@ -0,0 +1,292 @@
1
+ import { createHttpProviderError, LlmProviderError, normalizeProviderError } from './provider-error.js';
2
+ const THINKING_CAPABILITY_TTL_MS = 5 * 60 * 1000;
3
+ const thinkingCapabilityCache = new Map();
4
+ function supportsThinkingHeuristic(model) {
5
+ return model.includes('qwen3') || model.includes('gpt-oss') || model.includes('deepseek-r1');
6
+ }
7
+ function containsThinkingSignal(value) {
8
+ if (typeof value === 'string') {
9
+ return /thinking|reasoning|<think>/i.test(value);
10
+ }
11
+ if (Array.isArray(value)) {
12
+ return value.some((entry) => containsThinkingSignal(entry));
13
+ }
14
+ if (value && typeof value === 'object') {
15
+ return Object.values(value).some((entry) => containsThinkingSignal(entry));
16
+ }
17
+ return false;
18
+ }
19
+ export async function getOllamaThinkingCapability(baseUrl, model) {
20
+ const cacheKey = `${baseUrl}::${model}`;
21
+ const cached = thinkingCapabilityCache.get(cacheKey);
22
+ if (cached && (Date.now() - cached.checkedAt) < THINKING_CAPABILITY_TTL_MS) {
23
+ return cached.supportsThinking;
24
+ }
25
+ let supportsThinking = supportsThinkingHeuristic(model);
26
+ try {
27
+ const response = await fetch(`${baseUrl}/api/show`, {
28
+ method: 'POST',
29
+ headers: { 'Content-Type': 'application/json' },
30
+ body: JSON.stringify({ name: model }),
31
+ signal: AbortSignal.timeout(10_000),
32
+ });
33
+ if (response.ok) {
34
+ const data = (await response.json());
35
+ supportsThinking = containsThinkingSignal(data.capabilities)
36
+ || containsThinkingSignal(data.details)
37
+ || containsThinkingSignal(data.model_info)
38
+ || containsThinkingSignal(data.template)
39
+ || containsThinkingSignal(data.modelfile)
40
+ || supportsThinkingHeuristic(model);
41
+ }
42
+ }
43
+ catch {
44
+ supportsThinking = supportsThinkingHeuristic(model);
45
+ }
46
+ thinkingCapabilityCache.set(cacheKey, { checkedAt: Date.now(), supportsThinking });
47
+ return supportsThinking;
48
+ }
49
+ function supportsStructuredOutputs(model) {
50
+ return !model.includes('legacy');
51
+ }
52
+ function getThinkingInstruction(mode) {
53
+ switch (mode) {
54
+ case 'deep':
55
+ return 'Reasoning: high';
56
+ case 'standard':
57
+ return 'Reasoning: medium';
58
+ default:
59
+ return null;
60
+ }
61
+ }
62
+ export class OllamaProvider {
63
+ baseUrl;
64
+ id = 'ollama';
65
+ name = 'Ollama';
66
+ supportsStreaming = true;
67
+ supportsToolCalling = true;
68
+ supportsStructuredOutputs = true;
69
+ constructor(baseUrl = process.env['OLLAMA_URL'] ?? 'http://127.0.0.1:11434') {
70
+ this.baseUrl = baseUrl;
71
+ }
72
+ isConfigured() {
73
+ return Boolean(this.baseUrl);
74
+ }
75
+ async chat(request) {
76
+ const usedModel = request.model;
77
+ const thinkingCapable = await getOllamaThinkingCapability(this.baseUrl, usedModel);
78
+ const thinkingMode = thinkingCapable ? (request.thinkingMode ?? 'standard') : 'off';
79
+ const reasoningInstruction = getThinkingInstruction(thinkingMode);
80
+ const messages = reasoningInstruction
81
+ ? [{ role: 'system', content: reasoningInstruction }, ...request.messages]
82
+ : request.messages;
83
+ // Map images for multi-modal models
84
+ const ollamaMessages = messages.map((m) => {
85
+ const out = { role: m.role, content: m.content };
86
+ if (m.images?.length) {
87
+ out.images = m.images.map((img) => img.data);
88
+ }
89
+ if (m.tool_calls)
90
+ out.tool_calls = m.tool_calls;
91
+ if (m.tool_call_id)
92
+ out.tool_call_id = m.tool_call_id;
93
+ return out;
94
+ });
95
+ const body = {
96
+ model: usedModel,
97
+ messages: ollamaMessages,
98
+ stream: false,
99
+ options: {
100
+ temperature: request.temperature ?? 0.3,
101
+ num_predict: request.maxTokens ?? 4096,
102
+ },
103
+ };
104
+ if (request.tools?.length) {
105
+ body.tools = request.tools;
106
+ }
107
+ if (request.jsonSchema && supportsStructuredOutputs(usedModel)) {
108
+ body.format = request.jsonSchema;
109
+ }
110
+ try {
111
+ const response = await fetch(`${this.baseUrl}/api/chat`, {
112
+ method: 'POST',
113
+ headers: { 'Content-Type': 'application/json' },
114
+ body: JSON.stringify(body),
115
+ signal: AbortSignal.timeout(300_000),
116
+ });
117
+ if (!response.ok) {
118
+ const text = await response.text().catch(() => '');
119
+ throw createHttpProviderError(this.id, response.status, `Ollama API error: ${response.status} ${text.slice(0, 200)}`);
120
+ }
121
+ const data = (await response.json());
122
+ const nativeToolCalls = (data.message?.tool_calls ?? []).map((toolCall) => ({
123
+ name: toolCall.function.name,
124
+ arguments: toolCall.function.arguments ?? {},
125
+ }));
126
+ return {
127
+ providerId: this.id,
128
+ providerName: this.name,
129
+ model: data.model ?? usedModel,
130
+ content: data.message?.content ?? '',
131
+ thinking: data.message?.thinking ?? '',
132
+ evalCount: data.eval_count ?? 0,
133
+ nativeToolCalls,
134
+ usedStructuredOutput: Boolean(request.jsonSchema) && supportsStructuredOutputs(usedModel),
135
+ };
136
+ }
137
+ catch (error) {
138
+ if (error instanceof LlmProviderError)
139
+ throw error;
140
+ if (error instanceof Error && error.name === 'TimeoutError') {
141
+ throw new LlmProviderError(this.id, 'connectivity', 'Ollama request timed out', true);
142
+ }
143
+ throw normalizeProviderError(this.id, error);
144
+ }
145
+ }
146
+ async *streamChat(request) {
147
+ const usedModel = request.model;
148
+ const thinkingCapable = await getOllamaThinkingCapability(this.baseUrl, usedModel);
149
+ const thinkingMode = thinkingCapable ? (request.thinkingMode ?? 'standard') : 'off';
150
+ const reasoningInstruction = getThinkingInstruction(thinkingMode);
151
+ const messages = reasoningInstruction
152
+ ? [{ role: 'system', content: reasoningInstruction }, ...request.messages]
153
+ : request.messages;
154
+ // Map images for multi-modal models
155
+ const ollamaMessages = messages.map((m) => {
156
+ const out = { role: m.role, content: m.content };
157
+ if (m.images?.length) {
158
+ out.images = m.images.map((img) => img.data);
159
+ }
160
+ if (m.tool_calls)
161
+ out.tool_calls = m.tool_calls;
162
+ if (m.tool_call_id)
163
+ out.tool_call_id = m.tool_call_id;
164
+ return out;
165
+ });
166
+ const response = await fetch(`${this.baseUrl}/api/chat`, {
167
+ method: 'POST',
168
+ headers: { 'Content-Type': 'application/json' },
169
+ body: JSON.stringify({
170
+ model: usedModel,
171
+ messages: ollamaMessages,
172
+ tools: request.tools?.length ? request.tools : undefined,
173
+ stream: true,
174
+ options: {
175
+ temperature: request.temperature ?? 0.3,
176
+ num_predict: request.maxTokens ?? 4096,
177
+ },
178
+ }),
179
+ signal: AbortSignal.timeout(300_000),
180
+ });
181
+ if (!response.ok || !response.body) {
182
+ const text = await response.text().catch(() => '');
183
+ throw createHttpProviderError(this.id, response.status, `Ollama stream error: ${response.status} ${text.slice(0, 200)}`);
184
+ }
185
+ const reader = response.body.getReader();
186
+ const decoder = new TextDecoder();
187
+ let buffer = '';
188
+ while (true) {
189
+ const { done, value } = await reader.read();
190
+ if (done)
191
+ break;
192
+ buffer += decoder.decode(value, { stream: true });
193
+ const lines = buffer.split('\n');
194
+ buffer = lines.pop() ?? '';
195
+ for (const line of lines) {
196
+ if (!line.trim())
197
+ continue;
198
+ const chunk = JSON.parse(line);
199
+ if (chunk.message?.thinking) {
200
+ yield { type: 'thinking', text: chunk.message.thinking };
201
+ }
202
+ if (chunk.message?.content) {
203
+ yield { type: 'content', text: chunk.message.content };
204
+ }
205
+ if (chunk.message?.tool_calls?.length) {
206
+ yield {
207
+ type: 'tool_calls',
208
+ toolCalls: chunk.message.tool_calls.map((toolCall) => ({
209
+ name: toolCall.function.name,
210
+ arguments: toolCall.function.arguments ?? {},
211
+ })),
212
+ };
213
+ }
214
+ if (chunk.eval_count) {
215
+ yield { type: 'usage', evalCount: chunk.eval_count };
216
+ }
217
+ }
218
+ }
219
+ if (buffer.trim()) {
220
+ const chunk = JSON.parse(buffer);
221
+ if (chunk.message?.thinking) {
222
+ yield { type: 'thinking', text: chunk.message.thinking };
223
+ }
224
+ if (chunk.message?.content) {
225
+ yield { type: 'content', text: chunk.message.content };
226
+ }
227
+ if (chunk.message?.tool_calls?.length) {
228
+ yield {
229
+ type: 'tool_calls',
230
+ toolCalls: chunk.message.tool_calls.map((toolCall) => ({
231
+ name: toolCall.function.name,
232
+ arguments: toolCall.function.arguments ?? {},
233
+ })),
234
+ };
235
+ }
236
+ if (chunk.eval_count) {
237
+ yield { type: 'usage', evalCount: chunk.eval_count };
238
+ }
239
+ }
240
+ yield { type: 'done' };
241
+ }
242
+ async healthCheck() {
243
+ const checkedAt = new Date().toISOString();
244
+ if (!this.isConfigured()) {
245
+ return {
246
+ id: this.id,
247
+ name: this.name,
248
+ configured: false,
249
+ healthy: false,
250
+ baseUrl: this.baseUrl,
251
+ error: 'OLLAMA_URL is not configured',
252
+ checkedAt,
253
+ supportsStreaming: this.supportsStreaming,
254
+ supportsToolCalling: this.supportsToolCalling,
255
+ supportsStructuredOutputs: this.supportsStructuredOutputs,
256
+ };
257
+ }
258
+ try {
259
+ const response = await fetch(`${this.baseUrl}/api/tags`, { signal: AbortSignal.timeout(5_000) });
260
+ if (!response.ok) {
261
+ throw createHttpProviderError(this.id, response.status, `Ollama health check failed: ${response.status}`);
262
+ }
263
+ return {
264
+ id: this.id,
265
+ name: this.name,
266
+ configured: true,
267
+ healthy: true,
268
+ baseUrl: this.baseUrl,
269
+ checkedAt,
270
+ supportsStreaming: this.supportsStreaming,
271
+ supportsToolCalling: this.supportsToolCalling,
272
+ supportsStructuredOutputs: this.supportsStructuredOutputs,
273
+ };
274
+ }
275
+ catch (error) {
276
+ const normalized = normalizeProviderError(this.id, error);
277
+ return {
278
+ id: this.id,
279
+ name: this.name,
280
+ configured: true,
281
+ healthy: false,
282
+ baseUrl: this.baseUrl,
283
+ error: normalized.message,
284
+ checkedAt,
285
+ supportsStreaming: this.supportsStreaming,
286
+ supportsToolCalling: this.supportsToolCalling,
287
+ supportsStructuredOutputs: this.supportsStructuredOutputs,
288
+ };
289
+ }
290
+ }
291
+ }
292
+ //# sourceMappingURL=ollama.js.map