@agenthippo-ai/agenthippo 0.2.3-dataplane → 1.110.9

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 (130) hide show
  1. package/out/analytics/dashboard/provider.js +13 -2
  2. package/out/analytics/dashboard/webview/panel.js +3 -2
  3. package/out/analytics/identity.js +0 -0
  4. package/out/analytics/service.js +23 -25
  5. package/out/analytics/sessionEvents.js +98 -23
  6. package/out/auth/authBaseUrl.js +59 -0
  7. package/out/auth/bootstrapApply.js +9 -10
  8. package/out/auth/brokerClient.js +48 -14
  9. package/out/auth/provider.js +8 -21
  10. package/out/auth/sessionStore.js +3 -40
  11. package/out/auth/signInFlow.js +2 -1
  12. package/out/auth-broker/config.js +50 -5
  13. package/out/auth-broker/server.js +122 -26
  14. package/out/auth-broker/store.js +25 -24
  15. package/out/authority/warrant.js +206 -0
  16. package/out/branding.js +27 -1
  17. package/out/cli/commands/conformance.js +249 -0
  18. package/out/cli/commands/data-plane.js +67 -0
  19. package/out/cli/commands/deploy.js +1 -0
  20. package/out/cli/commands/gateway.js +4 -0
  21. package/out/cli/commands/pack.js +182 -3
  22. package/out/cli/commands/sandbox.js +332 -0
  23. package/out/cli/commands/serve.js +875 -52
  24. package/out/cli/commands/store.js +12 -4
  25. package/out/cli/data-plane/gateway.js +28 -5
  26. package/out/cli/data-plane/index.js +17 -2
  27. package/out/cli/data-plane/registry.js +9 -0
  28. package/out/cli/data-plane/remote.js +106 -0
  29. package/out/cli/data-plane/sidecar.js +161 -0
  30. package/out/cli/deploy/layers/identity.js +85 -4
  31. package/out/cli/deploy-init.js +89 -24
  32. package/out/cli/deps.js +106 -2
  33. package/out/cli/idp-init.js +35 -0
  34. package/out/cli/index.js +155 -16
  35. package/out/cli/pack-install-ref.js +15 -0
  36. package/out/cli/pack-loader.js +8 -1
  37. package/out/cli/pack-runtime.js +61 -0
  38. package/out/cli/runtime.js +35 -1
  39. package/out/cli/serve-hardening.js +80 -3
  40. package/out/cli/utils.js +12 -1
  41. package/out/engines/base.js +32 -3
  42. package/out/engines/claude.js +33 -15
  43. package/out/engines/codex.js +110 -26
  44. package/out/engines/custom.js +8 -0
  45. package/out/evidence/sessionShip.js +610 -0
  46. package/out/extension.js +11 -2
  47. package/out/gateway/bridge.js +40 -2
  48. package/out/gateway/client.js +34 -1
  49. package/out/gateway/installSource.js +68 -0
  50. package/out/gateway/lifecycle.js +185 -51
  51. package/out/gateway/outboundPaths.js +4 -1
  52. package/out/gateway/secretsPreflight.js +90 -0
  53. package/out/gateway/service.js +7 -2
  54. package/out/headless/index.js +17 -1
  55. package/out/live-e2e/mcp-demo/server.mjs +32 -0
  56. package/out/mcp.js +30 -0
  57. package/out/pack/caps.js +271 -0
  58. package/out/pack/governor.js +309 -0
  59. package/out/pack/lease-schema.js +270 -0
  60. package/out/pack/lease.js +640 -0
  61. package/out/pack/reconcile.js +217 -0
  62. package/out/pack/signature.js +839 -0
  63. package/out/remote/handleRemoteChat.js +44 -3
  64. package/out/remote/remoteTrust.js +277 -0
  65. package/out/remote/serveClient.js +52 -5
  66. package/out/runner/host.js +131 -0
  67. package/out/runner/protocol.js +108 -0
  68. package/out/runner/remoteEngine.js +130 -0
  69. package/out/runtime/interfaces/edit-tracker.js +2 -0
  70. package/out/runtime/vscode/vscode-edit-tracker.js +2 -0
  71. package/out/sandbox/containerDriver.js +570 -0
  72. package/out/sandbox/processDriver.js +315 -0
  73. package/out/sandbox/sandboxRunner.js +191 -0
  74. package/out/sandbox/support.js +301 -0
  75. package/out/sandbox/types.js +55 -0
  76. package/out/scripts/agentide-litellm.sh +67 -5
  77. package/out/scripts/agentide_litellm_hooks_v1.py +182 -21
  78. package/out/scripts/analytics/agentide-litellm-otel.config.yaml +94 -132
  79. package/out/scripts/analytics/setup-analytics.ps1 +37 -13
  80. package/out/scripts/analytics/setup-analytics.sh +93 -13
  81. package/out/scripts/analytics/start-analytics.sh +86 -14
  82. package/out/scripts/gateway/setup-gateway.sh +72 -32
  83. package/out/shared/models.js +11 -110
  84. package/out/spotlight/install.js +7 -1
  85. package/out/store/index.js +41 -10
  86. package/out/sync.js +3 -1
  87. package/out/test/analytics.test.js +162 -23
  88. package/out/test/auth-baseurl.test.js +132 -0
  89. package/out/test/auth-bootstrapApply.test.js +27 -6
  90. package/out/test/auth-broker-hardening.test.js +227 -0
  91. package/out/test/auth-broker.test.js +59 -9
  92. package/out/test/branding.test.js +13 -0
  93. package/out/test/caps-governor.test.js +455 -0
  94. package/out/test/codex-new-file-survival.test.js +119 -0
  95. package/out/test/configuration-scopes.test.js +131 -0
  96. package/out/test/data-plane.test.js +67 -0
  97. package/out/test/deploy-init.test.js +40 -0
  98. package/out/test/gateway-client.test.js +28 -0
  99. package/out/test/gateway-install-source.test.js +111 -0
  100. package/out/test/gateway-lifecycle.test.js +1 -1
  101. package/out/test/gateway-outbound.test.js +7 -0
  102. package/out/test/gateway-secrets-preflight.test.js +127 -0
  103. package/out/test/gateway-session-routing.test.js +18 -0
  104. package/out/test/governor-hardening.test.js +217 -0
  105. package/out/test/headless-sync.test.js +22 -0
  106. package/out/test/lease-signature.test.js +357 -0
  107. package/out/test/lease-trust-root.test.js +273 -0
  108. package/out/test/lease-validation.test.js +169 -0
  109. package/out/test/lease.test.js +114 -0
  110. package/out/test/mcp.test.js +27 -1
  111. package/out/test/pack-admission.test.js +216 -0
  112. package/out/test/pack-digest-parity.test.js +140 -0
  113. package/out/test/pack-reconcile.test.js +183 -0
  114. package/out/test/pack-signature-artifact.test.js +103 -0
  115. package/out/test/pack-signature.test.js +176 -0
  116. package/out/test/pack-trust-root.test.js +202 -0
  117. package/out/test/remote-trust.test.js +242 -0
  118. package/out/test/runner-protocol.test.js +249 -0
  119. package/out/test/sandbox-driver.test.js +553 -0
  120. package/out/test/sandbox-hardening.test.js +395 -0
  121. package/out/test/sandbox-runner.test.js +343 -0
  122. package/out/test/serve-hardening.test.js +45 -6
  123. package/out/test/serveClient.test.js +6 -5
  124. package/out/test/sessionShip.test.js +560 -0
  125. package/out/test/spotlightPublish.test.js +28 -0
  126. package/out/test/store-install-safe.test.js +69 -0
  127. package/out/test/tls-bypass.test.js +90 -0
  128. package/out/test/warrant.test.js +138 -0
  129. package/out/utils.js +46 -9
  130. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  #---------------------------------------------------------------------------------------------
2
2
  # Copyright (c) AgentHippo.ai. All rights reserved.
3
+ # Licensed under the MIT License. See LICENSE.txt in the project root for license information.
3
4
  #---------------------------------------------------------------------------------------------
4
5
  """
5
6
  AgentIDE LiteLLM proxy hooks - Clean structured assistant output with separate input/output
@@ -7,11 +8,27 @@ Mimics langfuse_otel style: gen_ai.input.messages (history) + gen_ai.output.mess
7
8
 
8
9
  Usage in litellm config.yaml:
9
10
  callbacks: ["agentide_litellm_hooks_v1.clean_separated_otel_output"]
11
+
12
+ PRIVACY DEFAULT (PLAN/compliance-req.md §6 "data minimization"):
13
+ Prompt and completion CONTENT is NOT captured unless an operator opts in with
14
+
15
+ AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE=1
16
+
17
+ Without the opt-in every span carries metadata only — model, tokens, latency, status — plus a
18
+ sha256 + byte length of the content so a later disclosure can still be *matched* against a
19
+ span without the span holding the text. This is enforced here at span-write time, in addition
20
+ to `turn_off_message_logging: true` in the config, so a config that re-enables message logging
21
+ without the env opt-in still produces redacted spans (defence in depth: the config is edited by
22
+ hand far more often than this file).
10
23
  """
11
24
 
12
25
  from __future__ import annotations
13
26
  from typing import Any
27
+ import hashlib
14
28
  import json
29
+ import os
30
+ import re
31
+ import sys
15
32
 
16
33
  from litellm.integrations.custom_logger import CustomLogger
17
34
  from litellm.integrations.opentelemetry import OpenTelemetry
@@ -20,6 +37,109 @@ from opentelemetry.trace import Status, StatusCode
20
37
  _OTEL_TEXT_MAX_LEN = 4000 # per text block
21
38
  _OTEL_PREVIEW_LEN = 180 # short preview in event
22
39
 
40
+ # -----------------------------------------------------------------------------
41
+ # Content capture opt-in (fix: full capture used to be the default)
42
+ # -----------------------------------------------------------------------------
43
+
44
+ CONTENT_CAPTURE_ENV = "AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE"
45
+
46
+ _TRUTHY = frozenset({"1", "true", "yes", "on"})
47
+
48
+ # Span attributes that can carry prompt/completion text. Mirrors CONTENT_ATTRS in
49
+ # @agenthippo-ai/evidence-ship so the two redaction points cannot drift silently.
50
+ _CONTENT_ATTR_RE = re.compile(
51
+ r"^(gen_ai\.(input|output)\.messages"
52
+ r"|gen_ai\.(prompt|completion)"
53
+ r"|gen_ai\.system_instructions"
54
+ r"|llm\.(prompts|responses)"
55
+ r"|llm\.request\.functions\."
56
+ r"|error\.(message|stack_trace)"
57
+ r"|hidden_params"
58
+ r"|metadata\.requester_custom_headers)"
59
+ )
60
+
61
+ _capture_banner_printed = False
62
+
63
+
64
+ def _content_capture_enabled() -> bool:
65
+ """True only when an operator explicitly opted in to prompt/completion capture."""
66
+ return (os.environ.get(CONTENT_CAPTURE_ENV, "") or "").strip().lower() in _TRUTHY
67
+
68
+
69
+ def _announce_content_capture() -> None:
70
+ """Log loudly, once, whichever mode is in force — silence is how this got shipped on."""
71
+ global _capture_banner_printed
72
+ if _capture_banner_printed:
73
+ return
74
+ _capture_banner_printed = True
75
+ if _content_capture_enabled():
76
+ print(
77
+ "\n"
78
+ "!! AGENTHIPPO EVIDENCE: FULL PROMPT/COMPLETION CAPTURE IS ON !!\n"
79
+ f"!! {CONTENT_CAPTURE_ENV} is set. Every span will carry prompt and completion text.\n"
80
+ "!! Unset it to return to metadata-only (sha256 + length) spans.\n",
81
+ file=sys.stderr,
82
+ flush=True,
83
+ )
84
+ else:
85
+ print(
86
+ f"[agentide-otel] content capture OFF (metadata + sha256 only). "
87
+ f"Set {CONTENT_CAPTURE_ENV}=1 to record prompts/completions.",
88
+ file=sys.stderr,
89
+ flush=True,
90
+ )
91
+
92
+
93
+ def _sha256(text: str) -> str:
94
+ return hashlib.sha256(text.encode("utf-8", "replace")).hexdigest()
95
+
96
+
97
+ class _RedactingSpan:
98
+ """
99
+ Span proxy that drops content-bearing attributes and replaces them with a sha256 + length.
100
+
101
+ LiteLLM's own `set_attributes()` writes `gen_ai.input.messages` (the full history) and
102
+ friends. We cannot remove an attribute after the SDK has recorded it, so the only place to
103
+ stop it is between LiteLLM and the SDK — here. Everything that is not content passes through
104
+ untouched, so the span keeps model, tokens, latency, status, and the requester metadata the
105
+ evidence index reads.
106
+ """
107
+
108
+ def __init__(self, span: Any) -> None:
109
+ self._span = span
110
+
111
+ def __getattr__(self, name: str) -> Any: # everything we do not override
112
+ return getattr(self._span, name)
113
+
114
+ def set_attribute(self, key: Any, value: Any) -> Any:
115
+ if isinstance(key, str) and _CONTENT_ATTR_RE.match(key):
116
+ text = value if isinstance(value, str) else json.dumps(value, ensure_ascii=False, default=str)
117
+ self._span.set_attribute(f"{key}.sha256", _sha256(text))
118
+ self._span.set_attribute(f"{key}.length", len(text))
119
+ self._span.set_attribute(f"{key}.redacted", True)
120
+ return None
121
+ return self._span.set_attribute(key, value)
122
+
123
+ def set_attributes(self, attributes: Any) -> Any:
124
+ if isinstance(attributes, dict):
125
+ for key, value in attributes.items():
126
+ self.set_attribute(key, value)
127
+ return None
128
+ return self._span.set_attributes(attributes)
129
+
130
+ def add_event(self, name: Any, attributes: Any = None, **kwargs: Any) -> Any:
131
+ if isinstance(attributes, dict):
132
+ filtered = {}
133
+ for key, value in attributes.items():
134
+ if isinstance(key, str) and _CONTENT_ATTR_RE.match(key):
135
+ text = value if isinstance(value, str) else json.dumps(value, ensure_ascii=False, default=str)
136
+ filtered[f"{key}.sha256"] = _sha256(text)
137
+ filtered[f"{key}.length"] = len(text)
138
+ else:
139
+ filtered[key] = value
140
+ attributes = filtered
141
+ return self._span.add_event(name, attributes, **kwargs)
142
+
23
143
 
24
144
  def _read_field(obj: Any, key: str, default: Any = None) -> Any:
25
145
  if isinstance(obj, dict):
@@ -103,9 +223,16 @@ def _is_anthropic_route(model: Any) -> bool:
103
223
  return "claude" in name or "anthropic" in name
104
224
 
105
225
 
226
+ # Every model known to reject the Codex built-in web-search tools. Additive by policy: a
227
+ # deployment that still routes to an older model keeps its sanitizer when a newer one is added,
228
+ # because a dropped entry fails as upstream 400s on live traffic, not as a test failure.
229
+ _WEB_SEARCH_UNSUPPORTED_MODELS = ("gpt-5.6-luna", "gpt-4.1-nano")
230
+
231
+
106
232
  def _is_web_search_unsupported_model(model: Any) -> bool:
107
233
  """True when the upstream model rejects Codex web-search tools."""
108
- return "gpt-4.1-nano" in _model_name(model)
234
+ name = _model_name(model)
235
+ return any(m in name for m in _WEB_SEARCH_UNSUPPORTED_MODELS)
109
236
 
110
237
 
111
238
  # OpenAI Responses API fields that Anthropic (and some gateways) reject if forwarded verbatim.
@@ -360,7 +487,7 @@ def sanitize_messages(messages: Any) -> Any:
360
487
 
361
488
 
362
489
  class ReasoningParamsSanitizer(CustomLogger):
363
- """LiteLLM proxy hook: strip reasoning controls for gpt-4.1-nano."""
490
+ """LiteLLM proxy hook: strip reasoning controls for gpt-5.6-luna."""
364
491
 
365
492
  async def async_pre_call_hook(
366
493
  self,
@@ -410,7 +537,7 @@ class CodexProxySanitizer(CustomLogger):
410
537
  LiteLLM proxy hook: strip Codex/OpenAI Responses fields/tools that break proxy routes.
411
538
 
412
539
  - Anthropic: drop client_metadata (OpenAI-only Responses telemetry).
413
- - gpt-4.1-nano: drop web_search_preview and related built-in search tools.
540
+ - gpt-5.6-luna: drop web_search_preview and related built-in search tools.
414
541
  """
415
542
 
416
543
  def _sanitize(self, payload: dict, model: Any) -> dict:
@@ -548,8 +675,36 @@ class CleanSeparatedOtelOutput(OpenTelemetry):
548
675
  - gen_ai.input.messages → full history (as set by LiteLLM)
549
676
  - gen_ai.output.messages → only the new assistant reply (stringified structured array)
550
677
  - Short preview event for quick visibility
678
+
679
+ Content (both directions) is recorded ONLY when AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE is set.
680
+ Otherwise every content attribute becomes `<key>.sha256` + `<key>.length` — enough to prove
681
+ which bytes a turn saw, without shipping the bytes.
551
682
  """
552
683
 
684
+ def set_attributes(self, span: Any, *args: Any, **kwargs: Any) -> Any:
685
+ """
686
+ Redaction chokepoint. Overriding here rather than at the call site covers every path in
687
+ the base class that sets attributes, including ones this subclass does not override.
688
+ """
689
+ _announce_content_capture()
690
+ if not _content_capture_enabled():
691
+ span = _RedactingSpan(span)
692
+ return super().set_attributes(span, *args, **kwargs)
693
+
694
+ def _get_span_name(self, kwargs: dict) -> str:
695
+ # Evidence pipeline indexes spans named litellm_request; main-stable semconv uses "chat …".
696
+ #
697
+ # The constant is a PRIVATE litellm symbol and this runs on the span-creation path, so an
698
+ # upstream rename would raise for every span — total evidence loss at layer 1, not a
699
+ # degraded span name. The literal is the same value; falling back to it keeps the pipeline
700
+ # indexing while the import is fixed.
701
+ try:
702
+ from litellm.integrations.opentelemetry import LITELLM_REQUEST_SPAN_NAME
703
+
704
+ return LITELLM_REQUEST_SPAN_NAME
705
+ except Exception: # pragma: no cover - depends on the pinned litellm build
706
+ return "litellm_request"
707
+
553
708
  def _start_primary_span(
554
709
  self,
555
710
  kwargs: dict,
@@ -573,24 +728,30 @@ class CleanSeparatedOtelOutput(OpenTelemetry):
573
728
  _extract_assistant_text(response_obj)
574
729
 
575
730
  if text.strip():
576
- # Structured output (separate from input history)
577
- structured_msgs = _make_output_messages_structured(text)
578
- span.set_attribute(
579
- "gen_ai.output.messages",
580
- json.dumps(structured_msgs, ensure_ascii=False)
581
- )
582
-
583
- # Short preview event - easy to spot, low noise
584
- preview = text[:_OTEL_PREVIEW_LEN] + ("..." if len(text) > _OTEL_PREVIEW_LEN else "")
585
- span.add_event(
586
- name="gen_ai.output.generated",
587
- attributes={
588
- "gen_ai.output.preview": preview,
589
- "gen_ai.output.length": len(text),
590
- "gen_ai.output.truncated": len(text) > _OTEL_TEXT_MAX_LEN,
591
- "gen_ai.response.model": _read_field(kwargs, "model", "unknown")
592
- }
593
- )
731
+ capture = _content_capture_enabled()
732
+ event_attrs: dict[str, Any] = {
733
+ # Always present: proves a reply of exactly these bytes was produced, and lets a
734
+ # disclosed transcript be matched back to the span without storing the text.
735
+ "gen_ai.output.sha256": _sha256(text),
736
+ "gen_ai.output.length": len(text),
737
+ "gen_ai.output.truncated": len(text) > _OTEL_TEXT_MAX_LEN,
738
+ "gen_ai.output.captured": capture,
739
+ "gen_ai.response.model": _read_field(kwargs, "model", "unknown"),
740
+ }
741
+
742
+ if capture:
743
+ # Structured output (separate from input history) — opt-in only.
744
+ structured_msgs = _make_output_messages_structured(text)
745
+ span.set_attribute(
746
+ "gen_ai.output.messages",
747
+ json.dumps(structured_msgs, ensure_ascii=False)
748
+ )
749
+ # Short preview event - easy to spot, low noise
750
+ event_attrs["gen_ai.output.preview"] = (
751
+ text[:_OTEL_PREVIEW_LEN] + ("..." if len(text) > _OTEL_PREVIEW_LEN else "")
752
+ )
753
+
754
+ span.add_event(name="gen_ai.output.generated", attributes=event_attrs)
594
755
 
595
756
  span.end(end_time=self._to_ns(end_time))
596
757
  return span
@@ -1,4 +1,7 @@
1
-
1
+ #---------------------------------------------------------------------------------------------
2
+ # Copyright (c) AgentHippo.ai. All rights reserved.
3
+ # Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4
+ #---------------------------------------------------------------------------------------------
2
5
 
3
6
  # AgentIDE LiteLLM configuration with OpenTelemetry
4
7
  # Use this instead of agentide-litellm.config.yaml for lightweight analytics
@@ -19,6 +22,20 @@
19
22
  # OTEL_SERVICE_NAME=agentide-litellm # Service name in traces
20
23
  # DEBUG_OTEL=true # Enable OTEL debug logging
21
24
 
25
+ #
26
+ # PRIVACY DEFAULT — metadata only. See "Content capture" below.
27
+ # AUTHENTICATION — general_settings.master_key is REQUIRED; the proxy fails to start without it.
28
+ # ROUTES — consumer/subscription-credential routes are NOT in this file. See
29
+ # agentide-litellm-consumer-routes.local-dev-only.config.yaml.
30
+ #
31
+
32
+ general_settings:
33
+ # Without a master key LiteLLM accepts unauthenticated POST /v1/chat/completions from anything
34
+ # that can reach the port — on a serve host that is every colocated container, and the proxy
35
+ # holds provider credentials. No default is supplied on purpose: an unset LITELLM_MASTER_KEY
36
+ # must fail closed at startup rather than quietly open the proxy.
37
+ master_key: os.environ/LITELLM_MASTER_KEY
38
+
22
39
  litellm_settings:
23
40
  telemetry: false # Disables sending data to LiteLLM's remote servers
24
41
  drop_params: true
@@ -36,139 +53,84 @@ litellm_settings:
36
53
  # callbacks: ["agentide_litellm_hooks_v1.structured_genai_otel"] # , "agentide_litellm_hooks.tool_order_sanitizer"]
37
54
  # callbacks: ["agentide_litellm_hooks_v1.structured_genai_otel", "agentide_litellm_hooks_v1.reasoning_params_sanitizer"]
38
55
  callbacks: ["agentide_litellm_hooks_v1.structured_genai_otel", "agentide_litellm_hooks_v1.codex_proxy_sanitizer", "agentide_litellm_hooks_v1.tool_order_sanitizer", "agentide_litellm_hooks_v1.empty_thinking_sanitizer", "agentide_litellm_hooks_v1.reasoning_params_sanitizer"]
39
- turn_off_message_logging: false
56
+
57
+ # ---------------------------------------------------------------------------
58
+ # Content capture — OPT-IN (PLAN/compliance-req.md §6, data minimization)
59
+ # ---------------------------------------------------------------------------
60
+ # Default: prompts and completions are NOT written to spans. Every span still carries model,
61
+ # token counts, latency, status, requester metadata, and a sha256 + byte length of the content,
62
+ # so a turn stays attributable and a disclosed transcript can be matched back to it.
63
+ #
64
+ # This used to be `turn_off_message_logging: false` + `message_logging: true`, i.e. full prompt
65
+ # and completion capture on by default, unredacted for the serve lane.
66
+ #
67
+ # TO RECORD PROMPTS/COMPLETIONS (both steps are required — either alone keeps you redacted):
68
+ # 1. set turn_off_message_logging: false and callback_settings.otel.message_logging: true
69
+ # 2. export AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE=1
70
+ # Step 2 is enforced in agentide_litellm_hooks_v1.py at span-write time and logs loudly when on.
71
+ turn_off_message_logging: true
40
72
 
41
73
  callback_settings:
42
74
  otel:
43
- message_logging: true # critical for seeing tool_calls, arguments, responses
44
-
45
- model_list:
46
-
47
- - model_name: litellm/gpt-4.1-nano
48
- litellm_params:
49
- model: openai/gpt-4.1-nano
50
- api_base: https://api.openai.com/v1
51
- drop_params: true
52
- additional_drop_params: ["user", "max_output_tokens", "reasoning.effort", "reasoning_effort"] # Must be set at specific model -- global does not work: Force LiteLLM to remove this specific field before sending the request
53
-
54
- # Add this under (existing) model_list:
55
- # model_list:
56
- # - model_name: ...
57
- - model_name: litellm/claude-opus-4-7
58
- litellm_params:
59
- model: anthropic/claude-opus-4-7
60
- reasoning_effort: "medium"
61
- additional_drop_params: ["user", "max_output_tokens"]
62
- store: false
63
- api_key: os.environ/ANTHROPIC_SETUP_TOKEN
64
- model_info:
65
- upstream_provider: "anthropic-setup-token"
66
-
67
- # Add this under (existing) model_list:
68
- # model_list:
69
- # - model_name: ...
70
- - model_name: litellm/claude-haiku-4-5-20251001
71
- litellm_params:
72
- model: anthropic/claude-haiku-4-5-20251001
73
- top_p: null
74
- drop_params: true
75
- api_base: https://api.anthropic.com
76
-
77
- - model_name: litellm/claude-sonnet-4-6
78
- litellm_params:
79
- model: anthropic/claude-sonnet-4-6
80
- api_base: https://api.anthropic.com
81
-
82
-
83
- # Add this under (existing) model_list:
84
- # model_list:
85
- # - model_name: ...
86
- - model_name: litellm/gpt-5.3-codex
87
- litellm_params:
88
- model: openai/gpt-5.3-codex
89
- store: false
90
- api_key: os.environ/OPENAI_CODEX_API_KEY
91
- api_base: https://chatgpt.com/backend-api/codex
92
- reasoning_effort: "medium"
93
- additional_drop_params: ["user", "max_output_tokens"] # Must be set at specific model -- global does not work: Force LiteLLM to remove this specific field before sending the request
94
-
95
- - model_name: litellm/gpt-5.5
96
- litellm_params:
97
- model: openai/gpt-5.5
98
- store: false
99
- api_key: os.environ/OPENAI_API_KEY
100
- api_base: https://api.openai.com/v1
101
- reasoning_effort: "medium"
102
- additional_drop_params: ["user", "max_output_tokens"] # Must be set at specific model -- global does not work: Force LiteLLM to remove this specific field before sending the request
103
-
104
- # Add this under (existing) model_list:
105
- # model_list:
106
- # - model_name: ...
107
- - model_name: litellm/gpt-5.1-codex-mini
108
- litellm_params:
109
- model: openai/gpt-5.1-codex-mini
110
- reasoning_effort: "medium"
111
- additional_drop_params: ["user", "max_output_tokens", "temperature"]
112
- store: false
113
- api_key: os.environ/OPENAI_CODEX_API_KEY
114
- api_base: https://chatgpt.com/backend-api/codex
115
-
116
- # Add this under (existing) model_list:
117
- # model_list:
118
- # - model_name: ...
119
- - model_name: litellm/claude-opus-4-6-subs
120
- litellm_params:
121
- model: anthropic/claude-opus-4-6
122
- reasoning_effort: "medium"
123
- max_tokens: 64000
124
- additional_drop_params: ["user", "max_output_tokens"]
125
- store: false
126
- api_key: os.environ/ANTHROPIC_SETUP_TOKEN
127
-
128
-
129
- - model_name: litellm/claude-haiku-4-5-subs
130
- litellm_params:
131
- model: anthropic/claude-haiku-4-5
132
- reasoning_effort: "medium"
133
- additional_drop_params: ["user", "max_output_tokens"]
134
- store: false
135
- api_key: os.environ/ANTHROPIC_SETUP_TOKEN
136
-
137
- # Add this under (existing) model_list:
138
- # model_list:
139
- # - model_name: ...
140
- - model_name: litellm/claude-opus-4-7-test
141
- litellm_params:
142
- model: anthropic/claude-opus-4-7
143
- reasoning_effort: "medium"
144
- additional_drop_params: ["user", "max_output_tokens"]
145
- store: false
146
- model_info:
147
- upstream_provider: "anthropic"
75
+ message_logging: false # true + AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE=1 records tool_calls, arguments, responses
76
+
77
+ # ---------------------------------------------------------------------------
78
+ # Model routes
79
+ # ---------------------------------------------------------------------------
80
+ # A fresh install ships with no active routes. Uncomment `model_list:` and the
81
+ # provider blocks you use, then put the matching key in ~/.agent-hippo/.env
82
+ # (e.g. echo 'OPENAI_API_KEY=sk-...' >> ~/.agent-hippo/.env). The launcher
83
+ # warns at startup about any referenced env var that is not set.
84
+ #
85
+ # NOTE: `additional_drop_params` must be set per model — the global setting
86
+ # does not work.
87
+ # More providers: https://docs.litellm.ai/docs/providers
88
+ #
89
+ # Consumer/subscription-credential routes (litellm/gpt-5.3-codex,
90
+ # litellm/claude-opus-4-7, …) are deliberately NOT in this file — see
91
+ # agentide-litellm-consumer-routes.local-dev-only.config.yaml and
92
+ # PLAN/compliance.md §12. `litellm/gpt-5.3-codex` is the default model in
93
+ # several docs/manifests; it has NOT been re-pointed at a platform-API route
94
+ # here, because which platform model id is equivalent is a provider question,
95
+ # not a config one — pick one deliberately rather than inheriting a guess.
148
96
 
149
- # Add this under (existing) model_list:
150
97
  # model_list:
151
- # - model_name: ...
152
- - model_name: litellm/gpt-5.4-nano
153
- litellm_params:
154
- model: openai/gpt-5.4-nano
155
- reasoning_effort: "medium"
156
- additional_drop_params: ["user", "max_output_tokens"]
157
- store: false
158
- api_base: https://api.openai.com/v1
159
- model_info:
160
- upstream_provider: "openai"
161
-
162
- # Fleet / Cloud Run: ah-auto resolves via the central AgentHippo model gateway (same as
163
- # colocated agents with AGENTHIPPO_LITELLM_BASE_URL=https://gateway.agenthippo.ai/v1).
164
- # Set AGENTHIPPO_GATEWAY_KEY to an ahgw_… service key (create-gateway-key on the gateway host).
165
- - model_name: ah-auto
166
- litellm_params:
167
- # openai/ prefix required — bare `ah-auto` fails LiteLLM router deployment (no provider).
168
- model: openai/ah-auto
169
- api_base: https://gateway.agenthippo.ai/v1
170
- api_key: os.environ/AGENTHIPPO_GATEWAY_KEY
171
- drop_params: true
172
- additional_drop_params: ["user", "max_output_tokens", "reasoning.effort", "reasoning_effort"]
173
- model_info:
174
- upstream_provider: "agenthippo-gateway"
98
+ # # --- OpenAI (platform API) ---
99
+ # - model_name: litellm/gpt-5.5
100
+ # litellm_params:
101
+ # model: openai/gpt-5.5
102
+ # api_base: https://api.openai.com/v1
103
+ # api_key: os.environ/OPENAI_API_KEY
104
+ # drop_params: true
105
+ # additional_drop_params: ["user", "max_output_tokens"]
106
+ #
107
+ # # --- Anthropic (platform API) ---
108
+ # - model_name: litellm/claude-sonnet-4-6
109
+ # litellm_params:
110
+ # model: anthropic/claude-sonnet-4-6
111
+ # api_base: https://api.anthropic.com
112
+ # api_key: os.environ/ANTHROPIC_API_KEY
113
+ # drop_params: true
114
+ #
115
+ # # --- AgentHippo model gateway (fleet / Cloud Run) ---
116
+ # # Same endpoint as colocated agents with
117
+ # # AGENTHIPPO_LITELLM_BASE_URL=https://gateway.agenthippo.ai/v1.
118
+ # # AGENTHIPPO_GATEWAY_KEY is an ahgw_… service key (create-gateway-key on
119
+ # # the gateway host).
120
+ # - model_name: ah-auto
121
+ # litellm_params:
122
+ # # openai/ prefix required — bare `ah-auto` fails LiteLLM router
123
+ # # deployment (no provider).
124
+ # model: openai/ah-auto
125
+ # api_base: https://gateway.agenthippo.ai/v1
126
+ # api_key: os.environ/AGENTHIPPO_GATEWAY_KEY
127
+ # drop_params: true
128
+ # additional_drop_params: ["user", "max_output_tokens", "reasoning.effort", "reasoning_effort"]
129
+ #
130
+ # # --- Distributed LiteLLM OTEL: central model gateway ---
131
+ # - model_name: ah-auto
132
+ # litellm_params:
133
+ # model: openai/ah-auto
134
+ # api_base: https://gatewayv1.agenthippo.ai/v1
135
+ # api_key: os.environ/AGENTHIPPO_GATEWAY_KEY
136
+ # drop_params: true
@@ -282,6 +282,37 @@ function Resolve-LiteLLMHooks {
282
282
  return $null
283
283
  }
284
284
 
285
+ # Ensure the proxy has an auth key without ever asking the user for one.
286
+ # Mirrors ensure_litellm_master_key in setup-analytics.sh — the launcher refuses to start
287
+ # unauthenticated, so a first run with no key would leave LiteLLM dead with the error in a log.
288
+ function Ensure-LiteLLMMasterKey {
289
+ $envFile = Join-Path $AgentHomeDir ".env"
290
+
291
+ if ((Test-Path $envFile) -and (Select-String -Path $envFile -Pattern '^\s*LITELLM_MASTER_KEY=' -Quiet)) {
292
+ Log-Success "LiteLLM master key already configured"
293
+ return
294
+ }
295
+ if ($env:LITELLM_MASTER_KEY) {
296
+ Log-Success "Using LITELLM_MASTER_KEY from the environment"
297
+ return
298
+ }
299
+
300
+ $bytes = New-Object byte[] 24
301
+ [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
302
+ $key = "sk-" + (($bytes | ForEach-Object { $_.ToString("x2") }) -join "")
303
+
304
+ New-Item -ItemType Directory -Path $AgentHomeDir -Force | Out-Null
305
+ $lines = @(
306
+ "# Generated by setup-analytics - authenticates the local LiteLLM proxy.",
307
+ "# Clients read the same value as AGENTHIPPO_LITELLM_API_KEY.",
308
+ "LITELLM_MASTER_KEY=$key",
309
+ "AGENTHIPPO_LITELLM_API_KEY=$key"
310
+ )
311
+ Add-Content -Path $envFile -Value $lines
312
+ $env:LITELLM_MASTER_KEY = $key
313
+ Log-Success "Generated a LiteLLM master key (stored in $envFile)"
314
+ }
315
+
285
316
  function Setup-Config {
286
317
  Log-Step "Setting up configuration..."
287
318
 
@@ -289,6 +320,9 @@ function Setup-Config {
289
320
  New-Item -ItemType Directory -Path (Join-Path $AnalyticsDir "config") -Force | Out-Null
290
321
  New-Item -ItemType Directory -Path (Join-Path $AnalyticsDir "traces") -Force | Out-Null
291
322
 
323
+ # Before any early return below - the launcher exits non-zero without a key.
324
+ Ensure-LiteLLMMasterKey
325
+
292
326
  # Copy OTEL config
293
327
  $configDest = Join-Path $AnalyticsDir "config\otel-collector.yaml"
294
328
  $configSrc = Join-Path $ScriptDir "otel-collector.yaml"
@@ -330,19 +364,9 @@ function Setup-Config {
330
364
  Log-Success "LiteLLM OTEL config already present (user-owned)"
331
365
  }
332
366
 
333
- # Keep canonical template in sync with user edits when writable.
334
- if ((Test-Path $litellmSrc) -and (Test-Path $litellmDest)) {
335
- $sourceHash = (Get-Sha256Hash $litellmSrc)
336
- $destHash = (Get-Sha256Hash $litellmDest)
337
- if ($sourceHash -ne $destHash) {
338
- try {
339
- Copy-Item $litellmDest $litellmSrc -Force
340
- Log-Success "Synced LiteLLM OTEL config back to source template"
341
- } catch {
342
- Log-Info "Using user-owned LiteLLM config (source template is read-only)"
343
- }
344
- }
345
- }
367
+ # Deliberately ONE-WAY (template -> user). Copying the user's config back over the shipped
368
+ # template wrote their edits into the installed extension / a git-tracked file, and model
369
+ # entries can carry a literal upstream api_key - a credential leak into source control.
346
370
 
347
371
  $hooksDest = Join-Path $AgentHomeDir "agentide_litellm_hooks_v1.py"
348
372
  $hooksSrc = Resolve-LiteLLMHooks